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