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