]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/client/Main.qc
map previews when voting, g_maplist_textonly for old style
[divverent/nexuiz.git] / data / qcsrc / client / Main.qc
1 // --------------------------------------------------------------------------\r
2 // BEGIN REQUIRED CSQC FUNCTIONS\r
3 //include "main.qh"\r
4 \r
5 void() menu_show_error =\r
6 {\r
7         drawstring('0 200', "ERROR - MENU IS VISIBLE BUT NO MENU WAS DEFINED!", '8 8 0', '1 0 0', 1, 0);\r
8 };\r
9 \r
10 // CSQC_Init : Called every time the CSQC code is initialized (essentially at map load)\r
11 // Useful for precaching things\r
12 \r
13 void() menu_sub_null =\r
14 {\r
15 };\r
16 \r
17 // let's make this a general data buffer...\r
18 float using_gps;\r
19 \r
20 float __engine_check;\r
21 \r
22 void testpassedyay() {\r
23         asm {\r
24                 RETURN 0;\r
25         }\r
26 }\r
27 \r
28 void CSQC_Init(void)\r
29 {\r
30 #if 0\r
31         asm {\r
32                 STORE_F "DP_SV_WRITEPICTURE" PARM0;\r
33                 CALL1 checkextension;\r
34                 STORE_F RETURN __engine_check;\r
35                 IF __engine_check 6;\r
36                 STORE_F "^3Your engine build is outdated\n^3This Server uses a newer QC VM. Please update!\n" PARM0;\r
37                 CALL1 print;\r
38                 STORE_F "\ndisconnect\n" PARM0;\r
39                 CALL1 localcmd;\r
40                 DONE;\r
41         }\r
42 #else\r
43         __engine_check = true;\r
44 #endif\r
45         \r
46         float i;\r
47         CSQC_CheckEngine();\r
48         drawfont = 0;\r
49         menu_visible = FALSE;\r
50         menu_show = menu_show_error;\r
51         menu_action = menu_sub_null;\r
52         using_gps = false;\r
53         //ctf_temp_1 = "";\r
54         localcmd("alias order \"cmd order $*\"");\r
55         //registercmd("ctf_menu");\r
56         registercmd("ons_map");\r
57         //registercmd("menu_action");\r
58         registercmd("sbar_columns_set");\r
59         registercmd("sbar_columns_help");\r
60 \r
61         registercvar("sbar_usecsqc", "1");\r
62         registercvar("sbar_columns", "ping name | caps returns frags deaths", CVAR_SAVE);\r
63 \r
64         gametype = 0;\r
65 \r
66         gps_start = world;\r
67 \r
68         // sbar_fields uses strunzone on the titles!\r
69         for(i = 0; i < MAX_SBAR_FIELDS; ++i)\r
70                 sbar_title[i] = strzone("(null)");\r
71 \r
72         postinit = false;\r
73 \r
74         Sbar_Init();\r
75 }\r
76 \r
77 // CSQC_Shutdown : Called every time the CSQC code is shutdown (changing maps, quitting, etc)\r
78 void CSQC_Shutdown(void)\r
79 {\r
80         asm {\r
81                 IF __engine_check 2;\r
82                 RETURN 0;\r
83         }\r
84         buf_del(databuf);\r
85 }\r
86 \r
87 void PostInit(void)\r
88 {\r
89         float i;\r
90 \r
91         print(strcat("PostInit\n    maxclients = ", ftos(maxclients), "\n"));\r
92         databuf = buf_create();\r
93         for(i = 0; i < maxclients; ++i)\r
94         {\r
95                 bufstr_set(databuf, DATABUF_PING + i, "N/A");\r
96                 bufstr_set(databuf, DATABUF_DEATHS + i, "0");\r
97                 bufstr_set(databuf, DATABUF_CAPTURES + i, "0");\r
98                 bufstr_set(databuf, DATABUF_RETURNS + i, "0");\r
99         }\r
100         \r
101         localcmd(strcat("\nsbar_columns_set ", cvar_string("sbar_columns"), ";\n"));\r
102 \r
103         postinit = true;\r
104 }\r
105 \r
106 // CSQC_ConsoleCommand : Used to parse commands in the console that have been registered with the "registercmd" function\r
107 // Return value should be 1 if CSQC handled the command, otherwise return 0 to have the engine handle it.\r
108 void Cmd_Sbar_SetFields(float);\r
109 void Cmd_Sbar_Help(float);\r
110 float CSQC_ConsoleCommand(string strMessage)\r
111 {\r
112         float argc;\r
113         // Tokenize String\r
114         argc = tokenize(strMessage);\r
115         \r
116         // Acquire Command\r
117         local string strCmd;\r
118         strCmd = argv(0);\r
119 \r
120         /*if(strCmd == "ctf_menu") {\r
121                 ctf_menu_show();\r
122                 nReturn = true;\r
123                 } else*/\r
124         if(strCmd == "ons_map") {\r
125                 Cmd_ons_map();\r
126                 return true;\r
127         } else if(strCmd == "sbar_columns_set") {\r
128                 Cmd_Sbar_SetFields(argc);\r
129                 return true;\r
130         } else if(strCmd == "sbar_columns_help") {\r
131                 Cmd_Sbar_Help(argc);\r
132                 return true;\r
133         }\r
134         \r
135         return false;\r
136 }\r
137 \r
138 float GameCommand(string msg)\r
139 {\r
140         float argc;\r
141         argc = tokenize(msg);\r
142         string cmd;\r
143         cmd = argv(0);\r
144         if(cmd == "mv_download") {\r
145                 Cmd_MapVote_MapDownload(argc);\r
146                 return true;\r
147         }\r
148         \r
149         return false;\r
150 }\r
151 \r
152 // CSQC_InputEvent : Used to perform actions based on any key pressed, key released and mouse on the client.\r
153 // Return value should be 1 if CSQC handled the input, otherwise return 0 to have the input passed to the engine.\r
154 // All keys are in ascii.\r
155 // bInputType = 0 is key pressed, 1 is key released, 2 is mouse input.\r
156 // In the case of keyboard input, nPrimary is the ascii code, and nSecondary is 0.\r
157 // In the case of mouse input, nPrimary is xdelta, nSecondary is ydelta.\r
158 float CSQC_InputEvent(float bInputType, float nPrimary, float nSecondary)\r
159 {\r
160         local float bSkipKey;\r
161         bSkipKey = false;\r
162         if(menu_visible)\r
163                 if(menu_action(bInputType, nPrimary, nSecondary))\r
164                         return TRUE;\r
165         return bSkipKey;\r
166 }\r
167 \r
168 // END REQUIRED CSQC FUNCTIONS\r
169 // --------------------------------------------------------------------------\r
170 \r
171 // --------------------------------------------------------------------------\r
172 // BEGIN OPTIONAL CSQC FUNCTIONS\r
173 void ReadONS()\r
174 {\r
175         entity gps;\r
176         using_gps = true;\r
177 \r
178         self.origin_x = ReadCoord();\r
179         self.origin_y = ReadCoord();\r
180         self.angles_y = ReadCoord();\r
181         self.origin_z = self.angles_x = self.angles_z = 0;\r
182 \r
183         for(gps = gps_start; gps; gps = gps.chain)\r
184         {\r
185                 if(gps == self)\r
186                         break;\r
187         }\r
188         if(!gps)\r
189         {\r
190                 self.chain = gps_start;\r
191                 gps_start = self;\r
192         }\r
193 }\r
194 \r
195 void RemoveONS()\r
196 {\r
197         if(gps_start == self)\r
198                 gps_start = self.chain;\r
199         else\r
200         {\r
201                 local entity ent;\r
202                 ent = gps_start;\r
203                         \r
204                 while(ent.chain != self && ent.chain != world)\r
205                         ent = ent.chain;\r
206                 if(ent.chain == self)\r
207                         ent.chain = self.chain;\r
208         }\r
209 }\r
210 \r
211 // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured.\r
212 // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS.\r
213 void(float bIsNewEntity) CSQC_Ent_Update =\r
214 {\r
215         float msg;\r
216         self.enttype = ReadByte();\r
217         if(self.enttype == ENT_CLIENT_ENTCS)\r
218         {\r
219                 self.sv_entnum = ReadByte()-1;\r
220 \r
221                 for(msg = ReadByte(); msg != ENTCS_MSG_END; msg = ReadByte())\r
222                 {\r
223                         switch(msg)\r
224                         {\r
225                         case ENTCS_MSG_ONS_GPS: ReadONS(); break;\r
226                         case ENTCS_MSG_ONS_REMOVE: RemoveONS(); break;\r
227                         default:\r
228                                 error("unknown ENTCS_MSG type\n");\r
229                         }\r
230                 }\r
231         }\r
232         else\r
233                 error("unknown entity type in CSQC_Ent_Update\n");\r
234         \r
235 };\r
236 // CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed.  Essentially call remove(self) as well.\r
237 void CSQC_Ent_Remove()\r
238 {\r
239         if(self.enttype == ENT_CLIENT_ENTCS)\r
240         {\r
241                 if(using_gps) //gametype == GAME_ONSLAUGHT)\r
242                 {\r
243                         if(gps_start == self)\r
244                                 gps_start = self.chain;\r
245                         else\r
246                         {\r
247                                 local entity ent;\r
248                                 ent = gps_start;\r
249                         \r
250                                 while(ent.chain != self && ent.chain != world)\r
251                                         ent = ent.chain;\r
252                                 if(ent.chain == self)\r
253                                         ent.chain = self.chain;\r
254                         }\r
255                 }\r
256         }\r
257         remove(self);\r
258 }\r
259 \r
260 void Gamemode_Init()\r
261 {\r
262         local string mapinfo, infoline;\r
263         local float len;\r
264         local float file;\r
265         local vector mi_min, mi_max;\r
266 \r
267         gametype = cvar("gametype");\r
268         if(gametype == GAME_ONSLAUGHT) {\r
269                 if(!strcasecmp(substring(mapname, 0, 5), "maps/"))\r
270                         minimapname = substring(mapname, 5, 999);\r
271                 else\r
272                         minimapname = mapname;\r
273                 len = strlen(minimapname);\r
274                 if(!strcasecmp(substring(minimapname, len-4, 4), ".bsp"))\r
275                         minimapname = substring(minimapname, 0, len-4);\r
276                 \r
277                 mapinfo = strcat("maps/", minimapname, ".info");\r
278                 minimapname = strzone(strcat("gfx/", minimapname, "_mini.tga"));\r
279 \r
280                 mi_min = world.mins;\r
281                 mi_max = world.maxs;\r
282                 \r
283                 file = fopen(mapinfo, FILE_READ);\r
284                 if(file >= 0) {\r
285                         while((infoline = fgets(file))) {\r
286                                 if(!strncasecmp(infoline, "mins", 4)) {\r
287                                         mi_min = stov(substring(infoline, 5, 999));\r
288                                 } else if(!strncasecmp(infoline, "maxs", 4)) {\r
289                                         mi_max = stov(substring(infoline, 5, 999));\r
290                                 } else if(strncasecmp(infoline, "//", 2)) { // don't print comment-style lines\r
291                                         print(strcat("mapinfo: ", infoline, "\n"));\r
292                                 }\r
293                         }\r
294                 } else {\r
295                         print(strcat("Map has no .info file (", mapinfo, ").\n"));\r
296                 }\r
297                 fclose(file);\r
298 \r
299                 print(strcat("Mins: ", vtos(mi_min), "    Maxs: ", vtos(mi_max), "\n"));\r
300                 \r
301                 mi_center = (mi_min + mi_max) * 0.5;\r
302                 mi_scale = mi_max - mi_min;\r
303                 \r
304                 \r
305                 print(strcat("Using ", minimapname, " as minimap.\n"));\r
306                 precache_pic(minimapname);\r
307                 precache_pic("gfx/ons-cp-neutral.tga");\r
308                 precache_pic("gfx/ons-cp-red.tga");\r
309                 precache_pic("gfx/ons-cp-blue.tga");\r
310                 precache_pic("gfx/ons-frame.tga");\r
311                 precache_pic("gfx/ons-frame-team.tga");\r
312         } else if(gametype == GAME_KEYHUNT) {\r
313                 precache_pic("gfx/sb_key_carrying");\r
314                 precache_pic("gfx/sb_key_carrying_outline");\r
315         }\r
316 }\r
317 // 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
318 void CSQC_Parse_StuffCmd(string strMessage)\r
319 {\r
320         localcmd(strMessage);\r
321         // watch for gametype changes!\r
322         if(gametype != cvar("gametype"))\r
323         {\r
324                 Gamemode_Init();\r
325         }\r
326 }\r
327 // 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
328 void CSQC_Parse_Print(string strMessage)\r
329 {\r
330         print(strMessage);\r
331 }\r
332 // 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
333 void CSQC_Parse_CenterPrint(string strMessage)\r
334 {\r
335         cprint(strMessage);\r
336 }\r
337 \r
338 void CSQC_CheckRevision();\r
339 \r
340 void Net_ReadInit()\r
341 {\r
342         csqc_revision = ReadShort();\r
343         maxclients = ReadByte();\r
344 \r
345         CSQC_CheckRevision();\r
346 }\r
347 \r
348 void Net_ReadPings()\r
349 {\r
350         float plnum, ping;\r
351         for(plnum = ReadByte(); plnum != 0; plnum = ReadByte())\r
352         {\r
353                 ping = ReadShort();\r
354                 bufstr_set(databuf, DATABUF_PING + plnum-1, ftos(ping));\r
355         }\r
356 }\r
357 \r
358 void Net_ReadCaptures()\r
359 {\r
360         float plnum, caps, mode;\r
361         mode = ReadByte();\r
362         caps_team1 = ReadByte();\r
363         caps_team2 = ReadByte();\r
364         for(plnum = ReadByte(); plnum != 0; plnum = ReadByte())\r
365         {\r
366                 caps = ReadByte();\r
367                 bufstr_set(databuf, DATABUF_CAPTURES + plnum-1, ftos(caps));\r
368         }\r
369 }\r
370 \r
371 void Net_ReadDatabuf(float ofs)\r
372 {\r
373         float plnum, data;\r
374         for(plnum = ReadByte(); plnum != 0; plnum = ReadByte())\r
375         {\r
376                 data = ReadByte();\r
377                 bufstr_set(databuf, ofs + plnum-1, ftos(data));\r
378         }\r
379 }\r
380 \r
381 void Net_ReadPicture()\r
382 {\r
383         string img;\r
384         if(csqc_flags & CSQC_FLAG_READPICTURE)\r
385         {\r
386                 img = ReadPicture();\r
387                 print(strcat("Got Picture: ", img, "\n"));\r
388         } else {\r
389                 img = ReadString();\r
390                 print(strcat("^3Warning: ^7Couldn't download ", img, ". This is probably because your engine build is outdated.\n"));\r
391                 float psize, i;\r
392                 psize = ReadShort();\r
393                 // Can I be sure that ReadShort is 2 bytes and ReadLong is 4 bytes?\r
394                 // Because then this could be optimized to first get all 4-byte-groups,\r
395                 // then the remaining 2, then the remaining 1\r
396                 for(i = 0; i < psize; ++i)\r
397                         ReadByte();\r
398         }\r
399 }\r
400 \r
401 // CSQC_Parse_TempEntity : Handles all temporary entity network data in the CSQC layer.\r
402 // You must ALWAYS first acquire the temporary ID, which is sent as a byte.\r
403 // Return value should be 1 if CSQC handled the temporary entity, otherwise return 0 to have the engine process the event.\r
404 float CSQC_Parse_TempEntity()\r
405 {\r
406         local float bHandled;\r
407                 bHandled  = true;\r
408         // Acquire TE ID\r
409         local float nTEID;\r
410                 nTEID = ReadByte();\r
411 \r
412                 // NOTE: Could just do return instead of break...\r
413         switch(nTEID)\r
414         {\r
415                 case TE_CSQC_INIT:\r
416                         Net_ReadInit();\r
417                         bHandled = true;\r
418                         break;\r
419                 case TE_CSQC_PING:\r
420                         Net_ReadPings();\r
421                         bHandled = true;\r
422                         break;\r
423                 case TE_CSQC_CAPTURES:\r
424                         Net_ReadCaptures();\r
425                         bHandled = true;\r
426                         break;\r
427                 case TE_CSQC_RETURNS:\r
428                         Net_ReadDatabuf(DATABUF_RETURNS);\r
429                         bHandled = true;\r
430                         break;\r
431                 case TE_CSQC_DEATHS:\r
432                         Net_ReadDatabuf(DATABUF_DEATHS);\r
433                         bHandled = true;\r
434                         break;\r
435                 case TE_CSQC_PICTURE:\r
436                         Net_ReadPicture();\r
437                         bHandled = true;\r
438                         break;\r
439                 case TE_CSQC_MAPVOTE:\r
440                         Net_Mapvote();\r
441                         bHandled = true;\r
442                         break;\r
443                 \r
444                 default:\r
445                         // No special logic for this temporary entity; return 0 so the engine can handle it\r
446                         bHandled = false;\r
447                         break;\r
448         }\r
449         \r
450         if(!postinit)\r
451                 PostInit();\r
452                 \r
453         return bHandled;\r
454 }\r
455 \r
456 // COMMIT-TODO: Update if necessare, before committing\r
457 float csqc_svn_map[CSQC_REVISION] =\r
458 {\r
459         3812, // 3795,\r
460         3815\r
461 };\r
462 \r
463 // COMMIT-TODO: Update if necessare, before committing\r
464 void CSQC_CheckRevision()\r
465 {\r
466         if(csqc_revision == CSQC_REVISION)\r
467         {\r
468                 print("^2SVQC and CSQC revisions are compatible.\n");\r
469         } else if(csqc_revision < CSQC_REVISION) {\r
470                 print("^1Your csprogs.dat (CSQC) version is newer than the one on the server.\n");\r
471                 print("^1The last known svn revision for the server's CSQC is: ^7");\r
472                 print(ftos(csqc_svn_map[csqc_revision])); // don't use strcat, fteqcc loves screwing up arrays...\r
473                 print("\n");\r
474         } else if(csqc_revision > CSQC_REVISION) {\r
475                 print("^1Your csprogs.dat (CSQC) is too old for this server.\n");\r
476                 print("^1Please update to a newer version.\n");\r
477         }\r
478 }\r