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