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