]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/client/Main.qc
zoom: do it entirely client side
[divverent/nexuiz.git] / data / qcsrc / client / Main.qc
1 // --------------------------------------------------------------------------\r
2 // BEGIN REQUIRED CSQC FUNCTIONS\r
3 //include "main.qh"\r
4 \r
5 #define DP_CSQC_ENTITY_REMOVE_IS_B0RKED\r
6 \r
7 void() menu_show_error =\r
8 {\r
9         drawstring('0 200 0', "ERROR - MENU IS VISIBLE BUT NO MENU WAS DEFINED!", '8 8 0', '1 0 0', 1, 0);\r
10 };\r
11 \r
12 // CSQC_Init : Called every time the CSQC code is initialized (essentially at map load)\r
13 // Useful for precaching things\r
14 \r
15 void() menu_sub_null =\r
16 {\r
17 };\r
18 \r
19 // let's make this a general data buffer...\r
20 float using_gps;\r
21 \r
22 #ifdef USE_FTE\r
23 float __engine_check;\r
24 #endif\r
25 \r
26 string config_get(string key, string defaultvalue)\r
27 {\r
28         string s;\r
29         s = db_get(configdb, strcat("/s/", key));\r
30         if(s == "")\r
31                 return defaultvalue;\r
32         else\r
33                 return db_get(configdb, strcat("/v/", key));\r
34 }\r
35 \r
36 void CSQC_Init(void)\r
37 {\r
38 #ifdef USE_FTE\r
39 #pragma target ID\r
40         __engine_check = checkextension("DP_SV_WRITEPICTURE");\r
41         if(!__engine_check)\r
42         {\r
43                 print("^3Your engine build is outdated\n^3This Server uses a newer QC VM. Please update!\n");\r
44                 localcmd("\ndisconnect\n");\r
45                 return;\r
46         }\r
47 #pragma target FTE\r
48 #endif\r
49         \r
50         float i;\r
51         CSQC_CheckEngine();\r
52 \r
53         configdb = db_create();\r
54         compressShortVector_init();\r
55 \r
56         drawfont = 0;\r
57         menu_visible = FALSE;\r
58         menu_show = menu_show_error;\r
59         menu_action = menu_sub_null;\r
60         using_gps = false;\r
61         maxclients = 255; // we later get the real maxclients to speed up stuff\r
62         //ctf_temp_1 = "";\r
63         // localcmd("alias order \"cmd order $*\""); enable if ctf-command thingy is used\r
64         //registercmd("ctf_menu");\r
65         registercmd("ons_map");\r
66         //registercmd("menu_action");\r
67         registercmd("sbar_columns_set");\r
68         registercmd("sbar_columns_help");\r
69 \r
70         registercmd("+button3");\r
71         registercmd("-button3");\r
72         registercmd("+button4");\r
73         registercmd("-button4");\r
74 \r
75         registercvar("sbar_usecsqc", "1");\r
76         registercvar("sbar_columns", "default", CVAR_SAVE);\r
77 \r
78         gametype = 0;\r
79 \r
80         gps_start = world;\r
81 \r
82         // sbar_fields uses strunzone on the titles!\r
83         for(i = 0; i < MAX_SBAR_FIELDS; ++i)\r
84                 sbar_title[i] = strzone("(null)");\r
85 \r
86         postinit = false;\r
87 \r
88         teams = Sort_Spawn();\r
89         players = Sort_Spawn();\r
90         \r
91         GetTeam(COLOR_SPECTATOR, true); // add specs first\r
92 }\r
93 \r
94 // CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc)\r
95 void CSQC_Shutdown(void)\r
96 {\r
97 #ifdef USE_FTE\r
98 #pragma TARGET id\r
99         if(!__engine_check)\r
100                 return 0;\r
101 #pragma TARGET fte\r
102 #endif\r
103 \r
104         remove(teams);\r
105         remove(players);\r
106         db_close(configdb);\r
107 \r
108         entity e;\r
109         for(e = world; (e = find(e, classname, "saved_cvar_value")); )\r
110                         cvar_set(e.netname, e.message);\r
111 }\r
112 \r
113 .float has_team;\r
114 float SetTeam(entity o, float Team)\r
115 {\r
116         entity tm;\r
117         if(Team == -1) // leave\r
118         {\r
119                 if(o.has_team)\r
120                 {\r
121                         //print("(DISCONNECT) leave team ", ftos(o.team), "\n");\r
122                         tm = GetTeam(o.team, false);\r
123                         tm.team_size -= 1;\r
124                         o.has_team = 0;\r
125                         return TRUE;\r
126                 }\r
127         }\r
128         else\r
129         {\r
130                 if not(o.has_team)\r
131                 {\r
132                         //print("(CONNECT) enter team ", ftos(o.team), "\n");\r
133                         o.team = Team;\r
134                         tm = GetTeam(Team, true);\r
135                         tm.team_size += 1;\r
136                         o.has_team = 1;\r
137                         return TRUE;\r
138                 }\r
139                 else if(Team != o.team)\r
140                 {\r
141                         //print("(CHANGE) leave team ", ftos(o.team), "\n");\r
142                         tm = GetTeam(o.team, false);\r
143                         tm.team_size -= 1;\r
144                         o.team = Team;\r
145                         //print("(CHANGE) enter team ", ftos(o.team), "\n");\r
146                         tm = GetTeam(Team, true);\r
147                         tm.team_size += 1;\r
148                         return TRUE;\r
149                 }\r
150         }\r
151         return FALSE;\r
152 }\r
153 \r
154 void Playerchecker_Think()\r
155 {\r
156         float i;\r
157         for(i = 0; i < maxclients; ++i)\r
158         {\r
159                 if(getplayerkey(i, "name") == "")\r
160                 {\r
161                         if(playerslots[i].sort_prev)\r
162                         {\r
163                                 //print("playerchecker: KILL KILL KILL\n");\r
164                                 // player disconnected\r
165                                 SetTeam(playerslots[i], -1);\r
166                                 RemovePlayer(playerslots[i]);\r
167                                 playerslots[i].sort_prev = world;\r
168                                 playerslots[i].gotscores = 0;\r
169                         }\r
170                 }\r
171                 else\r
172                 {\r
173                         if not(playerslots[i].sort_prev)\r
174                         {\r
175                                 //print("playerchecker: SPAWN SPAWN SPAWN\n");\r
176                                 // player connected\r
177                                 if not(playerslots[i])\r
178                                         playerslots[i] = spawn();\r
179                                 playerslots[i].sv_entnum = i;\r
180                                 playerslots[i].gotscores = 0;\r
181                                 SetTeam(playerslots[i], COLOR_SPECTATOR);\r
182                                 RegisterPlayer(playerslots[i]);\r
183                                 Sbar_UpdatePlayerPos(playerslots[i]);\r
184                         }\r
185                 }\r
186         }\r
187         self.nextthink = time + 0.2;\r
188 }\r
189 \r
190 void PostInit(void)\r
191 {\r
192         print(strcat("PostInit\n    maxclients = ", ftos(maxclients), "\n"));\r
193         localcmd(strcat("\nsbar_columns_set ", cvar_string("sbar_columns"), ";\n"));\r
194 \r
195         entity playerchecker;\r
196         playerchecker = spawn();\r
197         playerchecker.think = Playerchecker_Think;\r
198         playerchecker.nextthink = time + 0.2;\r
199 \r
200         postinit = true;\r
201 }\r
202 \r
203 // CSQC_ConsoleCommand : Used to parse commands in the console that have been registered with the "registercmd" function\r
204 // Return value should be 1 if CSQC handled the command, otherwise return 0 to have the engine handle it.\r
205 float button_zoom;\r
206 void Cmd_Sbar_SetFields(float);\r
207 void Cmd_Sbar_Help(float);\r
208 float CSQC_ConsoleCommand(string strMessage)\r
209 {\r
210         float argc;\r
211         // Tokenize String\r
212         //argc = tokenize(strMessage);\r
213         argc = tokenizebyseparator(strMessage, " ");\r
214         \r
215         // Acquire Command\r
216         local string strCmd;\r
217         strCmd = argv(0);\r
218 \r
219         if(strCmd == "+button4") { // zoom\r
220                 button_zoom = 1;\r
221                 return true;\r
222         } else if(strCmd == "-button4") { // zoom\r
223                 button_zoom = 0;\r
224                 return true;\r
225         } else if(strCmd == "+button3") { // secondary\r
226                 button_attack2 = 1;\r
227                 return false;\r
228         } else if(strCmd == "-button3") { // secondary\r
229                 button_attack2 = 0;\r
230                 return false;\r
231         } else if(strCmd == "ons_map") {\r
232                 Cmd_ons_map();\r
233                 return true;\r
234         } else if(strCmd == "sbar_columns_set") {\r
235                 Cmd_Sbar_SetFields(argc);\r
236                 return true;\r
237         } else if(strCmd == "sbar_columns_help") {\r
238                 Cmd_Sbar_Help(argc);\r
239                 return true;\r
240         } else if(strCmd == "+showscores") {\r
241                 sb_showscores = true;\r
242                 return true;\r
243         } else if(strCmd == "-showscores") {\r
244                 sb_showscores = false;\r
245                 return true;\r
246         }\r
247         \r
248         return false;\r
249 }\r
250 \r
251 float GameCommand(string msg)\r
252 {\r
253         float argc;\r
254         argc = tokenize(msg);\r
255         string cmd;\r
256         cmd = argv(0);\r
257         if(cmd == "mv_download") {\r
258                 Cmd_MapVote_MapDownload(argc);\r
259                 return true;\r
260         }\r
261         else if(cmd == "settemp") {\r
262                 entity e;\r
263                 for(e = world; (e = find(e, classname, "saved_cvar_value")); )\r
264                         if(e.netname == argv(1))\r
265                                 goto saved;\r
266                 e = spawn();\r
267                 e.classname = "saved_cvar_value";\r
268                 e.netname = strzone(argv(1));\r
269                 e.message = strzone(cvar_string(argv(1)));\r
270 :saved\r
271                 cvar_set(argv(1), argv(2));\r
272         }\r
273         \r
274         return false;\r
275 }\r
276 \r
277 // CSQC_InputEvent : Used to perform actions based on any key pressed, key released and mouse on the client.\r
278 // Return value should be 1 if CSQC handled the input, otherwise return 0 to have the input passed to the engine.\r
279 // All keys are in ascii.\r
280 // bInputType = 0 is key pressed, 1 is key released, 2 is mouse input.\r
281 // In the case of keyboard input, nPrimary is the ascii code, and nSecondary is 0.\r
282 // In the case of mouse input, nPrimary is xdelta, nSecondary is ydelta.\r
283 float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary)\r
284 {\r
285         local float bSkipKey;\r
286         bSkipKey = false;\r
287         \r
288         if(menu_visible)\r
289                 if(menu_action(bInputType, nPrimary, nSecondary))\r
290                         return TRUE;\r
291         return bSkipKey;\r
292 }\r
293 \r
294 // END REQUIRED CSQC FUNCTIONS\r
295 // --------------------------------------------------------------------------\r
296 \r
297 // --------------------------------------------------------------------------\r
298 // BEGIN OPTIONAL CSQC FUNCTIONS\r
299 void Ent_ReadONS()\r
300 {\r
301         entity gps;\r
302         using_gps = true;\r
303 \r
304         self.origin_x = ReadCoord();\r
305         self.origin_y = ReadCoord();\r
306         self.angles_y = ReadCoord();\r
307         self.origin_z = self.angles_x = self.angles_z = 0;\r
308 \r
309         for(gps = gps_start; gps; gps = gps.chain)\r
310         {\r
311                 if(gps == self)\r
312                         break;\r
313         }\r
314         if(!gps)\r
315         {\r
316                 self.chain = gps_start;\r
317                 gps_start = self;\r
318         }\r
319 }\r
320 \r
321 void Ent_RemoveONS()\r
322 {\r
323         if(gps_start == self)\r
324                 gps_start = self.chain;\r
325         else\r
326         {\r
327                 local entity ent;\r
328                 ent = gps_start;\r
329                         \r
330                 while(ent.chain != self && ent.chain != world)\r
331                         ent = ent.chain;\r
332                 if(ent.chain == self)\r
333                         ent.chain = self.chain;\r
334         }\r
335 }\r
336 \r
337 void Ent_Remove();\r
338 void Ent_ReadPlayerScore()\r
339 {\r
340         float i, Team, n;\r
341         float isNew;\r
342         entity o;\r
343 \r
344         // damnit -.- don't want to go change every single .sv_entnum in sbar.qc AGAIN\r
345         // (no I've never heard of M-x replace-string, sed, or anything like that)\r
346         isNew = !self.owner; // workaround for DP bug\r
347         n = ReadByte()-1;\r
348 \r
349 #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED\r
350         if(!isNew && n != self.sv_entnum)\r
351         {\r
352                 print("A CSQC entity changed its owner!\n");\r
353                 isNew = true;\r
354                 Ent_Remove();\r
355                 self.enttype = ENT_CLIENT_SCORES;\r
356         }\r
357 #endif\r
358 \r
359         self.sv_entnum = n;\r
360         Team = GetPlayerColor(self.sv_entnum);\r
361 \r
362         if not(playerslots[self.sv_entnum])\r
363                 playerslots[self.sv_entnum] = spawn();\r
364         o = self.owner = playerslots[self.sv_entnum];\r
365         o.sv_entnum = self.sv_entnum;\r
366         o.gotscores = 1;\r
367         if not(o.sort_prev)\r
368                 RegisterPlayer(o);\r
369 \r
370         SetTeam(o, Team);\r
371 \r
372 #if MAX_SCORE <= 3\r
373         for(i = 0; i < MAX_SCORE; ++i)\r
374                 o.(scores[i]) = ReadShort();\r
375 #else\r
376         float sf;\r
377 #if MAX_SCORE <= 8\r
378         sf = ReadByte();\r
379 #else\r
380         sf = ReadShort();\r
381 #endif\r
382         float p;\r
383         for(i = 0, p = 1; i < MAX_SCORE; ++i, p *= 2)\r
384                 if(sf & p)\r
385                         o.(scores[i]) = ReadShort();\r
386 #endif\r
387 \r
388         Sbar_UpdatePlayerPos(o);\r
389 }\r
390 \r
391 void Ent_ReadTeamScore()\r
392 {\r
393         float i;\r
394         entity o;\r
395         \r
396         self.team = ReadByte();\r
397         o = self.owner = GetTeam(self.team, true);\r
398 \r
399 #if MAX_TEAMSCORE <= 3\r
400         for(i = 0; i < MAX_TEAMSCORE; ++i)\r
401                 o.(teamscores[i]) = ReadShort();\r
402 #else\r
403         float sf;\r
404 #if MAX_TEAMSCORE <= 8\r
405         sf = ReadByte();\r
406 #else\r
407         sf = ReadShort();\r
408 #endif\r
409         float p;\r
410         for(i = 0, p = 1; i < MAX_TEAMSCORE; ++i, p *= 2)\r
411                 if(sf & p)\r
412                         o.(teamscores[i]) = ReadShort();\r
413 #endif\r
414 \r
415         Sbar_UpdateTeamPos(o);\r
416 }\r
417 \r
418 // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured.\r
419 // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS.\r
420 void(float bIsNewEntity) CSQC_Ent_Update =\r
421 {\r
422         float msg, t;\r
423         t = ReadByte();\r
424 #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED\r
425         if(self.enttype)\r
426                 if(t != self.enttype)\r
427                 {\r
428                         print("A CSQC entity changed its type!\n");\r
429                         Ent_Remove();\r
430                 }\r
431 #endif\r
432         self.enttype = t;\r
433         if(self.enttype == ENT_CLIENT_ENTCS)\r
434         {\r
435                 self.sv_entnum = ReadByte()-1;\r
436 \r
437                 for(msg = ReadByte(); msg != ENTCS_MSG_END; msg = ReadByte())\r
438                 {\r
439                         switch(msg)\r
440                         {\r
441                         case ENTCS_MSG_ONS_GPS: Ent_ReadONS(); break;\r
442                         case ENTCS_MSG_ONS_REMOVE: Ent_RemoveONS(); break;\r
443                         default:\r
444                                 error("unknown ENTCS_MSG type\n");\r
445                         }\r
446                 }\r
447         }\r
448         else if(self.enttype == ENT_CLIENT_SCORES)\r
449                 Ent_ReadPlayerScore();\r
450         else if(self.enttype == ENT_CLIENT_TEAMSCORES)\r
451                 Ent_ReadTeamScore();\r
452         else if(self.enttype == ENT_CLIENT_POINTPARTICLES)\r
453                 Ent_PointParticles();\r
454         else if(self.enttype == ENT_CLIENT_RAINSNOW)\r
455                 Ent_RainOrSnow();\r
456         else if(self.enttype == ENT_CLIENT_LASER)\r
457                 Ent_Laser();\r
458         else\r
459                 error("unknown entity type in CSQC_Ent_Update\n");\r
460         \r
461 };\r
462 // Destructor, but does NOT deallocate the entity by calling remove(). Also\r
463 // used when an entity changes its type. For an entity that someone interacts\r
464 // with others, make sure it can no longer do so.\r
465 void Ent_Remove()\r
466 {\r
467         if(self.enttype == ENT_CLIENT_ENTCS)\r
468         {\r
469                 if(using_gps) //gametype == GAME_ONSLAUGHT)\r
470                 {\r
471                         if(gps_start == self)\r
472                                 gps_start = self.chain;\r
473                         else\r
474                         {\r
475                                 local entity ent;\r
476                                 ent = gps_start;\r
477                         \r
478                                 while(ent.chain != self && ent.chain != world)\r
479                                         ent = ent.chain;\r
480                                 if(ent.chain == self)\r
481                                         ent.chain = self.chain;\r
482                         }\r
483                 }\r
484         } else if(self.enttype == ENT_CLIENT_SCORES)\r
485         {\r
486                 if(self.owner)\r
487                 {\r
488                         SetTeam(self.owner, -1);\r
489                         if(self.owner.sort_prev)\r
490                                 RemovePlayer(self.owner);\r
491                         self.owner.sort_prev = NULL;\r
492                 }\r
493         } else if(self.enttype == ENT_CLIENT_TEAMSCORES)\r
494         {\r
495                 /*\r
496                 if(self.owner)\r
497                         RemoveTeam(self.owner);\r
498                 */\r
499                 // we don't NEED to remove them... they won't display anyway\r
500                 // plus, svqc never does this anyway\r
501         } else if(self.enttype == ENT_CLIENT_POINTPARTICLES)\r
502         {\r
503                 Ent_PointParticles_Remove();\r
504         }\r
505 \r
506         self.enttype = 0;\r
507         self.classname = "";\r
508         self.draw = menu_sub_null;\r
509         // TODO possibly set more stuff to defaults\r
510 }\r
511 // CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed.  Essentially call remove(self) as well.\r
512 void CSQC_Ent_Remove()\r
513 {\r
514         if(self.enttype)\r
515                 Ent_Remove();\r
516         remove(self);\r
517 }\r
518 \r
519 void Gamemode_Init()\r
520 {\r
521         local string mapinfo, infoline;\r
522         local float len;\r
523         local float file;\r
524         local vector mi_min, mi_max;\r
525 \r
526         if(!strcasecmp(substring(mapname, 0, 5), "maps/"))\r
527                 shortmapname = substring(mapname, 5, 999);\r
528         else\r
529                 shortmapname = mapname;\r
530         len = strlen(shortmapname);\r
531         if(!strcasecmp(substring(shortmapname, len-4, 4), ".bsp"))\r
532                 shortmapname = substring(shortmapname, 0, len-4);\r
533         shortmapname = strzone(shortmapname);\r
534 \r
535         if(gametype == GAME_ONSLAUGHT) {\r
536                 mapinfo = strcat("maps/", minimapname, ".info");\r
537                 minimapname = strzone(strcat("gfx/", shortmapname, "_mini.tga"));\r
538 \r
539                 mi_min = world.mins;\r
540                 mi_max = world.maxs;\r
541                 \r
542                 file = fopen(mapinfo, FILE_READ);\r
543                 if(file >= 0) {\r
544                         while((infoline = fgets(file))) {\r
545                                 if(!strncasecmp(infoline, "mins", 4)) {\r
546                                         mi_min = stov(substring(infoline, 5, 999));\r
547                                 } else if(!strncasecmp(infoline, "maxs", 4)) {\r
548                                         mi_max = stov(substring(infoline, 5, 999));\r
549                                 } else if(strncasecmp(infoline, "//", 2)) { // don't print comment-style lines\r
550                                         print(strcat("mapinfo: ", infoline, "\n"));\r
551                                 }\r
552                         }\r
553                 } else {\r
554                         print(strcat("Map has no .info file (", mapinfo, ").\n"));\r
555                 }\r
556                 fclose(file);\r
557 \r
558                 print(strcat("Mins: ", vtos(mi_min), "    Maxs: ", vtos(mi_max), "\n"));\r
559                 \r
560                 mi_center = (mi_min + mi_max) * 0.5;\r
561                 mi_scale = mi_max - mi_min;\r
562                 \r
563                 \r
564                 print(strcat("Using ", minimapname, " as minimap.\n"));\r
565                 precache_pic(minimapname);\r
566                 precache_pic("gfx/ons-cp-neutral.tga");\r
567                 precache_pic("gfx/ons-cp-red.tga");\r
568                 precache_pic("gfx/ons-cp-blue.tga");\r
569                 precache_pic("gfx/ons-frame.tga");\r
570                 precache_pic("gfx/ons-frame-team.tga");\r
571         } else if(gametype == GAME_KEYHUNT) {\r
572                 precache_pic("gfx/sb_key_carrying");\r
573                 precache_pic("gfx/sb_key_carrying_outline");\r
574         }\r
575 }\r
576 // CSQC_Parse_StuffCmd : Provides the stuffcmd string in the first parameter that the server provided.  To execute standard behavior, simply execute localcmd with the string.\r
577 void CSQC_Parse_StuffCmd(string strMessage)\r
578 {\r
579         localcmd(strMessage);\r
580 }\r
581 // CSQC_Parse_Print : Provides the print string in the first parameter that the server provided.  To execute standard behavior, simply execute print with the string.\r
582 void CSQC_Parse_Print(string strMessage)\r
583 {\r
584         print(strMessage);\r
585 }\r
586 // CSQC_Parse_CenterPrint : Provides the centerprint string in the first parameter that the server provided.  To execute standard behavior, simply execute cprint with the string.\r
587 void CSQC_Parse_CenterPrint(string strMessage)\r
588 {\r
589         cprint(strMessage);\r
590 }\r
591 \r
592 void CSQC_CheckRevision();\r
593 \r
594 void Gamemode_Init();\r
595 void Net_ReadScoresInfo()\r
596 {\r
597         float i;\r
598         gametype = ReadByte();\r
599         for(i = 0; i < MAX_SCORE; ++i)\r
600         {\r
601                 scores_label[i] = strzone(ReadString());\r
602                 scores_flags[i] = ReadByte();\r
603         }\r
604         for(i = 0; i < MAX_TEAMSCORE; ++i)\r
605         {\r
606                 teamscores_label[i] = strzone(ReadString());\r
607                 teamscores_flags[i] = ReadByte();\r
608         }\r
609         Sbar_InitScores();\r
610         Gamemode_Init();\r
611 }\r
612 \r
613 void Net_ReadInit()\r
614 {\r
615         csqc_revision = ReadShort();\r
616         maxclients = ReadByte();\r
617         minstagib = ReadByte();\r
618 \r
619         CSQC_CheckRevision();\r
620 }\r
621 \r
622 string Net_ReadPicture()\r
623 {\r
624         string img;\r
625         if(csqc_flags & CSQC_FLAG_READPICTURE)\r
626         {\r
627                 img = ReadPicture();\r
628                 print(strcat("Got Picture: ", img, "\n"));\r
629         } else {\r
630                 img = ReadString();\r
631                 print(strcat("^3Warning: ^7Couldn't download ", img, ". This is probably because your engine build is outdated.\n"));\r
632                 float psize, i;\r
633                 psize = ReadShort();\r
634                 // Can I be sure that ReadShort is 2 bytes and ReadLong is 4 bytes?\r
635                 // Because then this could be optimized to first get all 4-byte-groups,\r
636                 // then the remaining 2, then the remaining 1\r
637                 for(i = 0; i < psize; ++i)\r
638                         ReadByte();\r
639         }\r
640         return img;\r
641 }\r
642 \r
643 void Net_Config()\r
644 {\r
645         string key, value;\r
646         key = ReadString();\r
647         value = ReadString();\r
648         db_put(configdb, strcat("/v/", key), value);\r
649         db_put(configdb, strcat("/s/", key), "1");\r
650 }\r
651 \r
652 void Net_ReadRace()\r
653 {\r
654         float b;\r
655 \r
656         b = ReadByte();\r
657 \r
658         switch(b)\r
659         {\r
660                 case RACE_NET_CHECKPOINT_HIT_QUALIFYING:\r
661                         race_checkpoint = ReadByte();\r
662                         race_time = ReadShort();\r
663                         race_previousbesttime = ReadShort();\r
664                         if(race_previousbestname)\r
665                                 strunzone(race_previousbestname);\r
666                         race_previousbestname = strzone(ReadString());\r
667 \r
668                         race_checkpointtime = time;\r
669 \r
670                         if(race_checkpoint == 0)\r
671                                 race_laptime = time; // valid\r
672 \r
673                         break;\r
674 \r
675                 case RACE_NET_CHECKPOINT_CLEAR:\r
676                         race_laptime = 0;\r
677                         race_checkpointtime = 0;\r
678                         break;\r
679 \r
680                 case RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING:\r
681                         race_laptime = ReadCoord();\r
682                         race_checkpointtime = -99999;\r
683                         // fall through\r
684                 case RACE_NET_CHECKPOINT_NEXT_QUALIFYING:\r
685                         race_nextcheckpoint = ReadByte();\r
686 \r
687                         race_nextbesttime = ReadShort();\r
688                         if(race_nextbestname)\r
689                                 strunzone(race_nextbestname);\r
690                         race_nextbestname = strzone(ReadString());\r
691                         break;\r
692 \r
693                 case RACE_NET_CHECKPOINT_HIT_RACE:\r
694                         race_mycheckpoint = ReadByte();\r
695                         race_mycheckpointtime = time;\r
696                         race_mycheckpointdelta = ReadShort();\r
697                         race_mycheckpointlapsdelta = ReadByte();\r
698                         if(race_mycheckpointlapsdelta >= 128)\r
699                                 race_mycheckpointlapsdelta -= 256;\r
700                         if(race_mycheckpointenemy)\r
701                                 strunzone(race_mycheckpointenemy);\r
702                         race_mycheckpointenemy = strzone(ReadString());\r
703                         break;\r
704 \r
705                 case RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT:\r
706                         race_othercheckpoint = ReadByte();\r
707                         race_othercheckpointtime = time;\r
708                         race_othercheckpointdelta = ReadShort();\r
709                         race_othercheckpointlapsdelta = ReadByte();\r
710                         if(race_othercheckpointlapsdelta >= 128)\r
711                                 race_othercheckpointlapsdelta -= 256;\r
712                         if(race_othercheckpointenemy)\r
713                                 strunzone(race_othercheckpointenemy);\r
714                         race_othercheckpointenemy = strzone(ReadString());\r
715                         break;\r
716         }\r
717 }\r
718 \r
719 void Net_ReadForceScoreboard()\r
720 {\r
721         sb_showscores_force = ReadByte();\r
722 }\r
723 \r
724 void Net_Reset()\r
725 {\r
726         race_laptime = 0;\r
727         race_checkpointtime = 0;\r
728 }\r
729 \r
730 void Net_ReadSpectating()\r
731 {\r
732         float newspectatee_status;\r
733         newspectatee_status = ReadByte();\r
734         if(newspectatee_status == player_localentnum)\r
735                 newspectatee_status = -1; // observing\r
736         if(newspectatee_status != spectatee_status)\r
737                 Net_Reset();\r
738         spectatee_status = newspectatee_status;\r
739 }\r
740 \r
741 void Net_ReadSpawn()\r
742 {\r
743         zoomin_effect = 1;\r
744         current_viewzoom = 0.6;\r
745 }\r
746 \r
747 // CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer.\r
748 // You must ALWAYS first acquire the temporary ID, which is sent as a byte.\r
749 // Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event.\r
750 float CSQC_Parse_TempEntity()\r
751 {\r
752         local float bHandled;\r
753                 bHandled  = true;\r
754         // Acquire TE ID\r
755         local float nTEID;\r
756                 nTEID = ReadByte();\r
757 \r
758                 // NOTE: Could just do return instead of break...\r
759         switch(nTEID)\r
760         {\r
761                 case TE_CSQC_INIT:\r
762                         Net_ReadInit();\r
763                         bHandled = true;\r
764                         break;\r
765                 case TE_CSQC_MAPVOTE:\r
766                         Net_Mapvote();\r
767                         bHandled = true;\r
768                         break;\r
769                 case TE_CSQC_CONFIG:\r
770                         Net_Config();\r
771                         bHandled = true;\r
772                         break;\r
773                 case TE_CSQC_SCORESINFO:\r
774                         Net_ReadScoresInfo();\r
775                         bHandled = true;\r
776                         break;\r
777                 case TE_CSQC_RACE:\r
778                         Net_ReadRace();\r
779                         bHandled = true;\r
780                         break;\r
781                 case TE_CSQC_FORCESCOREBOARD:\r
782                         Net_ReadForceScoreboard();\r
783                         bHandled = true;\r
784                         break;\r
785                 case TE_CSQC_SPECTATING:\r
786                         Net_ReadSpectating();\r
787                         bHandled = true;\r
788                         break;\r
789                 case 13: // TE_BEAM\r
790                         Net_GrapplingHook();\r
791                         bHandled = true;\r
792                         break;\r
793                 case TE_CSQC_SPAWN: // TE_BEAM\r
794                         Net_ReadSpawn();\r
795                         bHandled = true;\r
796                         break;\r
797                 default:\r
798                         // No special logic for this temporary entity; return 0 so the engine can handle it\r
799                         bHandled = false;\r
800                         break;\r
801         }\r
802         \r
803         if(!postinit)\r
804                 PostInit();\r
805                 \r
806         return bHandled;\r
807 }\r
808 \r
809 // COMMIT-TODO: Update if necessare, before committing\r
810 void CSQC_CheckRevision()\r
811 {\r
812         if(csqc_revision == CSQC_REVISION)\r
813         {\r
814                 print("^2SVQC and CSQC revisions are compatible.\n");\r
815         } else if(csqc_revision < CSQC_REVISION) {\r
816                 print("^1Your csprogs.dat (CSQC) version is newer than the one on the server.\n");\r
817         } else if(csqc_revision > CSQC_REVISION) {\r
818                 print("^1Your csprogs.dat (CSQC) is too old for this server.\n");\r
819                 print("^1Please update to a newer version.\n");\r
820         }\r
821 }\r