]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/client/Main.qc
bugfix target_spawn, and change docs to match 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         entity e;\r
198         for(i = 0; i < maxclients; ++i)\r
199         {\r
200                 e = playerslots[i];\r
201                 if(getplayerkey(i, "name") == "")\r
202                 {\r
203                         if(e.sort_prev)\r
204                         {\r
205                                 //print("playerchecker: KILL KILL KILL\n");\r
206                                 // player disconnected\r
207                                 SetTeam(e, -1);\r
208                                 RemovePlayer(e);\r
209                                 e.sort_prev = world;\r
210                                 //e.gotscores = 0;\r
211                         }\r
212                 }\r
213                 else\r
214                 {\r
215                         if not(e.sort_prev)\r
216                         {\r
217                                 //print("playerchecker: SPAWN SPAWN SPAWN\n");\r
218                                 // player connected\r
219                                 if not(e)\r
220                                         playerslots[i] = e = spawn();\r
221                                 e.sv_entnum = i;\r
222                                 //e.gotscores = 0; // we might already have the scores...\r
223                                 SetTeam(e, GetPlayerColor(i)); // will not hurt; later updates come with Sbar_UpdatePlayerTeams\r
224                                 RegisterPlayer(e);\r
225                                 Sbar_UpdatePlayerPos(e);\r
226                         }\r
227                 }\r
228         }\r
229         self.nextthink = time + 0.2;\r
230 }\r
231 \r
232 void Porto_Init();\r
233 void PostInit(void)\r
234 {\r
235         print(strcat("PostInit\n    maxclients = ", ftos(maxclients), "\n"));\r
236         localcmd(strcat("\nsbar_columns_set ", cvar_string("sbar_columns"), ";\n"));\r
237 \r
238         entity playerchecker;\r
239         playerchecker = spawn();\r
240         playerchecker.think = Playerchecker_Think;\r
241         playerchecker.nextthink = time + 0.2;\r
242 \r
243         Porto_Init();\r
244 \r
245         postinit = true;\r
246 }\r
247 \r
248 // CSQC_ConsoleCommand : Used to parse commands in the console that have been registered with the "registercmd" function\r
249 // Return value should be 1 if CSQC handled the command, otherwise return 0 to have the engine handle it.\r
250 float button_zoom;\r
251 void Cmd_Sbar_SetFields(float);\r
252 void Cmd_Sbar_Help(float);\r
253 float CSQC_ConsoleCommand(string strMessage)\r
254 {\r
255         float argc;\r
256         // Tokenize String\r
257         //argc = tokenize(strMessage);\r
258         argc = tokenize_sane(strMessage);\r
259         \r
260         // Acquire Command\r
261         local string strCmd;\r
262         strCmd = argv(0);\r
263 \r
264         if(strCmd == "+button4") { // zoom\r
265                 // return false, because the message shall be sent to the server anyway (for demos/speccing)\r
266                 if(ignore_plus_zoom)\r
267                 {\r
268                         --ignore_plus_zoom;\r
269                         return false;\r
270                 }\r
271                 button_zoom = 1;\r
272                 return true;\r
273         } else if(strCmd == "-button4") { // zoom\r
274                 if(ignore_minus_zoom)\r
275                 {\r
276                         --ignore_minus_zoom;\r
277                         return false;\r
278                 }\r
279                 button_zoom = 0;\r
280                 return true;\r
281         } else if(strCmd == "+button3") { // secondary\r
282                 button_attack2 = 1;\r
283                 return false;\r
284         } else if(strCmd == "-button3") { // secondary\r
285                 button_attack2 = 0;\r
286                 return false;\r
287         } else if(strCmd == "ons_map") {\r
288                 Cmd_ons_map();\r
289                 return true;\r
290         } else if(strCmd == "sbar_columns_set") {\r
291                 Cmd_Sbar_SetFields(argc);\r
292                 return true;\r
293         } else if(strCmd == "sbar_columns_help") {\r
294                 Cmd_Sbar_Help(argc);\r
295                 return true;\r
296         } else if(strCmd == "+showscores") {\r
297                 sb_showscores = true;\r
298                 return true;\r
299         } else if(strCmd == "-showscores") {\r
300                 sb_showscores = false;\r
301                 return true;\r
302         }\r
303         \r
304         return false;\r
305 }\r
306 \r
307 float GameCommand(string msg)\r
308 {\r
309         float argc;\r
310         argc = tokenize_sane(msg);\r
311         string cmd;\r
312         cmd = argv(0);\r
313         if(cmd == "mv_download") {\r
314                 Cmd_MapVote_MapDownload(argc);\r
315                 return true;\r
316         }\r
317         else if(cmd == "settemp") {\r
318                 cvar_clientsettemp(argv(1), argv(2));\r
319         }\r
320         \r
321         return false;\r
322 }\r
323 \r
324 // CSQC_InputEvent : Used to perform actions based on any key pressed, key released and mouse on the client.\r
325 // Return value should be 1 if CSQC handled the input, otherwise return 0 to have the input passed to the engine.\r
326 // All keys are in ascii.\r
327 // bInputType = 0 is key pressed, 1 is key released, 2 is mouse input.\r
328 // In the case of keyboard input, nPrimary is the ascii code, and nSecondary is 0.\r
329 // In the case of mouse input, nPrimary is xdelta, nSecondary is ydelta.\r
330 float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary)\r
331 {\r
332         local float bSkipKey;\r
333         bSkipKey = false;\r
334         \r
335         if(menu_visible)\r
336                 if(menu_action(bInputType, nPrimary, nSecondary))\r
337                         return TRUE;\r
338         return bSkipKey;\r
339 }\r
340 \r
341 // END REQUIRED CSQC FUNCTIONS\r
342 // --------------------------------------------------------------------------\r
343 \r
344 // --------------------------------------------------------------------------\r
345 // BEGIN OPTIONAL CSQC FUNCTIONS\r
346 void Ent_ReadEntCS()\r
347 {\r
348         InterpolateOrigin_Undo();\r
349 \r
350         self.classname = "entcs_receiver";\r
351         self.sv_entnum = ReadByte() - 1;\r
352         self.origin_x = ReadShort();\r
353         self.origin_y = ReadShort();\r
354         self.origin_z = ReadShort();\r
355         self.angles_y = ReadByte() * 360.0 / 256;\r
356         self.origin_z = self.angles_x = self.angles_z = 0;\r
357 \r
358         InterpolateOrigin_Note();\r
359 }\r
360 \r
361 void Ent_RemoveEntCS()\r
362 {\r
363 }\r
364 \r
365 void Ent_Remove();\r
366 void Ent_ReadPlayerScore()\r
367 {\r
368         float i, 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 \r
389         if not(playerslots[self.sv_entnum])\r
390                 playerslots[self.sv_entnum] = spawn();\r
391         o = self.owner = playerslots[self.sv_entnum];\r
392         o.sv_entnum = self.sv_entnum;\r
393         o.gotscores = 1;\r
394 \r
395         //if not(o.sort_prev)\r
396         //      RegisterPlayer(o);\r
397         //playerchecker will do this for us later, if it has not already done so\r
398 \r
399 #if MAX_SCORE <= 3\r
400         for(i = 0; i < MAX_SCORE; ++i)\r
401                 o.(scores[i]) = ReadShort();\r
402 #else\r
403         float sf;\r
404 #if MAX_SCORE <= 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_SCORE; ++i, p *= 2)\r
411                 if(sf & p)\r
412                         o.(scores[i]) = ReadShort();\r
413 #endif\r
414 \r
415         if(o.sort_prev)\r
416                 Sbar_UpdatePlayerPos(o); // if not registered, we cannot do this yet!\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 void Ent_Nagger()\r
447 {\r
448         float nags;\r
449 \r
450         nags = ReadByte();\r
451 \r
452         if(nags & 128)\r
453         {\r
454                 if(vote_called_vote)\r
455                         strunzone(vote_called_vote);\r
456                 vote_called_vote = strzone(ReadString());\r
457         }\r
458 \r
459         ready_waiting = (nags & 1);\r
460         ready_waiting_for_me = (nags & 2);\r
461         vote_waiting = (nags & 4);\r
462         vote_waiting_for_me = (nags & 8);\r
463 }\r
464 \r
465 // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured.\r
466 // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS.\r
467 void(float bIsNewEntity) CSQC_Ent_Update =\r
468 {\r
469         float t;\r
470         t = ReadByte();\r
471 #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED\r
472         if(self.enttype)\r
473                 if(t != self.enttype)\r
474                 {\r
475                         print("A CSQC entity changed its type!\n");\r
476                         Ent_Remove();\r
477                 }\r
478 #endif\r
479         self.enttype = t;\r
480         if(self.enttype == ENT_CLIENT_ENTCS)\r
481                 Ent_ReadEntCS();\r
482         else if(self.enttype == ENT_CLIENT_SCORES)\r
483                 Ent_ReadPlayerScore();\r
484         else if(self.enttype == ENT_CLIENT_TEAMSCORES)\r
485                 Ent_ReadTeamScore();\r
486         else if(self.enttype == ENT_CLIENT_POINTPARTICLES)\r
487                 Ent_PointParticles();\r
488         else if(self.enttype == ENT_CLIENT_RAINSNOW)\r
489                 Ent_RainOrSnow();\r
490         else if(self.enttype == ENT_CLIENT_LASER)\r
491                 Ent_Laser();\r
492         else if(self.enttype == ENT_CLIENT_NAGGER)\r
493                 Ent_Nagger();\r
494         else if(self.enttype == ENT_CLIENT_WAYPOINT)\r
495                 Ent_WaypointSprite();\r
496         else\r
497                 error(strcat("unknown entity type in CSQC_Ent_Update: ", ftos(self.enttype), "\n"));\r
498         \r
499 };\r
500 // Destructor, but does NOT deallocate the entity by calling remove(). Also\r
501 // used when an entity changes its type. For an entity that someone interacts\r
502 // with others, make sure it can no longer do so.\r
503 void Ent_Remove()\r
504 {\r
505         float i;\r
506         if(self.enttype == ENT_CLIENT_ENTCS)\r
507         {\r
508                 Ent_RemoveEntCS();\r
509         } else if(self.enttype == ENT_CLIENT_SCORES)\r
510         {\r
511                 if(self.owner)\r
512                 {\r
513                         SetTeam(self.owner, -1);\r
514                         self.owner.gotscores = 0;\r
515                         for(i = 0; i < MAX_SCORE; ++i)\r
516                                 self.owner.(scores[i]) = 0; // clear all scores\r
517                 }\r
518         } else if(self.enttype == ENT_CLIENT_TEAMSCORES)\r
519         {\r
520                 /*\r
521                 if(self.owner)\r
522                         RemoveTeam(self.owner);\r
523                 */\r
524                 // we don't NEED to remove them... they won't display anyway\r
525                 // plus, svqc never does this anyway\r
526         } else if(self.enttype == ENT_CLIENT_POINTPARTICLES)\r
527         {\r
528                 Ent_PointParticles_Remove();\r
529         }\r
530         else if(self.enttype == ENT_CLIENT_WAYPOINT)\r
531                 Ent_RemoveWaypointSprite();\r
532 \r
533         self.enttype = 0;\r
534         self.classname = "";\r
535         self.draw = menu_sub_null;\r
536         // TODO possibly set more stuff to defaults\r
537 }\r
538 // CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed.  Essentially call remove(self) as well.\r
539 void CSQC_Ent_Remove()\r
540 {\r
541         if(self.enttype)\r
542                 Ent_Remove();\r
543         remove(self);\r
544 }\r
545 \r
546 void Gamemode_Init()\r
547 {\r
548         get_mi_min_max_texcoords(1); // try the CLEVER way first\r
549         minimapname = strcat("gfx/", mi_shortname, "_radar.tga");\r
550         shortmapname = mi_shortname;\r
551 \r
552         if(precache_pic(minimapname) == "")\r
553         {\r
554                 // but maybe we have a non-clever minimap\r
555                 minimapname = strcat("gfx/", mi_shortname, "_mini.tga");\r
556                 if(precache_pic(minimapname) == "")\r
557                         minimapname = ""; // FAIL\r
558                 else\r
559                         get_mi_min_max_texcoords(0); // load new texcoords\r
560         }\r
561 \r
562         mi_center = (mi_min + mi_max) * 0.5;\r
563         mi_scale = mi_max - mi_min;\r
564         minimapname = strzone(minimapname);\r
565 \r
566         if(gametype == GAME_ONSLAUGHT) {\r
567                 print(strcat("Using ", minimapname, " as minimap.\n"));\r
568                 precache_pic("gfx/ons-cp-neutral.tga");\r
569                 precache_pic("gfx/ons-cp-red.tga");\r
570                 precache_pic("gfx/ons-cp-blue.tga");\r
571                 precache_pic("gfx/ons-frame.tga");\r
572                 precache_pic("gfx/ons-frame-team.tga");\r
573         } else if(gametype == GAME_KEYHUNT) {\r
574                 precache_pic("gfx/sb_key_carrying");\r
575                 precache_pic("gfx/sb_key_carrying_outline");\r
576         }\r
577 }\r
578 // 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
579 void CSQC_Parse_StuffCmd(string strMessage)\r
580 {\r
581         localcmd(strMessage);\r
582 }\r
583 // 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
584 void CSQC_Parse_Print(string strMessage)\r
585 {\r
586         print(strMessage);\r
587 }\r
588 // 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
589 void CSQC_Parse_CenterPrint(string strMessage)\r
590 {\r
591         cprint(strMessage);\r
592 }\r
593 \r
594 void CSQC_CheckRevision();\r
595 \r
596 void Fog_Force()\r
597 {\r
598         // TODO somehow thwart prvm_globalset client ...\r
599 \r
600         if(forcefog != "")\r
601                 localcmd(strcat("\nfog ", forcefog, "\nr_fog_exp2 0\nr_drawfog 1\n"));\r
602 }\r
603 \r
604 void Net_ReadFog()\r
605 {\r
606         if(forcefog)\r
607                 strunzone(forcefog);\r
608         ReadCoord(); // ignore fog interval\r
609         forcefog = strzone(ReadString());\r
610 }\r
611 \r
612 void Gamemode_Init();\r
613 void Net_ReadScoresInfo()\r
614 {\r
615         float i;\r
616         gametype = ReadByte();\r
617         for(i = 0; i < MAX_SCORE; ++i)\r
618         {\r
619                 scores_label[i] = strzone(ReadString());\r
620                 scores_flags[i] = ReadByte();\r
621         }\r
622         for(i = 0; i < MAX_TEAMSCORE; ++i)\r
623         {\r
624                 teamscores_label[i] = strzone(ReadString());\r
625                 teamscores_flags[i] = ReadByte();\r
626         }\r
627         Sbar_InitScores();\r
628         Gamemode_Init();\r
629 }\r
630 \r
631 void Net_ReadInit()\r
632 {\r
633         float i;\r
634         csqc_revision = ReadShort();\r
635         maxclients = ReadByte();\r
636         for(i = 0; i < 24; ++i)\r
637                 weaponimpulse[i] = ReadByte() - 1;\r
638         CSQC_CheckRevision();\r
639 }\r
640 \r
641 string Net_ReadPicture()\r
642 {\r
643         string img;\r
644         if(csqc_flags & CSQC_FLAG_READPICTURE)\r
645         {\r
646                 img = ReadPicture();\r
647                 print(strcat("Got Picture: ", img, "\n"));\r
648         } else {\r
649                 img = ReadString();\r
650                 print(strcat("^3Warning: ^7Couldn't download ", img, ". This is probably because your engine build is outdated.\n"));\r
651                 float psize, i;\r
652                 psize = ReadShort();\r
653                 // Can I be sure that ReadShort is 2 bytes and ReadLong is 4 bytes?\r
654                 // Because then this could be optimized to first get all 4-byte-groups,\r
655                 // then the remaining 2, then the remaining 1\r
656                 for(i = 0; i < psize; ++i)\r
657                         ReadByte();\r
658         }\r
659         return img;\r
660 }\r
661 \r
662 void Net_Config()\r
663 {\r
664         string key, value;\r
665         key = ReadString();\r
666         value = ReadString();\r
667         db_put(configdb, strcat("/v/", key), value);\r
668         db_put(configdb, strcat("/s/", key), "1");\r
669 }\r
670 \r
671 void Net_ReadRace()\r
672 {\r
673         float b;\r
674 \r
675         b = ReadByte();\r
676 \r
677         switch(b)\r
678         {\r
679                 case RACE_NET_CHECKPOINT_HIT_QUALIFYING:\r
680                         race_checkpoint = ReadByte();\r
681                         race_time = ReadShort();\r
682                         race_previousbesttime = ReadShort();\r
683                         if(race_previousbestname)\r
684                                 strunzone(race_previousbestname);\r
685                         race_previousbestname = strzone(ReadString());\r
686 \r
687                         race_checkpointtime = time;\r
688 \r
689                         if(race_checkpoint == 0)\r
690                                 race_laptime = time; // valid\r
691 \r
692                         break;\r
693 \r
694                 case RACE_NET_CHECKPOINT_CLEAR:\r
695                         race_laptime = 0;\r
696                         race_checkpointtime = 0;\r
697                         break;\r
698 \r
699                 case RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING:\r
700                         race_laptime = ReadCoord();\r
701                         race_checkpointtime = -99999;\r
702                         // fall through\r
703                 case RACE_NET_CHECKPOINT_NEXT_QUALIFYING:\r
704                         race_nextcheckpoint = ReadByte();\r
705 \r
706                         race_nextbesttime = ReadShort();\r
707                         if(race_nextbestname)\r
708                                 strunzone(race_nextbestname);\r
709                         race_nextbestname = strzone(ReadString());\r
710                         break;\r
711 \r
712                 case RACE_NET_CHECKPOINT_HIT_RACE:\r
713                         race_mycheckpoint = ReadByte();\r
714                         race_mycheckpointtime = time;\r
715                         race_mycheckpointdelta = ReadShort();\r
716                         race_mycheckpointlapsdelta = ReadByte();\r
717                         if(race_mycheckpointlapsdelta >= 128)\r
718                                 race_mycheckpointlapsdelta -= 256;\r
719                         if(race_mycheckpointenemy)\r
720                                 strunzone(race_mycheckpointenemy);\r
721                         race_mycheckpointenemy = strzone(ReadString());\r
722                         break;\r
723 \r
724                 case RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT:\r
725                         race_othercheckpoint = ReadByte();\r
726                         race_othercheckpointtime = time;\r
727                         race_othercheckpointdelta = ReadShort();\r
728                         race_othercheckpointlapsdelta = ReadByte();\r
729                         if(race_othercheckpointlapsdelta >= 128)\r
730                                 race_othercheckpointlapsdelta -= 256;\r
731                         if(race_othercheckpointenemy)\r
732                                 strunzone(race_othercheckpointenemy);\r
733                         race_othercheckpointenemy = strzone(ReadString());\r
734                         break;\r
735         }\r
736 }\r
737 \r
738 void Net_ReadForceScoreboard()\r
739 {\r
740         sb_showscores_force = ReadByte();\r
741 }\r
742 \r
743 void Net_Reset()\r
744 {\r
745         float i;\r
746         race_laptime = 0;\r
747         race_checkpointtime = 0;\r
748         for(i = 0; i < 24; ++i)\r
749                 angles_held_status[i] = 0;\r
750 }\r
751 \r
752 void Net_ReadSpectating()\r
753 {\r
754         float newspectatee_status;\r
755         newspectatee_status = ReadByte();\r
756         if(newspectatee_status == player_localentnum)\r
757                 newspectatee_status = -1; // observing\r
758         if(newspectatee_status != spectatee_status)\r
759                 Net_Reset();\r
760         spectatee_status = newspectatee_status;\r
761 }\r
762 \r
763 void Net_ReadZoomNotify()\r
764 {\r
765         spectatorbutton_zoom = ReadByte();\r
766 }\r
767 \r
768 void Net_ReadSpawn()\r
769 {\r
770         zoomin_effect = 1;\r
771         current_viewzoom = 0.6;\r
772 }\r
773 \r
774 void Net_ReadWarmupStage()\r
775 {\r
776         warmup_stage = ReadByte();\r
777 }\r
778 \r
779 // CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer.\r
780 // You must ALWAYS first acquire the temporary ID, which is sent as a byte.\r
781 // Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event.\r
782 void Net_ReadHoldAngles();\r
783 float CSQC_Parse_TempEntity()\r
784 {\r
785         local float bHandled;\r
786                 bHandled  = true;\r
787         // Acquire TE ID\r
788         local float nTEID;\r
789                 nTEID = ReadByte();\r
790 \r
791                 // NOTE: Could just do return instead of break...\r
792         switch(nTEID)\r
793         {\r
794                 case TE_CSQC_INIT:\r
795                         Net_ReadInit();\r
796                         bHandled = true;\r
797                         break;\r
798                 case TE_CSQC_MAPVOTE:\r
799                         Net_Mapvote();\r
800                         bHandled = true;\r
801                         break;\r
802                 case TE_CSQC_CONFIG:\r
803                         Net_Config();\r
804                         bHandled = true;\r
805                         break;\r
806                 case TE_CSQC_SCORESINFO:\r
807                         Net_ReadScoresInfo();\r
808                         bHandled = true;\r
809                         break;\r
810                 case TE_CSQC_RACE:\r
811                         Net_ReadRace();\r
812                         bHandled = true;\r
813                         break;\r
814                 case TE_CSQC_FORCESCOREBOARD:\r
815                         Net_ReadForceScoreboard();\r
816                         bHandled = true;\r
817                         break;\r
818                 case TE_CSQC_SPECTATING:\r
819                         Net_ReadSpectating();\r
820                         bHandled = true;\r
821                         break;\r
822                 case 13: // TE_BEAM\r
823                         Net_GrapplingHook();\r
824                         bHandled = true;\r
825                         break;\r
826                 case TE_CSQC_SPAWN:\r
827                         Net_ReadSpawn();\r
828                         bHandled = true;\r
829                         break;\r
830                 case TE_CSQC_ZOOMNOTIFY:\r
831                         Net_ReadZoomNotify();\r
832                         bHandled = true;\r
833                         break;\r
834                 case TE_CSQC_HOLDANGLES:\r
835                         Net_ReadHoldAngles();\r
836                         bHandled = true;\r
837                         break;\r
838                 case TE_CSQC_WARMUP:\r
839                         Net_ReadWarmupStage();\r
840                         bHandled = true;\r
841                         break;\r
842                 case TE_CSQC_FOG:\r
843                         Net_ReadFog();\r
844                         bHandled = true;\r
845                         break;\r
846                 default:\r
847                         // No special logic for this temporary entity; return 0 so the engine can handle it\r
848                         bHandled = false;\r
849                         break;\r
850         }\r
851         \r
852         if(!postinit)\r
853                 PostInit();\r
854                 \r
855         return bHandled;\r
856 }\r
857 \r
858 // COMMIT-TODO: Update if necessare, before committing\r
859 void CSQC_CheckRevision()\r
860 {\r
861         if(csqc_revision == CSQC_REVISION)\r
862         {\r
863                 print("^2SVQC and CSQC revisions are compatible.\n");\r
864         } else if(csqc_revision < CSQC_REVISION) {\r
865                 print("^1Your csprogs.dat (CSQC) version is newer than the one on the server.\n");\r
866         } else if(csqc_revision > CSQC_REVISION) {\r
867                 print("^1Your csprogs.dat (CSQC) is too old for this server.\n");\r
868                 print("^1Please update to a newer version.\n");\r
869         }\r
870 }\r
871 \r
872 string getcommandkey(string text, string command)\r
873 {\r
874         string keys;\r
875         float n, j, k, l;\r
876 \r
877         if (!sbar_showbinds)\r
878                 return text;\r
879 \r
880         keys = db_get(binddb, command);\r
881         if(csqc_flags & CSQC_FLAG_READPICTURE)\r
882         {\r
883                 if (!keys) \r
884                 {\r
885                         n = tokenize_insane(findkeysforcommand(command)); // uses '...' strings\r
886                         for(j = 0; j < n; ++j)\r
887                         {\r
888                                 k = stof(argv(j));\r
889                                 if(k != -1) \r
890                                 {\r
891                                         if ("" == keys)\r
892                                                 keys = keynumtostring(k);\r
893                                         else\r
894                                                 keys = strcat(keys, ", ", keynumtostring(k));\r
895 \r
896                                         ++l;\r
897                                         if (sbar_showbinds_limit > 0 && sbar_showbinds_limit >= l) break;\r
898                                 }\r
899                         \r
900                         }\r
901                         db_put(binddb, command, keys);\r
902                 }\r
903         }\r
904         \r
905         if ("" == keys) {\r
906                 if (sbar_showbinds > 1)\r
907                         return strcat(text, " (not bound)");\r
908                 else\r
909                         return text;\r
910         }\r
911         else if (sbar_showbinds > 1)\r
912                 return strcat(text, " (", keys, ")");\r
913         else\r
914                 return keys;\r
915 }\r