]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/g_world.qc
New and pointless mutator: g_running_guns :P
[divverent/nexuiz.git] / data / qcsrc / server / g_world.qc
1 string GetMapname();
2 void GotoNextMap();
3
4 void SetDefaultAlpha()
5 {
6         if(cvar("g_running_guns"))
7         {
8                 default_player_alpha = -1;
9                 default_weapon_alpha = +1;
10         }
11         else if(cvar("g_cloaked"))
12         {
13                 default_player_alpha = cvar("g_balance_cloaked_alpha");
14                 default_weapon_alpha = default_player_alpha;
15         }
16         else
17         {
18                 default_player_alpha = cvar("g_player_alpha");
19                 if(default_player_alpha <= 0)
20                         default_player_alpha = 1;
21                 default_weapon_alpha = default_player_alpha;
22         }
23 }
24
25 void fteqcc_testbugs()
26 {
27         float a, b;
28
29         if(!cvar("developer_fteqccbugs"))
30                 return;
31
32         dprint("*** fteqcc test: checking for bugs...\n");
33
34         a = 1;
35         b = 5;
36         if(sqrt(a) - sqrt(b - a) == 0)
37                 dprint("*** fteqcc test: found same-function-twice bug\n");
38         else
39                 dprint("*** fteqcc test: same-function-twice bug got FINALLY FIXED! HOORAY!\n");
40
41         world.frags = -10;
42         world.enemy = world;
43         world.enemy.frags += 10;
44         if(world.frags > 0.2 || world.frags < -0.2) // don't error out if it's just roundoff errors
45                 dprint("*** fteqcc test: found += bug\n");
46         else
47                 dprint("*** fteqcc test: += bug got FINALLY FIXED! HOORAY!\n");
48         world.frags = 0;
49 }
50
51 void GotoFirstMap()
52 {
53         if(cvar("_sv_init"))
54         {
55                 cvar_set("_sv_init", "0");
56                 tokenize(cvar_string("g_maplist"));
57                 if(argv(0) != GetMapname())
58                 {
59                         cvar_set("nextmap", argv(0));
60                         GotoNextMap();
61                 }
62         }
63 }
64
65 void worldspawn (void)
66 {
67
68         // Precache all player models
69         // Workaround for "invisible players"
70         precache_model("models/player/carni.zym");
71         precache_model("models/player/crash.zym");
72         precache_model("models/player/grunt.zym");
73         precache_model("models/player/headhunter.zym");
74         precache_model("models/player/insurrectionist.zym");
75         precache_model("models/player/jeandarc.zym");
76         precache_model("models/player/lurk.zym");
77         precache_model("models/player/lycanthrope.zym");
78         precache_model("models/player/marine.zym");
79         precache_model("models/player/nexus.zym");
80         precache_model("models/player/pyria.zym");
81         precache_model("models/player/shock.zym");
82         precache_model("models/player/skadi.zym");
83         precache_model("models/player/specop.zym");
84         precache_model("models/player/visitant.zym");
85
86         //precache_model ("progs/beam.mdl");
87         precache_model ("models/bullet.mdl");
88         precache_model ("models/casing_bronze.mdl");
89         precache_model ("models/casing_shell.mdl");
90         precache_model ("models/casing_steel.mdl");
91         precache_model ("models/ebomb.mdl");
92         precache_model ("models/elaser.mdl");
93         precache_model ("models/flash.md3");
94         precache_model ("models/gibs/bloodyskull.md3");
95         precache_model ("models/gibs/chunk.mdl");
96         precache_model ("models/gibs/eye.md3");
97         precache_model ("models/gibs/gib1.md3");
98         //precache_model ("models/gibs/gib2.md3");
99         //precache_model ("models/gibs/gib3.md3");
100         //precache_model ("models/gibs/gib4.md3");
101         precache_model ("models/gibs/gib5.md3");
102         //precache_model ("models/gibs/gib6.md3");
103         precache_model ("models/gibs/gib1.mdl");
104         precache_model ("models/gibs/gib2.mdl");
105         precache_model ("models/gibs/gib3.mdl");
106         precache_model ("models/grenademodel.md3");
107         precache_model ("models/hagarmissile.mdl");
108         precache_model ("models/items/a_bullets.mdl");
109         precache_model ("models/items/a_cells.md3");
110         precache_model ("models/items/a_rockets.md3");
111         precache_model ("models/items/a_shells.md3");
112         precache_model ("models/items/g_a1.md3");
113         precache_model ("models/items/g_a25.md3");
114         precache_model ("models/items/g_h1.md3");
115         precache_model ("models/items/g_h25.md3");
116         precache_model ("models/items/g_h100.md3");
117         precache_model ("models/items/g_invincible.md3");
118         precache_model ("models/items/g_strength.md3");
119         precache_model ("models/laser.mdl");
120         precache_model ("models/misc/chatbubble.spr");
121         precache_model ("models/misc/teambubble.spr");
122         precache_model ("models/nexflash.md3");
123         precache_model ("models/plasma.mdl");
124         precache_model ("models/plasmatrail.mdl");
125         precache_model ("models/rocket.md3");
126         //precache_model ("models/sprites/grenexpl.spr");
127         precache_model ("models/runematch/rune.mdl");
128         precache_model ("models/runematch/curse.mdl");
129         //precache_model ("models/sprites/hagar.spr");
130         //precache_model ("models/sprites/muzzleflash.spr32");
131         //precache_model ("models/sprites/electrocombo.spr32");
132         //precache_model ("models/sprites/plasmahitwall.spr32");
133         //precache_model ("models/sprites/plasmashot.spr32");
134         //precache_model ("models/sprites/rockexpl.spr");
135         precache_model ("models/tracer.mdl");
136         precache_model ("models/uziflash.md3");
137         precache_model ("models/weapons/g_crylink.md3");
138         precache_model ("models/weapons/g_electro.md3");
139         precache_model ("models/weapons/g_gl.md3");
140         precache_model ("models/weapons/g_hagar.md3");
141         precache_model ("models/weapons/g_nex.md3");
142         precache_model ("models/weapons/g_rl.md3");
143         precache_model ("models/weapons/g_shotgun.md3");
144         precache_model ("models/weapons/g_uzi.md3");
145         precache_model ("models/weapons/v_crylink.md3");
146         precache_model ("models/weapons/v_electro.md3");
147         precache_model ("models/weapons/v_gl.md3");
148         precache_model ("models/weapons/v_hagar.md3");
149         precache_model ("models/weapons/v_laser.md3");
150         precache_model ("models/weapons/v_nex.md3");
151         precache_model ("models/weapons/v_rl.md3");
152         precache_model ("models/weapons/v_shotgun.md3");
153         precache_model ("models/weapons/v_uzi.md3");
154         precache_model ("models/weapons/w_crylink.zym");
155         precache_model ("models/weapons/w_electro.zym");
156         precache_model ("models/weapons/w_gl.zym");
157         precache_model ("models/weapons/w_hagar.zym");
158         precache_model ("models/weapons/w_laser.zym");
159         precache_model ("models/weapons/w_nex.zym");
160         precache_model ("models/weapons/w_rl.zym");
161         precache_model ("models/weapons/w_shotgun.zym");
162         precache_model ("models/weapons/w_uzi.zym");
163
164         // laser for laser-guided weapons
165         precache_model ("models/laser_dot.mdl");
166
167         precache_sound ("misc/null.wav");
168         precache_sound ("misc/armor1.wav");
169         precache_sound ("misc/armor25.wav");
170         precache_sound ("misc/armorimpact.wav");
171         precache_sound ("misc/bodyimpact1.wav");
172         precache_sound ("misc/bodyimpact2.wav");
173         precache_sound ("misc/gib.wav");
174         precache_sound ("misc/gib_splat01.wav");
175         precache_sound ("misc/gib_splat02.wav");
176         precache_sound ("misc/gib_splat03.wav");
177         precache_sound ("misc/gib_splat04.wav");
178         //precache_sound ("misc/h2ohit.wav");
179         precache_sound ("misc/hit.wav");
180         precache_sound ("misc/footstep01.wav");
181         precache_sound ("misc/footstep02.wav");
182         precache_sound ("misc/footstep03.wav");
183         precache_sound ("misc/footstep04.wav");
184         precache_sound ("misc/footstep05.wav");
185         precache_sound ("misc/footstep06.wav");
186         precache_sound ("misc/hitground1.ogg");
187         precache_sound ("misc/hitground2.ogg");
188         precache_sound ("misc/hitground3.ogg");
189         precache_sound ("misc/hitground4.ogg");
190         precache_sound ("misc/itempickup.ogg");
191         precache_sound ("misc/itemrespawn.ogg");
192         precache_sound ("misc/jumppad.ogg");
193         precache_sound ("misc/mediumhealth.ogg");
194         precache_sound ("misc/megahealth.ogg");
195         precache_sound ("misc/minihealth.ogg");
196         precache_sound ("misc/powerup.ogg");
197         precache_sound ("misc/powerup_shield.ogg");
198         precache_sound ("misc/talk.wav");
199         precache_sound ("misc/teleport.ogg");
200         precache_sound ("plats/medplat1.wav");
201         precache_sound ("plats/medplat2.wav");
202         precache_sound ("player/lava.wav");
203         precache_sound ("player/slime.wav");
204         precache_sound ("weapons/crylink_fire.ogg");
205         precache_sound ("weapons/electro_bounce.ogg");
206         precache_sound ("weapons/electro_fire.ogg");
207         precache_sound ("weapons/electro_fire2.ogg");
208         precache_sound ("weapons/electro_fly.wav");
209         precache_sound ("weapons/electro_impact.ogg");
210         precache_sound ("weapons/electro_impact_combo.ogg");
211         //precache_sound ("weapons/grenade_bounce.ogg");
212         precache_sound ("weapons/grenade_bounce1.ogg");
213         precache_sound ("weapons/grenade_bounce2.ogg");
214         precache_sound ("weapons/grenade_bounce3.ogg");
215         precache_sound ("weapons/grenade_fire.ogg");
216         precache_sound ("weapons/grenade_impact.ogg");
217         precache_sound ("weapons/hagar_fire.ogg");
218         precache_sound ("weapons/hagexp1.ogg");
219         precache_sound ("weapons/hagexp2.ogg");
220         precache_sound ("weapons/hagexp3.ogg");
221         precache_sound ("weapons/hook_fire.ogg");
222         precache_sound ("weapons/hook_impact.ogg");
223         precache_sound ("weapons/lasergun_fire.ogg");
224         precache_sound ("weapons/laserimpact.ogg");
225         precache_sound ("weapons/nexfire.ogg");
226         precache_sound ("weapons/neximpact.ogg");
227         precache_sound ("weapons/ric1.ogg");
228         precache_sound ("weapons/ric2.ogg");
229         precache_sound ("weapons/ric3.ogg");
230         precache_sound ("weapons/rocket_fire.ogg");
231         precache_sound ("weapons/rocket_fly.wav");
232         precache_sound ("weapons/rocket_impact.ogg");
233         precache_sound ("weapons/rocket_det.ogg");
234         precache_sound ("weapons/shotgun_fire.ogg");
235         precache_sound ("weapons/tink1.ogg");
236         precache_sound ("weapons/uzi_fire.ogg");
237         precache_sound ("weapons/weapon_switch.ogg");
238         precache_sound ("weapons/weaponpickup.ogg");
239         precache_sound ("weapons/strength_fire.ogg");
240
241         //precache_sound ("announce/male/kill10.ogg");
242         //precache_sound ("announce/male/kill15.ogg");
243         //precache_sound ("announce/male/kill20.ogg");
244         //precache_sound ("announce/male/kill25.ogg");
245         //precache_sound ("announce/male/kill3.ogg");
246         //precache_sound ("announce/male/kill30.ogg");
247         //precache_sound ("announce/male/kill4.ogg");
248         //precache_sound ("announce/male/kill5.ogg");
249         //precache_sound ("announce/male/kill6.ogg");
250         //precache_sound ("announce/male/mapkill1.ogg");
251         //precache_sound ("announce/robotic/last_second_save.ogg");
252         //precache_sound ("announce/robotic/narrowly_averted.ogg");
253         //precache_sound ("minstagib/mockery.ogg");
254
255         // announcer sounds - male
256         precache_sound ("announcer/male/03kills.ogg");
257         precache_sound ("announcer/male/05kills.ogg");
258         precache_sound ("announcer/male/10kills.ogg");
259         precache_sound ("announcer/male/15kills.ogg");
260         precache_sound ("announcer/male/20kills.ogg");
261         precache_sound ("announcer/male/25kills.ogg");
262         precache_sound ("announcer/male/30kills.ogg");
263         precache_sound ("announcer/male/botlike.ogg");
264         precache_sound ("announcer/male/electrobitch.ogg");
265         precache_sound ("announcer/male/welcome.ogg");
266         precache_sound ("announcer/male/yoda.ogg");
267
268         // announcer sounds - robotic
269         precache_sound ("announcer/robotic/1fragleft.ogg");
270         precache_sound ("announcer/robotic/1minuteremains.ogg");
271         precache_sound ("announcer/robotic/2fragsleft.ogg");
272         precache_sound ("announcer/robotic/3fragsleft.ogg");
273         precache_sound ("announcer/robotic/lastsecond.ogg");
274         precache_sound ("announcer/robotic/narrowly.ogg");
275
276         // plays music for the level if there is any
277         if (self.noise)
278         {
279                 precache_sound (self.noise);
280                 ambientsound ('0 0 0', self.noise, 1.00, ATTN_NONE);
281         }
282
283                 // 0 normal
284         lightstyle(0, "m");
285
286         // 1 FLICKER (first variety)
287         lightstyle(1, "mmnmmommommnonmmonqnmmo");
288
289         // 2 SLOW STRONG PULSE
290         lightstyle(2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba");
291
292         // 3 CANDLE (first variety)
293         lightstyle(3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg");
294
295         // 4 FAST STROBE
296         lightstyle(4, "mamamamamama");
297
298         // 5 GENTLE PULSE 1
299         lightstyle(5,"jklmnopqrstuvwxyzyxwvutsrqponmlkj");
300
301         // 6 FLICKER (second variety)
302         lightstyle(6, "nmonqnmomnmomomno");
303
304         // 7 CANDLE (second variety)
305         lightstyle(7, "mmmaaaabcdefgmmmmaaaammmaamm");
306
307         // 8 CANDLE (third variety)
308         lightstyle(8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa");
309
310         // 9 SLOW STROBE (fourth variety)
311         lightstyle(9, "aaaaaaaazzzzzzzz");
312
313         // 10 FLUORESCENT FLICKER
314         lightstyle(10, "mmamammmmammamamaaamammma");
315
316         // 11 SLOW PULSE NOT FADE TO BLACK
317         lightstyle(11, "abcdefghijklmnopqrrqponmlkjihgfedcba");
318
319         // styles 32-62 are assigned by the light program for switchable lights
320
321         // 63 testing
322         lightstyle(63, "a");
323
324         player_count = 0;
325         lms_dead_count = 0;
326         lms_lowest_lives = 0;
327         lms_next_place = 0;
328
329         GotoFirstMap();
330
331         if(cvar("g_campaign"))
332                 CampaignPreInit();
333
334         InitGameplayMode();
335         //if (cvar("g_domination"))
336         //      dom_init();
337
338         local entity head;
339         head = nextent(world);
340         maxclients = 0;
341         while(head)
342         {
343                 maxclients++;
344                 head = nextent(head);
345         }
346
347         GameLogInit(); // prepare everything
348         if(cvar("sv_eventlog"))
349         {
350                 local string s;
351                 GameLogEcho(":logversion:2", FALSE);
352                 s = strcat(cvar_string("sv_eventlog_files_counter"), ".");
353                 s = strcat(s, ftos(random()));
354                 GameLogEcho(strcat(":gamestart:", GetMapname(), ":", s), FALSE);
355                 s = ":gameinfo:mutators:LIST";
356                 if(cvar("g_grappling_hook"))
357                         s = strcat(s, ":grappling_hook");
358                 if(!cvar("g_use_ammunition"))
359                         s = strcat(s, ":no_use_ammunition");
360                 if(!cvar("g_pickup_items"))
361                         s = strcat(s, ":no_pickup_items");
362                 if(cvar("g_instagib"))
363                         s = strcat(s, ":instagib");
364                 if(cvar("g_rocketarena"))
365                         s = strcat(s, ":rockerarena");
366                 if(cvar("g_nixnex"))
367                         s = strcat(s, ":nixnex");
368                 if(cvar("g_vampire"))
369                         s = strcat(s, ":vampire");
370                 if(cvar("g_laserguided_missile"))
371                         s = strcat(s, ":laserguided_missile");
372                 if(cvar("g_norecoil"))
373                         s = strcat(s, ":norecoil");
374                 if(cvar("g_midair"))
375                         s = strcat(s, ":midair");
376                 if(cvar("g_minstagib"))
377                         s = strcat(s, ":minstagib");
378                 GameLogEcho(s, FALSE);
379                 GameLogEcho(":gameinfo:end", FALSE);
380         }
381
382         cvar_set("nextmap", "");
383
384         SetDefaultAlpha();
385
386         if(cvar("g_campaign"))
387                 CampaignPostInit();
388
389         fteqcc_testbugs();
390 }
391
392 void light (void)
393 {
394         makestatic (self);
395 }
396
397 float( string pFilename ) TryFile =
398 {
399         local float lHandle;
400         dprint("TryFile(\"", pFilename, "\")\n");
401         lHandle = fopen( pFilename, FILE_READ );
402         if( lHandle != -1 ) {
403                 fclose( lHandle );
404                 return TRUE;
405         } else {
406                 return FALSE;
407         }
408 };
409
410 string GetMapname()
411 {
412         if (game == GAME_DEATHMATCH)
413                 return strcat("dm_", mapname);
414         else if (game == GAME_TEAM_DEATHMATCH)
415                 return strcat("tdm_", mapname);
416         else if (game == GAME_DOMINATION)
417                 return strcat("dom_", mapname);
418         else if (game == GAME_CTF)
419                 return strcat("ctf_", mapname);
420         else if (game == GAME_RUNEMATCH)
421                 return strcat("rune_", mapname);
422         else if (game == GAME_LMS)
423                 return strcat("lms_", mapname);
424         return strcat("dm_", mapname);
425 }
426
427 float GetMaplistPosition()
428 {
429         float pos, f;
430         string map, s;
431
432         map = strzone(GetMapname());
433         pos = 0;
434         f = tokenize(cvar_string("g_maplist"));
435
436         while(pos < f)
437         {
438                 s = strzone(argv(pos));
439                 if(s == map)
440                 {
441                         strunzone(s);
442                         strunzone(map);
443                         return pos;
444                 }
445                 strunzone(s);
446                 pos++;
447         }
448
449         strunzone(map);
450         // resume normal maplist rotation if current map is not in g_maplist
451         return cvar("g_maplist_index");
452 }
453
454 float MapHasRightSize(string map)
455 {
456         // open map size restriction file
457         float fh;
458         dprint("opensize "); dprint(map);
459         fh = fopen(strcat("maps/", map, ".sizes"), FILE_READ);
460         if(fh >= 0)
461         {
462                 float mapmin, mapmax;
463                 dprint(": ok, ");
464                 mapmin = stof(fgets(fh));
465                 mapmax = stof(fgets(fh));
466                 fclose(fh);
467                 if(player_count < mapmin)
468                 {
469                         dprint("not enough\n");
470                         return FALSE;
471                 }
472                 if(player_count > mapmax)
473                 {
474                         dprint("too many\n");
475                         return FALSE;
476                 }
477                 dprint("right size\n");
478                 return TRUE;
479         }
480         dprint(": not found\n");
481         return TRUE;
482 }
483
484 void() GotoNextMap =
485 {
486         //local string nextmap;
487         //local float n, nummaps;
488         //local string s;
489         string exit_cfg;
490         if (alreadychangedlevel)
491                 return;
492         alreadychangedlevel = TRUE;
493
494         if(cvar("g_campaign"))
495         {
496                 CampaignPostIntermission();
497                 return;
498         }
499
500         if (cvar("samelevel")) // if samelevel is set, stay on same level
501         {
502                 // this does not work because it tries to exec maps/nexdm01.mapcfg (which doesn't exist, it should be trying maps/dm_nexdm01.mapcfg for example)
503                 //localcmd(strcat("exec \"maps/", mapname, ".mapcfg\"\n"));
504                 // so instead just restart the current map using the restart command (DOES NOT WORK PROPERLY WITH exit_cfg STUFF)
505                 localcmd("restart\n");
506                 //changelevel (mapname);
507                 return;
508         }
509
510         // if an exit cfg is defined by exiting map, exec it.
511         exit_cfg = cvar_string("exit_cfg");
512         if(exit_cfg != "")
513                 localcmd(strcat("exec \"", exit_cfg, "\"\n"));
514
515         ResetGameCvars();
516
517
518         if (cvar("lastlevel"))
519         {
520                 localcmd(strcat("set lastlevel 0\n"));
521                 localcmd(strcat("togglemenu\n"));
522         }
523         else
524         {
525                 // method 0
526                 local float lCurrent;
527                 local float lSize;
528                 local float lOldCurrent;
529                 local float lBeforeCurrent;
530                 local float pass;
531                 local float found_but_wrong_size;
532                 found_but_wrong_size = FALSE;
533
534                 if(TryFile(strcat("maps/", cvar_string("nextmap"), ".mapcfg")))
535                 {
536                         localcmd(strcat("exec \"maps/", cvar_string("nextmap"), ".mapcfg\"\n"));
537                         return;
538                 }
539
540                 pass = 0;
541                 while (pass < 2)
542                 {
543                         pass = pass + 1;
544                         local string temp;
545                         temp = cvar_string( "g_maplist" );
546                         if(temp == "")
547                         {
548                                 bprint( "Maplist is empty!  Resetting it to default map list.\n" );
549                                 cvar_set("g_maplist", cvar_string("g_maplist_defaultlist"));
550                                 temp = cvar_string( "g_maplist" );
551                         }
552                         temp = strzone(temp);
553                         dprint("g_maplist is ", temp, "\n");
554                         lSize = tokenize( temp );
555                         lCurrent = GetMaplistPosition();
556                         lCurrent = max(0, min(floor(lCurrent), lSize - 1));
557                         lOldCurrent = lCurrent;
558                         dprint(ftos(lOldCurrent), " / ", ftos(lSize), " (start)\n");
559
560                         // if we want a random map selection...
561                         if(cvar("g_maplist_selectrandom") && lSize > 1)
562                         {
563                                 lBeforeCurrent = lCurrent - 1;
564                                 if(lBeforeCurrent < 0)
565                                         lBeforeCurrent = lSize - 1;
566                                 lCurrent = ceil(random() * (lSize - 1)) - 1; // random in 0..lsize-2
567                                 if(lCurrent >= lBeforeCurrent)
568                                         lCurrent += 1;
569                                 // choose any map except for the current one
570                         }
571
572                         while( 1 ) {
573                                 local string lFilename;
574
575                                 lCurrent = lCurrent + 1;
576                                 dprint(ftos(lCurrent), " / ", ftos(lSize), "\n");
577                                 if( lCurrent >= lSize ) {
578                                         lCurrent = 0;
579                                 }
580                                 if( lOldCurrent == lCurrent ) {
581                                         // we couldn't find a valid map at all
582                                         if (pass == 1)
583                                         {
584                                                 if(!found_but_wrong_size)
585                                                 {
586                                                         bprint( "Maplist is bad/messed up. Not one good mapcfg can be found in it!  Resetting it to default map list.\n" );
587                                                         cvar_set("g_maplist", cvar_string("g_maplist_defaultlist"));
588                                                         // let the loop restart with the default list now
589                                                 }
590                                                 else
591                                                 {
592                                                         dprint("wrong size, now trying all\n");
593                                                 }
594                                         }
595                                         else
596                                         {
597                                                 bprint( "Both g_maplist and g_maplist_defaultlist are messed up!  Complain to developers!\n" );
598                                                 localcmd( "disconnect\n" );
599                                         }
600                                         break;
601                                 }
602
603                                 lFilename = strzone(strcat( "maps/", argv( lCurrent ), ".mapcfg" ));
604                                 if( TryFile( lFilename ) ) {
605                                         if((pass == 2) || MapHasRightSize(argv(lCurrent)))
606                                         {
607                                                 cvar_set( "g_maplist_index", ftos( lCurrent ) );
608                                                 localcmd(strcat("exec \"", lFilename ,"\"\n"));
609                                                 strunzone(lFilename);
610                                                 pass = 2; // exit the outer loop
611                                                 break;
612                                         }
613                                         else
614                                                 found_but_wrong_size = TRUE;
615                                 } else {
616                                         dprint( "Couldn't find '", lFilename, "'..\n" );
617                                 }
618                                 strunzone(lFilename);
619                                 //changelevel( argv( lCurrent ) );
620                         }
621                         strunzone(temp);
622                 }
623
624                 /*
625                 // method 1
626
627                 //local entity pos;
628                 local float fh;
629                 local string line;
630
631                 // restart current map if no cycle is found
632                 nextmap = strzone(mapname);
633                 fh = fopen("maplist.cfg", FILE_READ);
634                 if (fh >= 0)
635                 {
636                         while (1)
637                         {
638                                 line = fgets(fh);
639                                 if (nextmap == mapname)
640                                 {
641                                         strunzone(nextmap);
642                                         nextmap = strzone(line);
643                                 }
644                                 if (!line)
645                                         break;
646                                 if (line == mapname)
647                                 {
648                                         line = fgets(fh);
649                                         if (!line)
650                                                 break;
651                                         strunzone(nextmap);
652                                         nextmap = strzone(line);
653                                         break;
654                                 }
655                         }
656                         fclose(fh);
657                 }
658                 changelevel (nextmap);
659                 strunzone(nextmap);*/
660
661                 // method 3
662                 /*
663                 s = cvar_string("g_maplist");
664                 nummaps = tokenize(s);
665                 // if no map list, restart current one
666                 nextmap = mapname;
667                 if (nummaps >= 1)
668                 {
669                         n = 0;
670                         while (n < nummaps)
671                         {
672                                 if (argv(n) == mapname)
673                                         break;
674                                 n = n + 1;
675                         }
676                         n = n + 1;
677                         if (n >= nummaps)
678                                 n = 0;
679                         nextmap = argv(n);
680                 }
681                 changelevel (nextmap);
682                 */
683         }
684 };
685
686
687 /*
688 ============
689 IntermissionThink
690
691 When the player presses attack or jump, change to the next level
692 ============
693 */
694 void() IntermissionThink =
695 {
696         if (time < intermission_exittime)
697                 return;
698
699         if (time < intermission_exittime + 10 && !self.button0 && !self.button1 && !self.button2 && !self.button3)
700                 return;
701
702         GotoNextMap ();
703 };
704
705 /*
706 ============
707 FindIntermission
708
709 Returns the entity to view from
710 ============
711 */
712 /*
713 entity() FindIntermission =
714 {
715         local   entity spot;
716         local   float cyc;
717
718 // look for info_intermission first
719         spot = find (world, classname, "info_intermission");
720         if (spot)
721         {       // pick a random one
722                 cyc = random() * 4;
723                 while (cyc > 1)
724                 {
725                         spot = find (spot, classname, "info_intermission");
726                         if (!spot)
727                                 spot = find (spot, classname, "info_intermission");
728                         cyc = cyc - 1;
729                 }
730                 return spot;
731         }
732
733 // then look for the start position
734         spot = find (world, classname, "info_player_start");
735         if (spot)
736                 return spot;
737
738 // testinfo_player_start is only found in regioned levels
739         spot = find (world, classname, "testplayerstart");
740         if (spot)
741                 return spot;
742
743 // then look for the start position
744         spot = find (world, classname, "info_player_deathmatch");
745         if (spot)
746                 return spot;
747
748         //objerror ("FindIntermission: no spot");
749         return world;
750 };
751 */
752
753 /*
754 ===============================================================================
755
756 RULES
757
758 ===============================================================================
759 */
760
761 void() DumpStats =
762 {
763         local float file;
764         local string s;
765
766         if(cvar("_printstats"))
767                 cvar_set("_printstats", "0");
768         else if(!gameover)
769                 return;
770
771         if(gameover)
772                 s = ":scores:";
773         else
774                 s = ":status:";
775
776         s = strcat(s, GetMapname(), ":", ftos(rint(time)));
777
778         if(cvar("sv_eventlog") && gameover)
779                 GameLogEcho(s, FALSE);
780         else if(cvar("sv_logscores_console"))
781                 ServerConsoleEcho(s, FALSE);
782         if(cvar("sv_logscores_file"))
783         {
784                 file = fopen(cvar_string("sv_logscores_filename"), FILE_APPEND);
785                 fputs(file, strcat(s, "\n"));
786         }
787
788         other = findchainflags(flags, FL_CLIENT);
789         while (other)
790         {
791                 if ((clienttype(other) == CLIENTTYPE_REAL) || (clienttype(other) == CLIENTTYPE_BOT && cvar("sv_logscores_bots")))
792                 {
793                         s = strcat(":player:", ftos(other.frags), ":");
794                         s = strcat(s, ftos(other.deaths), ":");
795                         s = strcat(s, ftos(rint(time - other.jointime)), ":");
796                         s = strcat(s, ftos(other.team), ":");
797
798                         if(cvar("sv_logscores_file"))
799                                 fputs(file, strcat(s, other.netname, "\n"));
800                         if(cvar("sv_eventlog") && gameover)
801                                 GameLogEcho(strcat(s, ftos(other.playerid), ":", other.netname), TRUE);
802                         else if(cvar("sv_logscores_console"))
803                                 ServerConsoleEcho(strcat(s, other.netname), TRUE);
804                 }
805                 other = other.chain;
806         }
807
808         if(cvar("sv_eventlog") && gameover)
809                 GameLogEcho(":end", FALSE);
810         else if(cvar("sv_logscores_console"))
811                 ServerConsoleEcho(":end", FALSE);
812         if(cvar("sv_logscores_file"))
813         {
814                 fputs(file, ":end\n");
815                 fclose(file);
816         }
817 }
818
819
820 /*
821 go to the next level for deathmatch
822 only called if a time or frag limit has expired
823 */
824 void() NextLevel =
825 {
826         gameover = TRUE;
827
828         intermission_running = 1;
829
830 // enforce a wait time before allowing changelevel
831         if(player_count > 0)
832                 intermission_exittime = time + cvar("sv_mapchange_delay");
833         else
834                 intermission_exittime = -60;
835
836         WriteByte (MSG_ALL, SVC_CDTRACK);
837         WriteByte (MSG_ALL, 3);
838         WriteByte (MSG_ALL, 3);
839
840         //pos = FindIntermission ();
841
842         VoteReset();
843
844         DumpStats();
845
846         if(cvar("sv_eventlog"))
847                 GameLogEcho(":gameover", FALSE);
848
849         GameLogClose();
850
851         other = findchainflags(flags, FL_CLIENT);
852         while (other != world)
853         {
854                 //other.nextthink = time + 0.5;
855                 other.takedamage = DAMAGE_NO;
856                 other.solid = SOLID_NOT;
857                 other.movetype = MOVETYPE_NONE;
858                 other.angles = other.v_angle;
859                 other.angles_x = other.angles_x * -1;
860
861                 self = other;
862
863                 if(other.winning)
864                         bprint(strcat(other.netname, " ^7wins.\n"));
865
866                 /*
867                 if (pos != world);
868                 {
869                         other.modelindex = 0;
870                         other.weaponentity = world; // remove weapon model
871                         other.view_ofs = '0 0 0';
872                         other.angles = other.v_angle = pos.mangle;
873                         if (!other.angles)
874                         {
875                                 other.angles = other.v_angle = pos.angles;
876                                 other.v_angle_x = other.v_angle_x * -1;
877                         }
878                         other.fixangle = TRUE;          // turn this way immediately
879                         setorigin (other, pos.origin);
880                 }
881                 */
882                 other = other.chain;
883         }
884
885         if(cvar("g_campaign"))
886                 CampaignPreIntermission();
887
888         WriteByte (MSG_ALL, SVC_INTERMISSION);
889
890         other = findchainflags(flags, FL_CLIENT);
891         while(other)
892         {
893                 if(clienttype(other) == CLIENTTYPE_REAL)
894                 if(cvar("sv_autoscreenshot"))
895                         stuffcmd(other, "wait\nscreenshot\necho \"^5A screenshot has been taken at request of the server.\"\n");
896                 other = other.chain;
897         }
898 };
899
900 /*
901 ============
902 CheckRules_Player
903
904 Exit deathmatch games upon conditions
905 ============
906 */
907 void() CheckRules_Player =
908 {
909         if (gameover)   // someone else quit the game already
910                 return;
911
912         // fixme: don't check players; instead check dom_team and ctf_team entities
913         //   (div0: and that in CheckRules_World please)
914         centermsg_check();
915 };
916
917 float checkrules_oneminutewarning;
918 float checkrules_leaderfrags;
919 float tdm_max_score, tdm_old_score;
920
921 float checkrules_equality;
922 float checkrules_overtimewarning;
923 float checkrules_overtimeend;
924
925 void() InitiateOvertime =
926 {
927         if(!checkrules_overtimeend)
928                 checkrules_overtimeend = time + 60 * cvar("timelimit_maxovertime");
929 }
930
931 float WINNING_NO = 0; // no winner, but time limits may terminate the game
932 float WINNING_YES = 1; // winner found
933 float WINNING_NEVER = 2; // no winner, enter overtime if time limit is reached
934 float WINNING_STARTOVERTIME = 3; // no winner, enter overtime NOW
935
936 float(float fraglimitreached, float equality) GetWinningCode =
937 {
938         if(equality)
939                 if(fraglimitreached)
940                         return WINNING_STARTOVERTIME;
941                 else
942                         return WINNING_NEVER;
943         else
944                 if(fraglimitreached)
945                         return WINNING_YES;
946                 else
947                         return WINNING_NO;
948 }
949
950 // set the .winning flag for exactly those players with a given field value
951 void(.float field, float value) SetWinners =
952 {
953         entity head;
954         head = findchain(classname, "player");
955         while (head)
956         {
957                 head.winning = (head.field == value);
958                 head = head.chain;
959         }
960 }
961
962 // set the .winning flag for those players with a given field value
963 void(.float field, float value) AddWinners =
964 {
965         entity head;
966         head = findchain(classname, "player");
967         while (head)
968         {
969                 if(head.field == value)
970                         head.winning = 1;
971                 head = head.chain;
972         }
973 }
974
975 // clear the .winning flags
976 void(void) ClearWinners =
977 {
978         entity head;
979         head = findchain(classname, "player");
980         while (head)
981         {
982                 head.winning = 0;
983                 head = head.chain;
984         }
985 }
986
987 // LMS winning condition: game terminates if and only if there's at most one
988 // one player who's living lives. Top two scores being equal cancels the time
989 // limit.
990 float() WinningCondition_LMS =
991 {
992         entity head;
993
994         if(lms_dead_count < 0)
995                 lms_dead_count = 0;
996
997         if(player_count > 1 && lms_dead_count >= player_count - 1)
998                 return WINNING_YES; // He's the last man standing!
999
1000         if((player_count == 1 && lms_dead_count == 1))
1001                 return WINNING_YES; // All dead... (n:n is handled by the test above)
1002
1003         dprint("player count = "); dprint(ftos(player_count));
1004         dprint(", dead count = "); dprint(ftos(lms_dead_count));
1005         dprint("\n");
1006
1007         // When we get here, we have at least two players who are actually LIVING,
1008         // or one player who is still waiting for a victim to join the server. Now
1009         // check if the top two players have equal score.
1010
1011         checkrules_leaderfrags = 0;
1012         head = findchain(classname, "player");
1013         checkrules_equality = FALSE;
1014         while (head)
1015         {
1016                 if(head.frags > checkrules_leaderfrags)
1017                 {
1018                         checkrules_leaderfrags = head.frags;
1019                         checkrules_equality = FALSE;
1020                 }
1021                 else if(head.frags > 0 && head.frags == checkrules_leaderfrags)
1022                         checkrules_equality = TRUE;
1023                 head = head.chain;
1024         }
1025
1026         SetWinners(frags, checkrules_leaderfrags);
1027
1028         // The top two players have the same amount of lives? No timelimit then,
1029         // enter overtime...
1030
1031         if(checkrules_equality)
1032                 return WINNING_NEVER;
1033
1034         // Top two have different scores? Way to go for our beloved TIMELIMIT!
1035         return WINNING_NO;
1036 }
1037
1038 // DM winning condition: game terminates if a player reached the fraglimit,
1039 // unless the first two players have the same score. The latter case also
1040 // breaks the time limit.
1041 float(float fraglimit) WinningCondition_MaxIndividualScore =
1042 {
1043         float checkrules_oldleaderfrags;
1044         entity head;
1045
1046         checkrules_oldleaderfrags = checkrules_leaderfrags;
1047         checkrules_leaderfrags = 0;
1048         head = findchain(classname, "player");
1049         checkrules_equality = FALSE;
1050         while (head)
1051         {
1052                 if(head.frags > checkrules_leaderfrags)
1053                 {
1054                         checkrules_leaderfrags = head.frags;
1055                         checkrules_equality = FALSE;
1056                 }
1057                 else if(head.frags > 0 && head.frags == checkrules_leaderfrags)
1058                         checkrules_equality = TRUE;
1059                 head = head.chain;
1060         }
1061
1062         SetWinners(frags, checkrules_leaderfrags);
1063
1064         if (!cvar("g_runematch"))
1065                 if (checkrules_leaderfrags != checkrules_oldleaderfrags)
1066                 {
1067                         if (checkrules_leaderfrags == fraglimit - 1)
1068                                 sound(world, CHAN_AUTO, "announcer/robotic/1fragleft.ogg", 1, ATTN_NONE);
1069                         else if (checkrules_leaderfrags == fraglimit - 2)
1070                                 sound(world, CHAN_AUTO, "announcer/robotic/2fragsleft.ogg", 1, ATTN_NONE);
1071                         else if (checkrules_leaderfrags == fraglimit - 3)
1072                                 sound(world, CHAN_AUTO, "announcer/robotic/3fragsleft.ogg", 1, ATTN_NONE);
1073                 }
1074
1075         return GetWinningCode(fraglimit && checkrules_leaderfrags >= fraglimit, checkrules_equality);
1076 }
1077
1078 float(float fraglimit) WinningConditionBase_Teamplay =
1079 {
1080         tdm_old_score = tdm_max_score;
1081         tdm_max_score = max(team1_score, team2_score, team3_score, team4_score);
1082
1083         checkrules_equality =
1084         (
1085                 (tdm_max_score > 0)
1086                 &&
1087                 (
1088                           (team1_score == tdm_max_score)
1089                         + (team2_score == tdm_max_score)
1090                         + (team3_score == tdm_max_score)
1091                         + (team4_score == tdm_max_score)
1092                         >= 2));
1093
1094         ClearWinners();
1095         if(team1_score == tdm_max_score)
1096                 AddWinners(team, COLOR_TEAM1);
1097         if(team2_score == tdm_max_score)
1098                 AddWinners(team, COLOR_TEAM2);
1099         if(team3_score == tdm_max_score)
1100                 AddWinners(team, COLOR_TEAM3);
1101         if(team4_score == tdm_max_score)
1102                 AddWinners(team, COLOR_TEAM4);
1103
1104         if(!cvar("g_runematch") && !cvar("g_domination"))
1105                 if(tdm_max_score != tdm_old_score)
1106                 {
1107                         if(tdm_max_score == fraglimit - 1)
1108                                 sound(world, CHAN_AUTO, "announcer/robotic/1fragleft.ogg", 1, ATTN_NONE);
1109                         else if(tdm_max_score == fraglimit - 2)
1110                                 sound(world, CHAN_AUTO, "announcer/robotic/2fragsleft.ogg", 1, ATTN_NONE);
1111                         else if(tdm_max_score == fraglimit - 3)
1112                                 sound(world, CHAN_AUTO, "announcer/robotic/3fragsleft.ogg", 1, ATTN_NONE);
1113                 }
1114
1115         return GetWinningCode(fraglimit && tdm_max_score >= fraglimit, checkrules_equality);
1116 }
1117
1118 // TDM winning condition: game terminates if a team's score sum reached the
1119 // fraglimit, unless the first two teams have the same total score. The latter
1120 // case also breaks the time limit.
1121 float(float fraglimit) WinningCondition_MaxTeamSum =
1122 {
1123         entity head;
1124
1125         team1_score = team2_score = team3_score = team4_score = 0;
1126
1127         head = findchain(classname, "player");
1128         while (head)
1129         {
1130                 if(head.team == COLOR_TEAM1)
1131                         team1_score += head.frags;
1132                 else if(head.team == COLOR_TEAM2)
1133                         team2_score += head.frags;
1134                 else if(head.team == COLOR_TEAM3)
1135                         team3_score += head.frags;
1136                 else if(head.team == COLOR_TEAM4)
1137                         team4_score += head.frags;
1138                 head = head.chain;
1139         }
1140
1141         return WinningConditionBase_Teamplay(fraglimit);
1142 }
1143
1144 // DOM/CTF winning condition: game terminates if the max of a team's players'
1145 // score reached the fraglimit, unless the first two teams have the same
1146 // maximum score. The latter case also breaks the time limit.
1147 float(float fraglimit) WinningCondition_MaxTeamMax =
1148 {
1149         entity head;
1150
1151         team1_score = team2_score = team3_score = team4_score = 0;
1152
1153         head = findchain(classname, "player");
1154         while (head)
1155         {
1156                 if(head.team == COLOR_TEAM1)
1157                 {
1158                         if(head.frags > team1_score)
1159                                 team1_score = head.frags;
1160                 }
1161                 else if(head.team == COLOR_TEAM2)
1162                 {
1163                         if(head.frags > team2_score)
1164                                 team2_score = head.frags;
1165                 }
1166                 else if(head.team == COLOR_TEAM3)
1167                 {
1168                         if(head.frags > team3_score)
1169                                 team3_score = head.frags;
1170                 }
1171                 else if(head.team == COLOR_TEAM4)
1172                 {
1173                         if(head.frags > team4_score)
1174                                 team4_score = head.frags;
1175                 }
1176                 head = head.chain;
1177         }
1178
1179         return WinningConditionBase_Teamplay(fraglimit);
1180 }
1181
1182 void PrintScoreboardFor(string name, string colorcode, float whichteam)
1183 {
1184         entity head;
1185         float fragtotal;
1186         string s;
1187         float found;
1188         found = FALSE;
1189         head = find(world, classname, "player");
1190         while(head)
1191         {
1192                 if(!whichteam || head.team == whichteam)
1193                 {
1194                         if(name != "")
1195                                 if(!found)
1196                                         ServerConsoleEcho(strcat(" ", colorcode, name, ":"), FALSE);
1197                         found = TRUE;
1198                         fragtotal = fragtotal + head.frags;
1199                         s = ftos(head.frags);
1200                         s = strcat(s, "/", ftos(head.deaths));
1201                         s = strcat(s, " @ ", ftos(head.ping));
1202                         if(clienttype(head) == CLIENTTYPE_BOT)
1203                                 s = strcat(s, "botms");
1204                         else
1205                                 s = strcat(s, "ms");
1206                         ServerConsoleEcho(strcat("  ", colorcode, head.netname, colorcode, " (", s, ")"), TRUE);
1207                 }
1208                 head = find(head, classname, "player");
1209         }
1210         if(whichteam && found)
1211                 ServerConsoleEcho(strcat(colorcode, "  (total: ", ftos(fragtotal), ")"), FALSE);
1212 }
1213
1214 void PrintScoreboard()
1215 {
1216         ServerConsoleEcho("Scoreboard:", FALSE);
1217         if(teams_matter)
1218         {
1219                 PrintScoreboardFor("Red", "^1", COLOR_TEAM1);
1220                 PrintScoreboardFor("Blue", "^4", COLOR_TEAM2);
1221                 PrintScoreboardFor("Pink", "^6", COLOR_TEAM3);
1222                 PrintScoreboardFor("Yellow", "^3", COLOR_TEAM4);
1223         }
1224         else
1225         {
1226                 PrintScoreboardFor("", "^7", 0);
1227         }
1228         ServerConsoleEcho(".", FALSE);
1229 }
1230
1231
1232 /*
1233 ============
1234 CheckRules_World
1235
1236 Exit deathmatch games upon conditions
1237 ============
1238 */
1239 void() CheckRules_World =
1240 {
1241         local float status;
1242         local float timelimit;
1243         local float fraglimit;
1244
1245         VoteThink();
1246
1247         SetDefaultAlpha();
1248
1249         if (intermission_running)
1250                 if (time >= intermission_exittime + 60)
1251                 {
1252                         GotoNextMap();
1253                         return;
1254                 }
1255
1256         if (gameover)   // someone else quit the game already
1257                 return;
1258
1259         DumpStats();
1260
1261         if(cvar("_scoreboard"))
1262         {
1263                 cvar_set("_scoreboard", "0");
1264                 PrintScoreboard();
1265         }
1266
1267         timelimit = cvar("timelimit") * 60;
1268         fraglimit = cvar("fraglimit");
1269
1270         if (timelimit && time >= timelimit)
1271                 InitiateOvertime();
1272
1273         if (checkrules_overtimeend && time >= checkrules_overtimeend)
1274         {
1275                 NextLevel();
1276                 return;
1277         }
1278
1279         if(!checkrules_overtimewarning && checkrules_overtimeend)
1280         {
1281                 checkrules_overtimewarning = TRUE;
1282                 //sound(world, CHAN_AUTO, "announcer/robotic/1minuteremains.ogg", 1, ATTN_NONE);
1283                 bcenterprint("^3Now playing ^1OVERTIME^3!\n\n^3Keep fragging until we have a ^1winner^3!");
1284         }
1285
1286         if (!checkrules_oneminutewarning && timelimit > 0 && time > timelimit - 60)
1287         {
1288                 checkrules_oneminutewarning = TRUE;
1289                 sound(world, CHAN_AUTO, "announcer/robotic/1minuteremains.ogg", 1, ATTN_NONE);
1290         }
1291
1292         status = WINNING_NO;
1293         if(cvar("g_lms"))
1294         {
1295                 status = WinningCondition_LMS();
1296         }
1297         else
1298         {
1299                 if(teams_matter)
1300                 {
1301                         if(cvar("g_tdm") || cvar("g_runematch") || cvar("g_ctf") || cvar("g_domination"))
1302                                 status = WinningCondition_MaxTeamSum(fraglimit);
1303                         //else if()
1304                         //      status = WinningCondition_MaxTeamMax(fraglimit);
1305                         else
1306                         {
1307                                 dprint("div0: How can this happen?\n");
1308                                 status = WinningCondition_MaxTeamMax(fraglimit);
1309                         }
1310                 }
1311                 else
1312                         status = WinningCondition_MaxIndividualScore(fraglimit);
1313         }
1314
1315         if(status == WINNING_STARTOVERTIME)
1316         {
1317                 status = WINNING_NEVER;
1318                 InitiateOvertime();
1319         }
1320
1321         if(status == WINNING_NEVER)
1322                 // equality cases! Nobody wins if the overtime ends in a draw.
1323                 ClearWinners();
1324
1325         if(checkrules_overtimeend)
1326                 if(status != WINNING_NEVER)
1327                         status = WINNING_YES;
1328
1329         if(status == WINNING_YES)
1330                 NextLevel();
1331 };