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