]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/client/Main.qc
ready status indicator in scoreboard
[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 \r
73         for(i = 0; i < 255; ++i)\r
74                 if(getplayerkey(i, "viewentity") == "")\r
75                         break;\r
76         maxclients = i;\r
77 \r
78         //ctf_temp_1 = "";\r
79         // localcmd("alias order \"cmd order $*\""); enable if ctf-command thingy is used\r
80         //registercmd("ctf_menu");\r
81         registercmd("ons_map");\r
82         //registercmd("menu_action");\r
83         registercmd("sbar_columns_set");\r
84         registercmd("sbar_columns_help");\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         R_AddEntity(self);\r
371 }\r
372 void ShotOrg_Draw2D()\r
373 {\r
374         vector coord2d_topleft, coord2d_topright, coord2d;\r
375         string s;\r
376         vector fs;\r
377 \r
378         s = vtos(self.view_ofs);\r
379         s = substring(s, 1, strlen(s) - 2);\r
380         if(tokenize_sane(s) == 3)\r
381                 s = strcat(argv(0), " ", argv(1), " ", argv(2));\r
382 \r
383         coord2d_topleft = project_3d_to_2d(self.origin + view_up * 4 - view_right * 4);\r
384         coord2d_topright = project_3d_to_2d(self.origin + view_up * 4 + view_right * 4);\r
385 \r
386         fs = '1 1 0' * ((coord2d_topright_x - coord2d_topleft_x) / stringwidth(s, FALSE));\r
387 \r
388         coord2d = coord2d_topleft - '0 1 0' * fs_y;\r
389         coord2d_z = 0;\r
390         drawstring(coord2d, s, fs, '1 1 1', 1, 0);\r
391 }\r
392 \r
393 void ShotOrg_Spawn()\r
394 {\r
395         debug_shotorg = spawn();\r
396         debug_shotorg.draw = ShotOrg_Draw;\r
397         debug_shotorg.draw2d = ShotOrg_Draw2D;\r
398         debug_shotorg.renderflags = RF_VIEWMODEL;\r
399         debug_shotorg.effects = EF_FULLBRIGHT;\r
400         precache_model("models/shotorg_adjuster.md3");\r
401         setmodel(debug_shotorg, "models/shotorg_adjuster.md3");\r
402         debug_shotorg.scale = 2;\r
403         debug_shotorg.view_ofs = '25 8 -8';\r
404 }\r
405 \r
406 void GameCommand(string msg)\r
407 {\r
408         float argc;\r
409         argc = tokenize_sane(msg);\r
410 \r
411         if(argv(0) == "help" || argc == 0)\r
412         {\r
413                 print("Usage: sv_cmd COMMAND..., where possible commands are:\n");\r
414                 print("  settemp cvar value\n");\r
415                 print("  radar\n");\r
416                 print("  sbar_columns_set ...\n");\r
417                 print("  sbar_columns_help\n");\r
418                 GameCommand_Generic("help");\r
419         }\r
420 \r
421         if(GameCommand_Generic(msg))\r
422                 return;\r
423 \r
424         string cmd;\r
425         cmd = argv(0);\r
426         if(cmd == "mv_download") {\r
427                 Cmd_MapVote_MapDownload(argc);\r
428         }\r
429         else if(cmd == "settemp") {\r
430                 cvar_clientsettemp(argv(1), argv(2));\r
431         }\r
432         else if(cmd == "radar") {\r
433                 ons_showmap = !ons_showmap;\r
434         }\r
435         else if(cmd == "sbar_columns_set") {\r
436                 Cmd_Sbar_SetFields(argc);\r
437         }\r
438         else if(cmd == "sbar_columns_help") {\r
439                 Cmd_Sbar_Help(argc);\r
440         }\r
441 #ifdef BLURTEST\r
442         else if(cmd == "blurtest") {\r
443                 blurtest_time0 = time;\r
444                 blurtest_time1 = time + stof(argv(1));\r
445                 blurtest_radius = stof(argv(2));\r
446                 blurtest_power = stof(argv(3));\r
447         }\r
448 #endif\r
449         else if(cmd == "shotorg_move") {\r
450                 if(!debug_shotorg)\r
451                         ShotOrg_Spawn();\r
452                 else\r
453                         debug_shotorg.view_ofs = debug_shotorg.view_ofs + stov(argv(1));\r
454                 localcmd("sv_cmd debug_shotorg \"", vtos(debug_shotorg.view_ofs), "\"\n");\r
455         }\r
456         else if(cmd == "shotorg_movez") {\r
457                 if(!debug_shotorg)\r
458                         ShotOrg_Spawn();\r
459                 else\r
460                         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
461                 localcmd("sv_cmd debug_shotorg \"", vtos(debug_shotorg.view_ofs), "\"\n");\r
462         }\r
463         else if(cmd == "shotorg_set") {\r
464                 if(!debug_shotorg)\r
465                         ShotOrg_Spawn();\r
466                 else\r
467                         debug_shotorg.view_ofs = stov(argv(1));\r
468                 localcmd("sv_cmd debug_shotorg \"", vtos(debug_shotorg.view_ofs), "\"\n");\r
469         }\r
470         else if(cmd == "shotorg_setz") {\r
471                 if(!debug_shotorg)\r
472                         ShotOrg_Spawn();\r
473                 else\r
474                         debug_shotorg.view_ofs = debug_shotorg.view_ofs * (stof(argv(1)) / debug_shotorg.view_ofs_x); // closer/farther, same xy pos\r
475                 localcmd("sv_cmd debug_shotorg \"", vtos(debug_shotorg.view_ofs), "\"\n");\r
476         }\r
477         else if(cmd == "shotorg_end") {\r
478                 if(debug_shotorg)\r
479                 {\r
480                         print(vtos(debug_shotorg.view_ofs), "\n");\r
481                         remove(debug_shotorg);\r
482                         debug_shotorg = world;\r
483                 }\r
484                 localcmd("sv_cmd debug_shotorg\n");\r
485         }\r
486         else\r
487         {\r
488                 print("Invalid command. For a list of supported commands, try cl_cmd help.\n");\r
489         }\r
490         \r
491         return;\r
492 }\r
493 \r
494 // CSQC_InputEvent : Used to perform actions based on any key pressed, key released and mouse on the client.\r
495 // Return value should be 1 if CSQC handled the input, otherwise return 0 to have the input passed to the engine.\r
496 // All keys are in ascii.\r
497 // bInputType = 0 is key pressed, 1 is key released, 2 is mouse input.\r
498 // In the case of keyboard input, nPrimary is the ascii code, and nSecondary is 0.\r
499 // In the case of mouse input, nPrimary is xdelta, nSecondary is ydelta.\r
500 float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary)\r
501 {\r
502         local float bSkipKey;\r
503         bSkipKey = false;\r
504         \r
505         if(menu_visible)\r
506                 if(menu_action(bInputType, nPrimary, nSecondary))\r
507                         return TRUE;\r
508         return bSkipKey;\r
509 }\r
510 \r
511 // END REQUIRED CSQC FUNCTIONS\r
512 // --------------------------------------------------------------------------\r
513 \r
514 // --------------------------------------------------------------------------\r
515 // BEGIN OPTIONAL CSQC FUNCTIONS\r
516 void Ent_ReadEntCS()\r
517 {\r
518         InterpolateOrigin_Undo();\r
519 \r
520         self.classname = "entcs_receiver";\r
521         self.sv_entnum = ReadByte() - 1;\r
522         self.origin_x = ReadShort();\r
523         self.origin_y = ReadShort();\r
524         self.origin_z = ReadShort();\r
525         self.angles_y = ReadByte() * 360.0 / 256;\r
526         self.origin_z = self.angles_x = self.angles_z = 0;\r
527 \r
528         InterpolateOrigin_Note();\r
529 }\r
530 \r
531 void Ent_Remove();\r
532 \r
533 void Ent_RemovePlayerScore()\r
534 {\r
535         float i;\r
536 \r
537         if(self.owner)\r
538         {\r
539                 SetTeam(self.owner, -1);\r
540                 self.owner.gotscores = 0;\r
541                 for(i = 0; i < MAX_SCORE; ++i)\r
542                         self.owner.(scores[i]) = 0; // clear all scores\r
543         }\r
544 }\r
545 \r
546 void Ent_ReadPlayerScore()\r
547 {\r
548         float i, n;\r
549         float isNew;\r
550         entity o;\r
551 \r
552         // damnit -.- don't want to go change every single .sv_entnum in sbar.qc AGAIN\r
553         // (no I've never heard of M-x replace-string, sed, or anything like that)\r
554         isNew = !self.owner; // workaround for DP bug\r
555         n = ReadByte()-1;\r
556 \r
557 #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED\r
558         if(!isNew && n != self.sv_entnum)\r
559         {\r
560                 print("A CSQC entity changed its owner!\n");\r
561                 isNew = true;\r
562                 Ent_Remove();\r
563                 self.enttype = ENT_CLIENT_SCORES;\r
564         }\r
565 #endif\r
566 \r
567         self.sv_entnum = n;\r
568 \r
569         if not(playerslots[self.sv_entnum])\r
570                 playerslots[self.sv_entnum] = spawn();\r
571         o = self.owner = playerslots[self.sv_entnum];\r
572         o.sv_entnum = self.sv_entnum;\r
573         o.gotscores = 1;\r
574 \r
575         //if not(o.sort_prev)\r
576         //      RegisterPlayer(o);\r
577         //playerchecker will do this for us later, if it has not already done so\r
578 \r
579 #if MAX_SCORE <= 3\r
580         for(i = 0; i < MAX_SCORE; ++i)\r
581                 o.(scores[i]) = ReadShort();\r
582 #else\r
583         float sf;\r
584 #if MAX_SCORE <= 8\r
585         sf = ReadByte();\r
586 #else\r
587         sf = ReadShort();\r
588 #endif\r
589         float p;\r
590         for(i = 0, p = 1; i < MAX_SCORE; ++i, p *= 2)\r
591                 if(sf & p)\r
592                         o.(scores[i]) = ReadShort();\r
593 #endif\r
594 \r
595         if(o.sort_prev)\r
596                 Sbar_UpdatePlayerPos(o); // if not registered, we cannot do this yet!\r
597         \r
598         self.entremove = Ent_RemovePlayerScore;\r
599 }\r
600 \r
601 void Ent_ReadTeamScore()\r
602 {\r
603         float i;\r
604         entity o;\r
605         \r
606         self.team = ReadByte();\r
607         o = self.owner = GetTeam(self.team, true);\r
608 \r
609 #if MAX_TEAMSCORE <= 3\r
610         for(i = 0; i < MAX_TEAMSCORE; ++i)\r
611                 o.(teamscores[i]) = ReadShort();\r
612 #else\r
613         float sf;\r
614 #if MAX_TEAMSCORE <= 8\r
615         sf = ReadByte();\r
616 #else\r
617         sf = ReadShort();\r
618 #endif\r
619         float p;\r
620         for(i = 0, p = 1; i < MAX_TEAMSCORE; ++i, p *= 2)\r
621                 if(sf & p)\r
622                         o.(teamscores[i]) = ReadShort();\r
623 #endif\r
624 \r
625         Sbar_UpdateTeamPos(o);\r
626 }\r
627 \r
628 void Net_Reset()\r
629 {\r
630 }\r
631 \r
632 void Ent_ClientData()\r
633 {\r
634         float f;\r
635         float newspectatee_status;\r
636 \r
637         f = ReadByte();\r
638 \r
639         sb_showscores_force = (f & 1);\r
640 \r
641         if(f & 2)\r
642         {\r
643                 newspectatee_status = ReadByte();\r
644                 if(newspectatee_status == player_localentnum)\r
645                         newspectatee_status = -1; // observing\r
646         }\r
647         else\r
648                 newspectatee_status = 0;\r
649 \r
650         spectatorbutton_zoom = (f & 4);\r
651 \r
652         if(f & 8)\r
653         {\r
654                 angles_held_status = 1;\r
655                 angles_held_x = ReadAngle();\r
656                 angles_held_y = ReadAngle();\r
657                 angles_held_z = 0;\r
658         }\r
659         \r
660         if(newspectatee_status != spectatee_status)\r
661         {\r
662                 // clear race stuff\r
663                 race_laptime = 0;\r
664                 race_checkpointtime = 0;\r
665         }\r
666         spectatee_status = newspectatee_status;\r
667 }\r
668 \r
669 void Ent_Nagger()\r
670 {\r
671         float nags, i, j, b, f;\r
672 \r
673         nags = ReadByte();\r
674 \r
675         if(nags & 128)\r
676         {\r
677                 if(vote_called_vote)\r
678                         strunzone(vote_called_vote);\r
679                 vote_called_vote = strzone(ColorTranslateRGB(ReadString()));\r
680         }\r
681 \r
682         if(nags & 1)\r
683         {\r
684                 for(j = 0; j < maxclients; ++j)\r
685                         if(playerslots[j])\r
686                                 playerslots[j].ready = 1;\r
687                 for(i = 1; i <= maxclients; i += 8)\r
688                 {\r
689                         f = ReadByte();\r
690                         for(j = i-1, b = 1; b < 256; b *= 2, ++j)\r
691                                 if not(f & b)\r
692                                         if(playerslots[j])\r
693                                                 playerslots[j].ready = 0;\r
694                 }\r
695         }\r
696 \r
697         ready_waiting = (nags & 1);\r
698         ready_waiting_for_me = (nags & 2);\r
699         vote_waiting = (nags & 4);\r
700         vote_waiting_for_me = (nags & 8);\r
701         warmup_stage = (nags & 16);\r
702 }\r
703 \r
704 void Ent_RandomSeed()\r
705 {\r
706         float s;\r
707         prandom_debug();\r
708         s = ReadShort();\r
709         psrandom(s);\r
710 }\r
711 \r
712 // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured.\r
713 // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS.\r
714 void Ent_RadarLink();\r
715 void Ent_Init();\r
716 void Ent_ScoresInfo();\r
717 void(float bIsNewEntity) CSQC_Ent_Update =\r
718 {\r
719         float t;\r
720         float savetime;\r
721         t = ReadByte();\r
722 \r
723         // set up the "time" global for received entities to be correct for interpolation purposes\r
724         savetime = time;\r
725         if(servertime)\r
726                 time = servertime;\r
727 \r
728 #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED\r
729         if(self.enttype)\r
730                 if(t != self.enttype)\r
731                 {\r
732                         print("A CSQC entity changed its type!\n");\r
733                         Ent_Remove();\r
734                 }\r
735 #endif\r
736         self.enttype = t;\r
737         switch(t)\r
738         {\r
739                 case ENT_CLIENT_ENTCS: Ent_ReadEntCS(); break;\r
740                 case ENT_CLIENT_SCORES: Ent_ReadPlayerScore(); break;\r
741                 case ENT_CLIENT_TEAMSCORES: Ent_ReadTeamScore(); break;\r
742                 case ENT_CLIENT_POINTPARTICLES: Ent_PointParticles(); break;\r
743                 case ENT_CLIENT_RAINSNOW: Ent_RainOrSnow(); break;\r
744                 case ENT_CLIENT_LASER: Ent_Laser(); break;\r
745                 case ENT_CLIENT_NAGGER: Ent_Nagger(); break;\r
746                 case ENT_CLIENT_WAYPOINT: Ent_WaypointSprite(); break;\r
747                 case ENT_CLIENT_RADARLINK: Ent_RadarLink(); break;\r
748                 case ENT_CLIENT_PROJECTILE: Ent_Projectile(); break;\r
749                 case ENT_CLIENT_GIBSPLASH: Ent_GibSplash(); break;\r
750                 case ENT_CLIENT_DAMAGEINFO: Ent_DamageInfo(); break;\r
751                 case ENT_CLIENT_CASING: Ent_Casing(); break;\r
752                 case ENT_CLIENT_INIT: Ent_Init(); break;\r
753                 case ENT_CLIENT_SCORES_INFO: Ent_ScoresInfo(); break;\r
754                 case ENT_CLIENT_MAPVOTE: Ent_MapVote(); break;\r
755                 case ENT_CLIENT_CLIENTDATA: Ent_ClientData(); break;\r
756                 case ENT_CLIENT_RANDOMSEED: Ent_RandomSeed(); break;\r
757                 default:\r
758                         error(strcat("unknown entity type in CSQC_Ent_Update: ", ftos(self.enttype), "\n"));\r
759                         break;\r
760         }\r
761         \r
762         time = savetime;\r
763 };\r
764 // Destructor, but does NOT deallocate the entity by calling remove(). Also\r
765 // used when an entity changes its type. For an entity that someone interacts\r
766 // with others, make sure it can no longer do so.\r
767 void Ent_Remove()\r
768 {\r
769         if(self.entremove)\r
770                 self.entremove();\r
771 \r
772         self.enttype = 0;\r
773         self.classname = "";\r
774         self.draw = menu_sub_null;\r
775         self.entremove = menu_sub_null;\r
776         // TODO possibly set more stuff to defaults\r
777 }\r
778 // CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed.  Essentially call remove(self) as well.\r
779 void CSQC_Ent_Remove()\r
780 {\r
781         if(self.enttype)\r
782                 Ent_Remove();\r
783         remove(self);\r
784 }\r
785 \r
786 void Gamemode_Init()\r
787 {\r
788         if(gametype == GAME_ONSLAUGHT) {\r
789                 print(strcat("Using ", minimapname, " as minimap.\n"));\r
790                 precache_pic("gfx/ons-cp-neutral.tga");\r
791                 precache_pic("gfx/ons-cp-red.tga");\r
792                 precache_pic("gfx/ons-cp-blue.tga");\r
793                 precache_pic("gfx/ons-frame.tga");\r
794                 precache_pic("gfx/ons-frame-team.tga");\r
795         } else if(gametype == GAME_KEYHUNT) {\r
796                 precache_pic("gfx/sb_key_carrying");\r
797                 precache_pic("gfx/sb_key_carrying_outline");\r
798         }\r
799 }\r
800 // 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
801 void CSQC_Parse_StuffCmd(string strMessage)\r
802 {\r
803         localcmd(strMessage);\r
804 }\r
805 // 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
806 void CSQC_Parse_Print(string strMessage)\r
807 {\r
808         print(ColorTranslateRGB(strMessage));\r
809 }\r
810 // 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
811 void CSQC_Parse_CenterPrint(string strMessage)\r
812 {\r
813         cprint(ColorTranslateRGB(strMessage));\r
814 }\r
815 \r
816 void CSQC_CheckRevision();\r
817 \r
818 void Fog_Force()\r
819 {\r
820         // TODO somehow thwart prvm_globalset client ...\r
821 \r
822         if(forcefog != "")\r
823                 localcmd(strcat("\nfog ", forcefog, "\nr_fog_exp2 0\nr_drawfog 1\n"));\r
824 }\r
825 \r
826 void Gamemode_Init();\r
827 void Ent_ScoresInfo()\r
828 {\r
829         float i;\r
830         self.classname = "ent_client_scores_info";\r
831         gametype = ReadByte();\r
832         for(i = 0; i < MAX_SCORE; ++i)\r
833         {\r
834                 scores_label[i] = strzone(ReadString());\r
835                 scores_flags[i] = ReadByte();\r
836         }\r
837         for(i = 0; i < MAX_TEAMSCORE; ++i)\r
838         {\r
839                 teamscores_label[i] = strzone(ReadString());\r
840                 teamscores_flags[i] = ReadByte();\r
841         }\r
842         Sbar_InitScores();\r
843         Gamemode_Init();\r
844 }\r
845 \r
846 void Ent_Init()\r
847 {\r
848         float i;\r
849         self.classname = "ent_client_init";\r
850 \r
851         csqc_revision = ReadShort();\r
852 \r
853         print("prev maxclients = ", ftos(maxclients), "\n");\r
854         maxclients = ReadByte();\r
855         print("new maxclients = ", ftos(maxclients), "\n");\r
856         for(i = 0; i < 24; ++i)\r
857                 weaponimpulse[i] = ReadByte() - 1;\r
858         hook_shotorigin_x = ReadCoord();\r
859         hook_shotorigin_y = ReadCoord();\r
860         hook_shotorigin_z = ReadCoord();\r
861 \r
862         if(forcefog)\r
863                 strunzone(forcefog);\r
864         forcefog = strzone(ReadString());\r
865 \r
866         CSQC_CheckRevision();\r
867         if(!postinit)\r
868                 PostInit();\r
869 }\r
870 \r
871 string Net_ReadPicture()\r
872 {\r
873         string img;\r
874         if(csqc_flags & CSQC_FLAG_READPICTURE)\r
875         {\r
876                 img = ReadPicture();\r
877                 print(strcat("Got Picture: ", img, "\n"));\r
878         } else {\r
879                 img = ReadString();\r
880                 print(strcat("^3Warning: ^7Couldn't download ", img, ". This is probably because your engine build is outdated.\n"));\r
881                 float psize, i;\r
882                 psize = ReadShort();\r
883                 // Can I be sure that ReadShort is 2 bytes and ReadLong is 4 bytes?\r
884                 // Because then this could be optimized to first get all 4-byte-groups,\r
885                 // then the remaining 2, then the remaining 1\r
886                 for(i = 0; i < psize; ++i)\r
887                         ReadByte();\r
888         }\r
889         return img;\r
890 }\r
891 \r
892 void Net_ReadRace()\r
893 {\r
894         float b;\r
895 \r
896         b = ReadByte();\r
897 \r
898         switch(b)\r
899         {\r
900                 case RACE_NET_CHECKPOINT_HIT_QUALIFYING:\r
901                         race_checkpoint = ReadByte();\r
902                         race_time = ReadShort();\r
903                         race_previousbesttime = ReadShort();\r
904                         if(race_previousbestname)\r
905                                 strunzone(race_previousbestname);\r
906                         race_previousbestname = strzone(ColorTranslateRGB(ReadString()));\r
907 \r
908                         race_checkpointtime = time;\r
909 \r
910                         if(race_checkpoint == 0)\r
911                                 race_laptime = time; // valid\r
912 \r
913                         break;\r
914 \r
915                 case RACE_NET_CHECKPOINT_CLEAR:\r
916                         race_laptime = 0;\r
917                         race_checkpointtime = 0;\r
918                         break;\r
919 \r
920                 case RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING:\r
921                         race_laptime = ReadCoord();\r
922                         race_checkpointtime = -99999;\r
923                         // fall through\r
924                 case RACE_NET_CHECKPOINT_NEXT_QUALIFYING:\r
925                         race_nextcheckpoint = ReadByte();\r
926 \r
927                         race_nextbesttime = ReadShort();\r
928                         if(race_nextbestname)\r
929                                 strunzone(race_nextbestname);\r
930                         race_nextbestname = strzone(ColorTranslateRGB(ReadString()));\r
931                         break;\r
932 \r
933                 case RACE_NET_CHECKPOINT_HIT_RACE:\r
934                         race_mycheckpoint = ReadByte();\r
935                         race_mycheckpointtime = time;\r
936                         race_mycheckpointdelta = ReadShort();\r
937                         race_mycheckpointlapsdelta = ReadByte();\r
938                         if(race_mycheckpointlapsdelta >= 128)\r
939                                 race_mycheckpointlapsdelta -= 256;\r
940                         if(race_mycheckpointenemy)\r
941                                 strunzone(race_mycheckpointenemy);\r
942                         race_mycheckpointenemy = strzone(ColorTranslateRGB(ReadString()));\r
943                         break;\r
944 \r
945                 case RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT:\r
946                         race_othercheckpoint = ReadByte();\r
947                         race_othercheckpointtime = time;\r
948                         race_othercheckpointdelta = ReadShort();\r
949                         race_othercheckpointlapsdelta = ReadByte();\r
950                         if(race_othercheckpointlapsdelta >= 128)\r
951                                 race_othercheckpointlapsdelta -= 256;\r
952                         if(race_othercheckpointenemy)\r
953                                 strunzone(race_othercheckpointenemy);\r
954                         race_othercheckpointenemy = strzone(ColorTranslateRGB(ReadString()));\r
955                         break;\r
956         }\r
957 }\r
958 \r
959 void Net_ReadSpawn()\r
960 {\r
961         zoomin_effect = 1;\r
962         current_viewzoom = 0.6;\r
963 }\r
964 \r
965 // CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer.\r
966 // You must ALWAYS first acquire the temporary ID, which is sent as a byte.\r
967 // Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event.\r
968 float CSQC_Parse_TempEntity()\r
969 {\r
970         local float bHandled;\r
971                 bHandled  = true;\r
972         // Acquire TE ID\r
973         local float nTEID;\r
974                 nTEID = ReadByte();\r
975 \r
976                 // NOTE: Could just do return instead of break...\r
977         switch(nTEID)\r
978         {\r
979                 case TE_CSQC_PICTURE:\r
980                         Net_MapVote_Picture();\r
981                         bHandled = true;\r
982                         break;\r
983                 case TE_CSQC_RACE:\r
984                         Net_ReadRace();\r
985                         bHandled = true;\r
986                         break;\r
987                 case 13: // TE_BEAM\r
988                         Net_GrapplingHook();\r
989                         bHandled = true;\r
990                         break;\r
991                 case TE_CSQC_SPAWN:\r
992                         Net_ReadSpawn();\r
993                         bHandled = true;\r
994                         break;\r
995                 default:\r
996                         // No special logic for this temporary entity; return 0 so the engine can handle it\r
997                         bHandled = false;\r
998                         break;\r
999         }\r
1000                 \r
1001         return bHandled;\r
1002 }\r
1003 \r
1004 // COMMIT-TODO: Update if necessare, before committing\r
1005 void CSQC_CheckRevision()\r
1006 {\r
1007         if(csqc_revision == CSQC_REVISION)\r
1008         {\r
1009                 print("^2SVQC and CSQC revisions are compatible.\n");\r
1010         } else if(csqc_revision < CSQC_REVISION) {\r
1011                 print("^1Your csprogs.dat (CSQC) version is newer than the one on the server.\n");\r
1012         } else if(csqc_revision > CSQC_REVISION) {\r
1013                 print("^1Your csprogs.dat (CSQC) is too old for this server.\n");\r
1014                 print("^1Please update to a newer version.\n");\r
1015         }\r
1016 }\r
1017 \r
1018 string getcommandkey(string text, string command)\r
1019 {\r
1020         string keys;\r
1021         float n, j, k, l;\r
1022 \r
1023         if (!sbar_showbinds)\r
1024                 return text;\r
1025 \r
1026         keys = db_get(binddb, command);\r
1027         if(csqc_flags & CSQC_FLAG_READPICTURE)\r
1028         {\r
1029                 if (!keys) \r
1030                 {\r
1031                         n = tokenize_insane(findkeysforcommand(command)); // uses '...' strings\r
1032                         for(j = 0; j < n; ++j)\r
1033                         {\r
1034                                 k = stof(argv(j));\r
1035                                 if(k != -1) \r
1036                                 {\r
1037                                         if ("" == keys)\r
1038                                                 keys = keynumtostring(k);\r
1039                                         else\r
1040                                                 keys = strcat(keys, ", ", keynumtostring(k));\r
1041 \r
1042                                         ++l;\r
1043                                         if (sbar_showbinds_limit > 0 && sbar_showbinds_limit >= l) break;\r
1044                                 }\r
1045                         \r
1046                         }\r
1047                         db_put(binddb, command, keys);\r
1048                 }\r
1049         }\r
1050         \r
1051         if ("" == keys) {\r
1052                 if (sbar_showbinds > 1)\r
1053                         return strcat(text, " (not bound)");\r
1054                 else\r
1055                         return text;\r
1056         }\r
1057         else if (sbar_showbinds > 1)\r
1058                 return strcat(text, " (", keys, ")");\r
1059         else\r
1060                 return keys;\r
1061 }\r