Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
world.qc
Go to the documentation of this file.
1#include "world.qh"
2
4#include <common/constants.qh>
9#include <common/mapinfo.qh>
17#include <common/net_linked.qh>
20#include <common/playerstats.qh>
21#include <common/state.qh>
22#include <common/stats.qh>
23#include <common/teams.qh>
24#include <common/util.qh>
28#include <server/anticheat.qh>
29#include <server/antilag.qh>
30#include <server/bot/api.qh>
31#include <server/campaign.qh>
32#include <server/cheats.qh>
33#include <server/client.qh>
38#include <server/damage.qh>
39#include <server/gamelog.qh>
40#include <server/hook.qh>
41#include <server/ipban.qh>
42#include <server/items/items.qh>
43#include <server/main.qh>
44#include <server/mapvoting.qh>
46#include <server/race.qh>
47#include <server/scores.qh>
49#include <server/spawnpoints.qh>
50#include <server/teamplay.qh>
52
53const float LATENCY_THINKRATE = 10;
59{
60 float delta;
61 entity e;
62
63 delta = 3 / maxclients;
64 if(delta < sys_frametime)
65 delta = 0;
66 this.nextthink = time + delta;
67
68 e = edict_num(this.cnt + 1);
69 if(IS_CLIENT(e) && IS_REAL_CLIENT(e))
70 {
71 WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
73 WriteShort(MSG_BROADCAST, bound(1, rint(CS(e).ping), 32767));
76
77 // record latency times for clients throughout the match so we can report it to playerstats
79 {
80 CS(e).latency_sum += CS(e).ping;
81 ++CS(e).latency_cnt;
82 CS(e).latency_time = time;
83 //print("sum: ", ftos(CS(e).latency_sum), ", cnt: ", ftos(CS(e).latency_cnt), ", avg: ", ftos(CS(e).latency_sum / CS(e).latency_cnt), ".\n");
84 }
85 }
86 else
87 {
88 WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
93 }
94 this.cnt = (this.cnt + 1) % maxclients;
95}
102
104
115
117{
118 float n;
120 {
121 // cvar_set("_sv_init", "0");
122 // we do NOT set this to 0 any more, so someone "accidentally" changing
123 // to this "init" map on a dedicated server will cause no permanent harm
124
127
128 if(!DoNextMapOverride(1))
129 GotoNextMap(1);
130
131 return;
132 }
133
134 if(time < 5)
135 {
136 this.nextthink = time;
137 }
138 else
139 {
140 this.nextthink = time + 1;
141 LOG_INFO("Waiting for _sv_init being set to 1 by initialization scripts...");
142 }
143}
144
146{
147 float h;
148 string k, v, d;
149 float n, i, adding, pureadding;
150
154
155 h = buf_create();
156 buf_cvarlist(h, "", "_"); // exclude all _ cvars as they are temporary
157 n = buf_getsize(h);
158
159 adding = true;
160 pureadding = true;
161
162 for(i = 0; i < n; ++i)
163 {
164 k = bufstr_get(h, i);
165
166#define BADPREFIX_COND(p) (substring(k, 0, strlen(p)) == p)
167#define BADSUFFIX_COND(s) (substring(k, -strlen(s), -1) == s)
168
169#define BADPREFIX(p) if(BADPREFIX_COND(p)) continue
170#define BADPRESUFFIX(p, s) if(BADPREFIX_COND(p) && BADSUFFIX_COND(s)) continue
171#define BADCVAR(p) if(k == p) continue
172#define BADVALUE(p, val) if (k == p && v == val) continue
173#define BADPRESUFFIXVALUE(p, s, val) if(BADPREFIX_COND(p) && BADSUFFIX_COND(s) && v == val) continue
174
175 // general excludes and namespaces for server admin used cvars
176 BADPREFIX("help_"); // PN's server has this listed as changed, let's not rat him out for THAT
177
178 // internal
179 BADPREFIX("csqc_");
180 BADPREFIX("cvar_check_");
181 BADCVAR("gamecfg");
182 BADCVAR("g_configversion");
183 BADCVAR("halflifebsp");
184 BADCVAR("sv_mapformat_is_quake2");
185 BADCVAR("sv_mapformat_is_quake3");
186 BADPREFIX("sv_world");
187
188 // client
189 BADPREFIX("chase_");
190 BADPREFIX("cl_");
191 BADPREFIX("con_");
192 BADPREFIX("scoreboard_");
193 BADPREFIX("g_campaign");
194 BADPREFIX("g_waypointsprite_");
195 BADPREFIX("gl_");
196 BADPREFIX("joy");
197 BADPREFIX("hud_");
198 BADPREFIX("m_");
199 BADPREFIX("menu_");
200 BADPREFIX("net_slist_");
201 BADPREFIX("r_");
202 BADPREFIX("sbar_");
203 BADPREFIX("scr_");
204 BADPREFIX("snd_");
205 BADPREFIX("show");
206 BADPREFIX("sensitivity");
207 BADPREFIX("userbind");
208 BADPREFIX("v_");
209 BADPREFIX("vid_");
210 BADPREFIX("crosshair");
211 BADPREFIX("notification_");
212 BADPREFIX("prvm_");
213 BADCVAR("mod_q3bsp_lightmapmergepower");
214 BADCVAR("mod_q3bsp_nolightmaps");
215 BADCVAR("fov");
216 BADCVAR("mastervolume");
217 BADCVAR("volume");
218 BADCVAR("bgmvolume");
219 BADCVAR("in_pitch_min");
220 BADCVAR("in_pitch_max");
221 BADCVAR("bottomcolor");
222 BADCVAR("topcolor");
223 BADCVAR("playermodel");
224
225 // private
226 BADCVAR("developer");
227 BADCVAR("log_dest_udp");
228 BADCVAR("net_address");
229 BADCVAR("net_address_ipv6");
230 BADCVAR("port");
231 BADCVAR("savedgamecfg");
232 BADCVAR("serverconfig");
233 BADCVAR("sv_autoscreenshot");
234 BADCVAR("sv_heartbeatperiod");
235 BADCVAR("sv_vote_master_ids");
236 BADCVAR("sv_vote_master_password");
237 BADCVAR("sys_colortranslation");
238 BADCVAR("sys_specialcharactertranslation");
239 BADCVAR("timeformat");
240 BADCVAR("timestamps");
241 BADCVAR("g_require_stats");
242 BADCVAR("g_chatban_list");
243 BADCVAR("g_playban_list");
244 BADCVAR("g_playban_minigames");
245 BADCVAR("g_voteban_list");
246 BADPREFIX("developer_");
247 BADPREFIX("g_ban_");
248 BADPREFIX("g_banned_list");
249 BADPREFIX("g_require_stats_");
250 BADPREFIX("g_chat_flood_");
251 BADPREFIX("g_ghost_items");
252 BADPREFIX("g_playerstats_");
253 BADPREFIX("g_voice_flood_");
254 BADPREFIX("log_file");
255 BADPREFIX("quit_");
256 BADPREFIX("rcon_");
257 BADPREFIX("sv_allowdownloads");
258 BADPREFIX("sv_autodemo");
259 BADPREFIX("sv_curl_");
260 BADPREFIX("sv_eventlog");
261 BADPREFIX("sv_logscores_");
262 BADPREFIX("sv_master");
263 BADPREFIX("sv_weaponstats_");
264 BADPREFIX("sv_waypointsprite_");
265 BADCVAR("rescan_pending");
266
267 // these can contain player IDs, so better hide
268 BADPREFIX("g_forced_team_");
269 BADCVAR("sv_allow_customplayermodels_idlist");
270 BADCVAR("sv_allow_customplayermodels_speciallist");
271
272 // mapinfo
273 BADCVAR("fraglimit");
274 BADCVAR("g_arena");
275 BADCVAR("g_assault");
276 BADCVAR("g_ca");
277 BADCVAR("g_ca_teams");
278 BADCVAR("g_conquest");
279 BADCVAR("g_conquest_teams");
280 BADCVAR("g_ctf");
281 BADCVAR("g_cts");
282 BADCVAR("g_dotc");
283 BADCVAR("g_dm");
284 BADCVAR("g_domination");
285 BADCVAR("g_domination_default_teams");
286 BADCVAR("g_duel");
287 BADCVAR("g_duel_not_dm_maps");
288 BADCVAR("g_freezetag");
289 BADCVAR("g_freezetag_teams");
290 BADCVAR("g_invasion_type");
291 BADCVAR("g_jailbreak");
292 BADCVAR("g_jailbreak_teams");
293 BADCVAR("g_keepaway");
294 BADCVAR("g_keyhunt");
295 BADCVAR("g_keyhunt_teams");
296 BADCVAR("g_lms");
297 BADCVAR("g_mayhem");
298 BADCVAR("g_nexball");
299 BADCVAR("g_onslaught");
300 BADCVAR("g_race");
301 BADCVAR("g_race_laps_limit");
302 BADCVAR("g_race_qualifying_timelimit");
303 BADCVAR("g_race_qualifying_timelimit_override");
304 BADCVAR("g_runematch");
305 BADCVAR("g_shootfromeye");
306 BADCVAR("g_snafu");
307 BADCVAR("g_survival");
308 BADCVAR("g_survival_not_dm_maps");
309 BADCVAR("g_tdm");
310 BADCVAR("g_tdm_on_dm_maps");
311 BADCVAR("g_tdm_teams");
312 BADCVAR("g_tka");
313 BADCVAR("g_tka_on_ka_maps");
314 BADCVAR("g_tka_on_tdm_maps");
315 BADCVAR("g_tka_teams");
316 BADCVAR("g_tmayhem");
317 BADCVAR("g_tmayhem_teams");
318 BADCVAR("g_vip");
319 BADCVAR("leadlimit");
320 BADCVAR("teamplay");
321 BADCVAR("timelimit");
322 BADCVAR("g_mapinfo_q3compat");
323 BADCVAR("g_mapinfo_settemp_acl");
324 BADCVAR("g_mapinfo_ignore_warnings");
325 BADCVAR("g_maplist_ignore_sizes");
326 BADCVAR("g_maplist_sizes_count_bots");
327
328 // long
329 BADCVAR("hostname");
330 BADCVAR("g_maplist");
331 BADCVAR("g_maplist_mostrecent");
332 BADCVAR("sv_motd");
333 BADCVAR("sv_termsofservice_url");
334 BADCVAR("sv_adminnick");
335
336 v = cvar_string(k);
337 d = cvar_defstring(k);
338 if(v == d)
339 continue;
340
341 if(adding)
342 {
343 if (cvar_changes == "")
344 cvar_changes = "// this server runs at modified server settings:\n";
345
346 cvar_changes = strcat(cvar_changes, k, " \"", v, "\" // \"", d, "\"\n");
348 {
349 cvar_changes = "// too many settings have been changed to show them here\n";
350 adding = 0;
351 }
352 }
353
354 // now check if the changes are actually gameplay relevant
355
356 // does nothing gameplay relevant
357 BADCVAR("captureleadlimit_override");
358 BADCVAR("condump_stripcolors");
359 BADCVAR("fs_gamedir");
360 BADCVAR("g_allow_oldvortexbeam");
361 BADCVAR("g_balance_kill_delay");
362 BADCVAR("g_buffs_pickup_anyway");
363 BADCVAR("g_buffs_randomize");
364 BADCVAR("g_buffs_randomize_teamplay");
365 BADCVAR("g_campcheck_distance");
366 BADCVAR("g_chatsounds");
367 BADCVAR("g_ca_point_leadlimit");
368 BADCVAR("g_ca_point_limit");
369 BADCVAR("g_ca_spectate_enemies");
370 BADCVAR("g_ctf_captimerecord_always");
371 BADCVAR("g_ctf_flag_glowtrails");
372 BADCVAR("g_ctf_dynamiclights");
373 BADCVAR("g_ctf_flag_pickup_verbosename");
374 BADCVAR("g_ctf_flagcarrier_auto_helpme_damage");
375 BADPRESUFFIX("g_ctf_flag_", "_model");
376 BADPRESUFFIX("g_ctf_flag_", "_skin");
377 BADCVAR("g_domination_point_leadlimit");
378 BADCVAR("g_forced_respawn");
379 BADCVAR("g_freezetag_point_leadlimit");
380 BADCVAR("g_freezetag_point_limit");
381 BADCVAR("g_glowtrails");
382 BADCVAR("g_hats");
383 BADCVAR("g_casings");
384 BADCVAR("g_invasion_point_limit");
385 BADCVAR("g_jump_grunt");
386 BADCVAR("g_keepaway_ballcarrier_effects");
387 BADCVAR("g_keepaway_point_limit");
388 BADCVAR("g_keepawayball_effects");
389 BADCVAR("g_keyhunt_point_leadlimit");
390 BADCVAR("g_nexball_goalleadlimit");
391 BADCVAR("g_new_toys_autoreplace");
392 BADCVAR("g_new_toys_use_pickupsound");
393 BADCVAR("g_onslaught_point_limit");
394 BADCVAR("g_physics_predictall");
395 BADCVAR("g_piggyback");
396 BADCVAR("g_playerclip_collisions");
397 BADCVAR("g_spawn_alloweffects");
398 BADCVAR("g_tdm_point_leadlimit");
399 BADCVAR("g_tdm_point_limit");
400 BADCVAR("g_tka_point_leadlimit");
401 BADCVAR("g_tka_point_limit");
402 BADCVAR("g_mayhem_point_limit");
403 BADCVAR("g_mayhem_point_leadlimit");
404 BADCVAR("g_tmayhem_point_limit");
405 BADCVAR("g_tmayhem_point_leadlimit");
406 BADCVAR("leadlimit_and_fraglimit");
407 BADCVAR("leadlimit_override");
408 BADCVAR("pausable");
409 BADCVAR("sv_announcer");
410 BADCVAR("sv_autopause");
411 BADCVAR("sv_checkforpacketsduringsleep");
412 BADCVAR("sv_damagetext");
413 BADCVAR("sv_db_saveasdump");
414 BADCVAR("sv_intermission_cdtrack");
415 BADCVAR("sv_mapchange_delay");
416 BADCVAR("sv_minigames");
417 BADCVAR("sv_namechangetimer");
418 BADCVAR("sv_precacheplayermodels");
419 BADCVAR("sv_qcphysics");
420 BADCVAR("sv_radio");
421 BADCVAR("sv_stepheight");
422 BADCVAR("sv_timeout");
423 BADCVAR("sv_weapons_modeloverride");
424 BADCVAR("w_prop_interval");
425 BADPREFIX("chat_");
426 BADPREFIX("crypto_");
427 BADPREFIX("g_chat_");
428 BADPREFIX("g_ctf_captimerecord_");
429 BADPREFIX("g_hats_");
430 BADPREFIX("g_maplist_");
431 BADPREFIX("g_mod_");
432 BADPREFIX("g_respawn_");
433 BADPREFIX("net_");
434 BADPREFIX("skill_");
435 BADPREFIX("sv_allow_");
436 BADPREFIX("sv_maxidle");
437 BADPREFIX("sv_minigames_");
438 BADPREFIX("sv_radio_");
439 BADPREFIX("sv_timeout_");
440 BADPREFIX("sv_vote_");
441 BADPREFIX("timelimit_");
442 BADPRESUFFIX("g_", "_round_timelimit");
443 BADPRESUFFIX("g_", "_round_enddelay");
444
445 // allowed changes to server admins (please sync this to server.cfg)
446 // vi commands:
447 // :/"impure"/,$d
448 // :g!,^\/\/[^ /],d
449 // :%s,//\‍([^ ]*\‍).*,BADCVAR("\1");,
450 // :%!sort
451 // yes, this does contain some redundant stuff, don't really care
452 BADPREFIX("bot_ai_");
453 BADCVAR("bot_config_file");
454 BADCVAR("bot_number");
455 BADCVAR("bot_prefix");
456 BADCVAR("bot_suffix");
457 BADCVAR("capturelimit_override");
458 BADCVAR("fraglimit_override");
459 BADCVAR("gametype");
460 BADCVAR("g_antilag");
461 BADCVAR("g_balance_teams");
462 BADCVAR("g_balance_teams_queue");
463 BADCVAR("g_balance_teams_remove");
464 BADCVAR("g_balance_teams_remove_wait");
465 BADCVAR("g_balance_teams_prevent_imbalance");
466 BADCVAR("g_balance_teams_scorefactor");
467 BADCVAR("g_ban_sync_trusted_servers");
468 BADCVAR("g_ban_sync_uri");
469 BADCVAR("g_buffs");
470 BADCVAR("g_ca_teams_override");
471 BADCVAR("g_ca_prevent_stalemate");
472 BADCVAR("g_ctf_fullbrightflags");
473 BADCVAR("g_ctf_ignore_frags");
474 BADCVAR("g_ctf_leaderboard");
475 BADCVAR("g_domination_point_limit");
476 BADCVAR("g_domination_teams_override");
477 BADCVAR("g_freezetag_revive_spawnshield");
478 BADCVAR("g_freezetag_teams_override");
479 BADCVAR("g_friendlyfire");
480 BADCVAR("g_fullbrightitems");
481 BADCVAR("g_fullbrightplayers");
482 BADCVAR("g_keyhunt_point_limit");
483 BADCVAR("g_keyhunt_teams_override");
484 BADCVAR("g_lms_lives_override");
485 BADCVAR("g_mayhem_powerups");
486 BADCVAR("g_maplist");
487 BADCVAR("g_maxplayers");
488 BADCVAR("g_mirrordamage");
489 BADCVAR("g_nexball_goallimit");
490 BADCVAR("g_norecoil");
491 BADCVAR("g_physics_clientselect");
492 BADCVAR("g_pinata");
493 BADCVAR("g_powerups");
494 BADCVAR("g_powerups_drop_ondeath");
495 BADCVAR("g_player_brightness");
496 BADCVAR("g_rocket_flying");
497 BADCVAR("g_rocket_flying_disabledelays");
498 BADPREFIX("g_spawnshield");
499 BADCVAR("g_start_delay");
500 BADCVAR("g_superspectate");
501 BADCVAR("g_tdm_teams_override");
502 BADCVAR("g_tka_teams_override");
503 BADCVAR("g_tmayhem_teams_override");
504 BADCVAR("g_tmayhem_powerups");
505 BADCVAR("g_weapon_stay"); BADPRESUFFIX("g_", "_weapon_stay");
506 BADCVAR("hostname");
507 BADCVAR("log_file");
508 BADCVAR("maxplayers");
509 BADCVAR("minplayers");
510 BADCVAR("minplayers_per_team");
511 BADCVAR("net_address");
512 BADCVAR("port");
513 BADCVAR("rcon_password");
514 BADCVAR("rcon_restricted_commands");
515 BADCVAR("rcon_restricted_password");
516 BADCVAR("skill");
517 BADCVAR("sv_autoscreenshot");
518 BADCVAR("sv_autotaunt");
519 BADCVAR("sv_curl_defaulturl");
520 BADCVAR("sv_defaultcharacter");
521 BADCVAR("sv_defaultcharacterskin");
522 BADCVAR("sv_defaultplayercolors");
523 BADCVAR("sv_defaultplayermodel");
524 BADCVAR("sv_defaultplayerskin");
525 BADCVAR("sv_maxrate");
526 BADCVAR("sv_motd");
527 BADCVAR("sv_public");
528 BADCVAR("sv_showfps");
529 BADCVAR("sv_showskill");
530 BADCVAR("sv_showspectators");
531 BADCVAR("sv_status_privacy");
532 BADCVAR("sv_taunt");
533 BADCVAR("sv_vote_call");
534 BADCVAR("sv_vote_commands");
535 BADCVAR("sv_vote_majority_factor");
536 BADPREFIX("sv_vote_master");
537 BADCVAR("sv_vote_simple_majority_factor");
538 BADVALUE("sys_ticrate", "0.0078125");
539 BADVALUE("sys_ticrate", "0.015625");
540 BADVALUE("sys_ticrate", "0.03125");
541 BADCVAR("teamplay_mode");
542 BADCVAR("timelimit_override");
543 BADPREFIX("g_warmup");
544 BADPREFIX("sv_info_");
545 BADPREFIX("sv_ready_restart_");
546 BADCVAR("sv_teamnagger");
547
548 BADPRESUFFIXVALUE("g_", "_weaponarena", "most");
549 BADPRESUFFIXVALUE("g_", "_weaponarena", "most_available");
550
551 // mutators that announce themselves properly to the server browser
552 BADCVAR("g_instagib");
553 BADCVAR("g_new_toys");
554 BADCVAR("g_nix");
555 BADCVAR("g_grappling_hook");
556 BADCVAR("g_jetpack");
557
558#undef BADPRESUFFIX
559#undef BADPREFIX
560#undef BADCVAR
561#undef BADVALUE
562#undef BADPRESUFFIXVALUE
563
564 if(pureadding)
565 {
566 if (cvar_purechanges == "")
567 cvar_purechanges = "// this server runs at modified gameplay settings:\n";
568
569 cvar_purechanges = strcat(cvar_purechanges, k, " \"", v, "\" // \"", d, "\"\n");
571 {
572 cvar_purechanges = "// too many settings have been changed to show them here\n";
573 pureadding = 0;
574 }
575 }
577 // WARNING: this variable is used for the server list
578 // NEVER dare to skip this code!
579 // Hacks to intentionally appearing as "pure server" even though you DO have
580 // modified settings may be punished by removal from the server list.
581 // You can do to the variables cvar_changes and cvar_purechanges all you want,
582 // though.
583 }
584 buf_del(h);
585
586 if(cvar_changes == "")
587 cvar_changes = "// this server runs at default server settings\n";
589
590 if(cvar_purechanges == "")
591 cvar_purechanges = "// this server runs at default gameplay settings\n";
593}
594
596bool RandomSeed_Send(entity this, entity to, int sf)
597{
598 WriteHeader(MSG_ENTITY, ENT_CLIENT_RANDOMSEED);
600 return true;
601}
603{
604 this.cnt = bound(0, floor(random() * 65536), 65535);
605 this.nextthink = time + 5;
606
607 this.SendFlags |= 1;
608}
610{
614
615 getthink(randomseed)(randomseed); // sets random seed and nextthink
616}
617
618spawnfunc(__init_dedicated_server)
619{
620 // handler for _init/_init map (only for dedicated server initialization)
621
622 world_initialized = -1; // don't complain
623
625
626 entity e = new(GotoFirstMap);
628 e.nextthink = time; // this is usually 1 at this point
629
630 e = new(info_player_deathmatch); // safeguard against player joining
631
632 // assign reflectively to avoid "assignment to world" warning
633 for (int i = 0, n = numentityfields(); i < n; ++i)
634 {
635 string k = entityfieldname(i);
636 if (k == "classname")
637 {
638 // safeguard against various stuff ;)
639 putentityfieldstring(i, this, "worldspawn");
640 break;
641 }
642 }
643
644 // needs to be done so early because of the constants they create
645 static_init();
648
649 IL_PUSH(g_spawnpoints, e); // just incase
650
653}
654
658
660{
661 maxclients = 0;
662 for (entity head = nextent(NULL); head; head = nextent(head)) {
663 ++maxclients;
664 }
665}
666
668{
669 // at this stage team entities are spawned, teamplay contains the number of them
670
673
674 if (warmup_stage >= 0 && autocvar_g_maxplayers >= 0)
675 return;
676 if (!g_duel)
678
679 if (autocvar_g_maxplayers < 0)
680 {
681 if (map_maxplayers <= 0)
682 map_maxplayers = maxclients; // unlimited, but may need rounding
684 if (teamplay)
685 {
686 // automatic maxplayers should be a multiple of team count
687 int down = map_maxplayers % AVAILABLE_TEAMS;
688 int up = AVAILABLE_TEAMS - down;
689 map_maxplayers += (up < down && up + map_maxplayers <= maxclients) ? up : -down;
690 }
691 }
692
693 if (warmup_stage < 0)
694 {
695 int m = GetPlayerLimit();
696 if (m <= 0)
697 m = maxclients;
699 if (teamplay)
700 {
701 // automatic minplayers should be a multiple of team count
702 int down = map_minplayers % AVAILABLE_TEAMS;
703 int up = AVAILABLE_TEAMS - down;
704 map_minplayers += (up <= down && up + map_minplayers <= m) ? up : -down;
705 }
706 }
707 else
708 map_minplayers = 0; // don't display a minimum if it's not used (g_maxplayers < 0 && g_warmup >= 0)
709}
710
712{
713 VoteReset(false);
714
715 // find out good world mins/maxs bounds, either the static bounds found by looking for solid, or the mapinfo specified bounds
717 // assign reflectively to avoid "assignment to world" warning
718 for (int i = 0, done = 0, n = numentityfields(); i < n; ++i)
719 {
720 string k = entityfieldname(i);
721 vector v = (k == "mins") ? mi_min : (k == "maxs") ? mi_max : '0 0 0';
722 if (v)
723 {
724 putentityfieldstring(i, world, sprintf("%v", v));
725 if (++done == 2) break;
726 }
727 }
728 // currently, NetRadiant's limit is 131072 qu for each side
729 // distance from one corner of a 131072qu cube to the opposite corner is approx. 227023 qu
730 // set the distance according to map size but don't go over the limit to avoid issues with float precision
731 // in case somebody makes extremely large maps
732 max_shot_distance = min(230000, vlen(world.maxs - world.mins));
733
735 GameRules_teams(false);
736
737 if (!cvar_value_issafe(world.fog))
738 {
739 LOG_INFO("The current map contains a potentially harmful fog setting, ignored");
740 world.fog = string_null;
741 }
742 if(MapInfo_Map_fog != "")
743 {
744 if(MapInfo_Map_fog == "none")
745 world.fog = string_null;
746 else
748 }
750
752
755 cvar_set("_sv_vote_gametype_custom", ""); // clear it immediately so it can't get stuck
756
759
761}
762
764spawnfunc(worldspawn)
765{
766 // Must be checked first because we don't always error() and don't want to print this twice.
768 {
769 string msg = "world already spawned - your map may have EXACTLY ONE worldspawn!";
770 if (q3compat) // must be set during (first) worldspawn
771 {
772 // Q3 ignores spurious/extra worldspawn entities, test map: q3dmz_carnage
773 LOG_WARN(msg);
774 delete(this);
775 return;
776 }
777 else
778 error(msg);
779 }
781
782#ifdef WATERMARK
783 string watermark_start = cvar_string("sv_watermark_start");
784 if (watermark_start == "") // always true on Xonotic (re)start
785 cvar_set("sv_watermark_start", WATERMARK);
786 else if (watermark_start != WATERMARK) // true when qc code has been recompiled on a different git commit
787 {
788 LOG_INFOF(
789 "\n^1 Warning: ^3the server QC program was updated without a full restart."
790 "\n^3 Please restart the Xonotic executable otherwise you may get random bugs."
791 "\n\n");
792 }
793#endif
794
796
797 cvar_set("_endmatch", "0");
798
800 {
802 }
803 else
804 {
806 }
807
808 bool wantrestart = false;
809 {
811 {
812 // DP unloads dlcache pk3s before starting a listen server since https://gitlab.com/xonotic/darkplaces/-/merge_requests/134
813 // restore csqc_progname too
814 string expect = "csprogs.dat";
815 wantrestart = cvar_string("csqc_progname") != expect;
816 cvar_set("csqc_progname", expect);
817 }
818 else
819 {
820 // Try to use versioned csprogs from pk3
821 // Only ever use versioned csprogs.dat files on dedicated servers;
822 // we need to reset csqc_progname on clients ourselves, and it's easier if the client's release name is constant
823 string pk3csprogs = "csprogs-" WATERMARK ".dat";
824 // This always works; fall back to it if a versioned csprogs.dat is suddenly missing
825 string select = "csprogs.dat";
826 if (fexists(pk3csprogs)) select = pk3csprogs;
827 if (cvar_string("csqc_progname") != select)
828 {
829 cvar_set("csqc_progname", select);
830 wantrestart = true;
831 }
832 // Check for updates on startup
833 // We do it this way for atomicity so that connecting clients still match the server progs and don't disconnect
834 int sentinel = fopen("progs.txt", FILE_READ);
835 if (sentinel >= 0)
836 {
837 string switchversion = fgets(sentinel);
838 fclose(sentinel);
839 if (switchversion != "" && switchversion != WATERMARK)
840 {
841 LOG_INFOF("Switching progs: " WATERMARK " -> %s", switchversion);
842 // if it doesn't exist, assume either:
843 // a) the current program was overwritten
844 // b) this is a client only update
845 string newprogs = sprintf("progs-%s.dat", switchversion);
846 if (fexists(newprogs))
847 {
848 cvar_set("sv_progs", newprogs);
849 wantrestart = true;
850 }
851 string newcsprogs = sprintf("csprogs-%s.dat", switchversion);
852 if (fexists(newcsprogs))
853 {
854 cvar_set("csqc_progname", newcsprogs);
855 wantrestart = true;
856 }
857 }
858 }
859 }
860 if (wantrestart)
861 {
862 LOG_INFO("Restart requested");
864 // let initialization continue, shutdown depends on it
865 }
866 }
867
868 delete_fn = remove_safely; // during spawning, watch what you remove!
869
870 cvar_changes_init(); // do this very early now so it REALLY matches the server config
871
872 // default to RACE_RECORD, can be overwritten by gametypes
874
875 // needs to be done so early because of the constants they create
876 static_init();
877
879
881
882 // 0 normal
883 lightstyle(0, "m");
884
885 // 1 FLICKER (first variety)
886 lightstyle(1, "mmnmmommommnonmmonqnmmo");
887
888 // 2 SLOW STRONG PULSE
889 lightstyle(2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba");
890
891 // 3 CANDLE (first variety)
892 lightstyle(3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg");
893
894 // 4 FAST STROBE
895 lightstyle(4, "mamamamamama");
896
897 // 5 GENTLE PULSE 1
898 lightstyle(5,"jklmnopqrstuvwxyzyxwvutsrqponmlkj");
899
900 // 6 FLICKER (second variety)
901 lightstyle(6, "nmonqnmomnmomomno");
902
903 // 7 CANDLE (second variety)
904 lightstyle(7, "mmmaaaabcdefgmmmmaaaammmaamm");
905
906 // 8 CANDLE (third variety)
907 lightstyle(8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa");
908
909 // 9 SLOW STROBE (fourth variety)
910 lightstyle(9, "aaaaaaaazzzzzzzz");
911
912 // 10 FLUORESCENT FLICKER
913 lightstyle(10, "mmamammmmammamamaaamammma");
914
915 // 11 SLOW PULSE NOT FADE TO BLACK
916 lightstyle(11, "abcdefghijklmnopqrrqponmlkjihgfedcba");
917
918 // styles 32-62 are assigned by the spawnfunc_light program for switchable lights
919
920 // 63 testing
921 lightstyle(63, "a");
922
925 else
926 PlayerStats_GameReport_Init(); // we need this to be initiated before InitGameplayMode
927
929
934
936
937 player_count = 0;
942
944
945 // NOTE for matchid:
946 // changing the logic generating it is okay. But:
947 // it HAS to stay <= 64 chars
948 // character set: ASCII 33-126 without the following characters: : ; ' " \ $
949 // strftime(false, "%s") isn't reliable, see strftime_s description
950 matchid = strzone(sprintf("%d.%s.%06d", autocvar_sv_eventlog_files_counter, strftime_s(), random() * 1000000));
951
953 GameLogInit(); // requires matchid to be set
954
956
959
960 Ban_LoadBans();
961
964
967
968 // quake 3 music support
969 // bones_was_here: Q3 doesn't support .noise but the Nexuiz _MapInfo_Generate() does.
970 // TODO: Q3 supports an optional intro file: "music/intro.wav music/loop.wav"
971 string music = GetField_fullspawndata(world, "music", true);
972 if (music || world.noise)
973 // prefer .music over .noise
974 strcpy(clientstuff, strcat(clientstuff, "cd loop \"", (music ? music : world.noise), "\"\n"));
975
976 if(whichpack(strcat("maps/", mapname, ".cfg")) != "")
977 {
978 int fd = fopen(strcat("maps/", mapname, ".cfg"), FILE_READ);
979 if(fd != -1)
980 {
981 string s;
982 while((s = fgets(fd)))
983 {
984 int l = tokenize_console(s);
985 if(l < 2)
986 continue;
987 if(argv(0) == "cd")
988 {
989 string trackname = argv(2);
990 LOG_INFO("Found ^1UNSUPPORTED^7 cd loop command in .cfg file; put this line in mapinfo instead:");
991 LOG_INFO(" cdtrack ", trackname);
992 if (cvar_value_issafe(trackname))
993 {
994 string newstuff = strcat(clientstuff, "cd loop \"", trackname, "\"\n");
995 strcpy(clientstuff, newstuff);
996 }
997 }
998 else if(argv(0) == "fog")
999 {
1000 LOG_INFO("Found ^1UNSUPPORTED^7 fog command in .cfg file; put this line in worldspawn in the .map/.bsp/.ent file instead:");
1001 LOG_INFO(" \"fog\" \"", s, "\"");
1002 }
1003 else if(argv(0) == "set")
1004 {
1005 LOG_INFO("Found ^1UNSUPPORTED^7 set command in .cfg file; put this line in mapinfo instead:");
1006 LOG_INFO(" clientsettemp_for_type all ", argv(1), " ", argv(2));
1007 }
1008 else if(argv(0) != "//")
1009 {
1010 LOG_INFO("Found ^1UNSUPPORTED^7 set command in .cfg file; put this line in mapinfo instead:");
1011 LOG_INFO(" clientsettemp_for_type all ", argv(0), " ", argv(1));
1012 }
1013 }
1014 fclose(fd);
1015 }
1016 }
1017
1019
1020 Nagger_Init();
1021
1022 // set up information replies for clients and server to use
1026 bool records_available = false;
1027 for(int i = 0; i < 10; ++i)
1028 {
1029 string s = getrecords(i);
1030 if (s != "")
1031 {
1032 records_reply[i] = strzone(s);
1033 records_available = true;
1034 }
1035 }
1036 if (!records_available)
1037 records_reply[0] = "No records available for the current gametype.\n";
1040
1041 // begin other init
1045
1046 CheatInit();
1047
1048 if (!wantrestart) localcmd("\n_sv_hook_gamestart ", GetGametype(), "\n");
1049
1050 // fill sv_curl_serverpackages from .serverpackage files
1052 {
1053 string s = "csprogs-" WATERMARK ".dat";
1054 // remove automatically managed files from the list to prevent duplicates
1055 for (int i = 0, n = tokenize_console(cvar_string("sv_curl_serverpackages")); i < n; ++i)
1056 {
1057 string pkg = argv(i);
1058 if (startsWith(pkg, "csprogs-")) continue;
1059 if (endsWith(pkg, "-serverpackage.txt")) continue;
1060 if (endsWith(pkg, ".serverpackage")) continue; // OLD legacy
1061 s = cons(s, pkg);
1062 }
1063 // add automatically managed files to the list
1064 #define X(match) MACRO_BEGIN \
1065 int fd = search_begin(match, true, false); \
1066 if (fd >= 0) \
1067 { \
1068 for (int i = 0, j = search_getsize(fd); i < j; ++i) \
1069 { \
1070 s = cons(s, search_getfilename(fd, i)); \
1071 } \
1072 search_end(fd); \
1073 } \
1074 MACRO_END
1075 X("*-serverpackage.txt");
1076 X("*.serverpackage");
1077 #undef X
1078 cvar_set("sv_curl_serverpackages", s);
1079 }
1080
1081 // MOD AUTHORS: change this, and possibly remove a few of the blocks below to ignore certain changes
1082 modname = "Xonotic";
1083 // physics/balance/config changes that count as mod
1084 if(cvar_string("g_mod_physics") != cvar_defstring("g_mod_physics"))
1085 modname = cvar_string("g_mod_physics");
1086 if(cvar_string("g_mod_balance") != cvar_defstring("g_mod_balance") && cvar_string("g_mod_balance") != "Testing")
1087 modname = cvar_string("g_mod_balance");
1088 if(cvar_string("g_mod_config") != cvar_defstring("g_mod_config"))
1089 modname = cvar_string("g_mod_config");
1090 // extra mutators that deserve to count as mod
1091 MUTATOR_CALLHOOK(SetModname, modname);
1092 modname = M_ARGV(0, string);
1093
1094 // save it for later
1096
1097 WinningConditionHelper(this); // set worldstatus
1098
1099 if (autocvar_sv_autopause && autocvar_sv_dedicated && !wantrestart)
1100 // INITPRIO_LAST is too soon: bots either didn't join yet or didn't leave yet, see: bot_fixcount()
1102
1103 // load entity data outputted by create_scrshot_ent
1104 string filename = strcat("data/", mapname, "_scrshot_ent.txt");
1105 if(!find(NULL, classname, "info_autoscreenshot") && fexists(filename))
1106 loadfromfile(filename);
1107
1110}
1111
1113{
1114 //makestatic (this); // Who the f___ did that?
1115 delete(this);
1116}
1117
1118bool MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, int attempts, float maxaboveground, float minviewdistance, bool frompos)
1119{
1120 float m = e.dphitcontentsmask;
1121 e.dphitcontentsmask = goodcontents | badcontents;
1122
1123 vector org = boundmin;
1124 vector delta = boundmax - boundmin;
1125
1126 vector start, end;
1127 start = end = org;
1128 int j; // used after the loop
1129 for(j = 0; j < attempts; ++j)
1130 {
1131 start.x = org.x + random() * delta.x;
1132 start.y = org.y + random() * delta.y;
1133 start.z = org.z + random() * delta.z;
1134
1135 // rule 1: start inside world bounds, and outside
1136 // solid, and don't start from somewhere where you can
1137 // fall down to evil
1138 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1139 if (trace_fraction >= 1)
1140 continue;
1141 if (trace_startsolid)
1142 continue;
1143 if (trace_dphitcontents & badcontents)
1144 continue;
1145 if (trace_dphitq3surfaceflags & badsurfaceflags)
1146 continue;
1147
1148 // rule 2: if we are too high, lower the point
1149 if (trace_fraction * delta.z > maxaboveground)
1150 start = trace_endpos + '0 0 1' * maxaboveground;
1151 vector enddown = trace_endpos;
1152
1153 // rule 3: make sure we aren't outside the map. This only works
1154 // for somewhat well formed maps. A good rule of thumb is that
1155 // the map should have a convex outside hull.
1156 // these can be traceLINES as we already verified the starting box
1157 vector mstart = start + 0.5 * (e.mins + e.maxs);
1158 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1159 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1160 continue;
1161 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1162 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1163 continue;
1164 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1165 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1166 continue;
1167 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1168 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1169 continue;
1170 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1171 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1172 continue;
1173
1174 // rule 4: we must "be seen" by some spawnpoint or item
1175 // Note that checkpvs from mstart to item can detect visibility if mstart is behind
1176 // patch brushes (curved walls) that don't block visibility from the outside, however
1177 // the next traceline from item to mstart correctly detects invisibility in this case
1178 entity sp = NULL;
1179 if(frompos)
1180 {
1181 if((traceline(e.origin, mstart, MOVE_NORMAL, e), trace_fraction) >= 1)
1182 sp = e;
1183 }
1184 if(!sp)
1185 {
1186 IL_EACH(g_spawnpoints, checkpvs(mstart, it),
1187 {
1188 if((traceline(it.origin, mstart, MOVE_NORMAL, e), trace_fraction) >= 1)
1189 {
1190 sp = it;
1191 break;
1192 }
1193 });
1194 }
1195 if(!sp)
1196 {
1197 int items_checked = 0;
1198 IL_EACH(g_items, checkpvs(mstart, it),
1199 {
1200 if((traceline(it.origin + (it.mins + it.maxs) * 0.5, mstart, MOVE_NORMAL, e), trace_fraction) >= 1)
1201 {
1202 sp = it;
1203 break;
1204 }
1205
1206 ++items_checked;
1207 if(items_checked >= attempts)
1208 break; // sanity
1209 });
1210
1211 if(!sp)
1212 continue;
1213 }
1214
1215 float vlen_delta = vlen(delta);
1216 // find a random vector to "look at"
1217 end.x = org.x + random() * delta.x;
1218 end.y = org.y + random() * delta.y;
1219 end.z = org.z + random() * delta.z;
1220 end = start + normalize(end - start) * vlen_delta;
1221
1222 // rule 4: start TO end must not be too short
1223 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1225 continue;
1226 if(trace_fraction < minviewdistance / vlen_delta)
1227 continue;
1228
1229 // rule 5: don't want to look at sky
1231 continue;
1232
1233 // rule 6: we must not end up in trigger_hurt
1234 if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1235 continue;
1236
1237 break;
1238 }
1239
1240 e.dphitcontentsmask = m;
1241
1242 if(j < attempts)
1243 {
1244 setorigin(e, start);
1245 e.angles = vectoangles(end - start);
1246 LOG_DEBUG("Needed ", ftos(j + 1), " attempts");
1247 return true;
1248 }
1249 return false;
1250}
1251
1252float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1253{
1254 return MoveToRandomLocationWithinBounds(e, world.mins, world.maxs, goodcontents, badcontents, badsurfaceflags, attempts, maxaboveground, minviewdistance, false);
1255}
1256
1257/*
1258===============================================================================
1259
1260RULES
1261
1262===============================================================================
1263*/
1264
1265void DumpStats(float final)
1266{
1267 float file;
1268 string s;
1269 float to_console;
1270 float to_eventlog;
1271 float to_file;
1272 float i;
1273
1274 to_console = autocvar_sv_logscores_console;
1275 to_eventlog = autocvar_sv_eventlog;
1277
1278 if(!final)
1279 {
1280 to_console = true; // always print printstats replies
1281 to_eventlog = false; // but never print them to the event log
1282 }
1283
1284 if(to_eventlog)
1286 to_console = false; // otherwise we get the output twice
1287
1288 if(final)
1289 s = ":scores:";
1290 else
1291 s = ":status:";
1292 s = strcat(s, GetGametype(), "_", GetMapname(), ":", ftos(rint(time)));
1293
1294 if(to_console)
1295 LOG_HELP(s);
1296 if(to_eventlog)
1297 GameLogEcho(s);
1298
1299 file = -1;
1300 if(to_file)
1301 {
1303 if(file == -1)
1304 to_file = false;
1305 else
1306 fputs(file, strcat(s, "\n"));
1307 }
1308
1309 s = strcat(":labels:player:", GetPlayerScoreString(NULL, 0));
1310 if(to_console)
1311 LOG_HELP(s);
1312 if(to_eventlog)
1313 GameLogEcho(s);
1314 if(to_file)
1315 fputs(file, strcat(s, "\n"));
1316
1318 s = strcat(":player:see-labels:", GetPlayerScoreString(it, 0), ":");
1319 s = strcat(s, ftos(rint(time - CS(it).jointime)), ":");
1320 if(IS_PLAYER(it) || INGAME_JOINED(it))
1321 s = strcat(s, ftos(it.team), ":");
1322 else
1323 s = strcat(s, "spectator:");
1324
1325 if(to_console)
1326 LOG_HELP(s, playername(it.netname, it.team, false));
1327 if(to_eventlog)
1328 GameLogEcho(strcat(s, ftos(it.playerid), ":", playername(it.netname, it.team, false)));
1329 if(to_file)
1330 fputs(file, strcat(s, playername(it.netname, it.team, false), "\n"));
1331 });
1332
1333 if(teamplay)
1334 {
1335 s = strcat(":labels:teamscores:", GetTeamScoreString(0, 0));
1336 if(to_console)
1337 LOG_HELP(s);
1338 if(to_eventlog)
1339 GameLogEcho(s);
1340 if(to_file)
1341 fputs(file, strcat(s, "\n"));
1342
1343 for(i = 1; i < 16; ++i)
1344 {
1345 s = strcat(":teamscores:see-labels:", GetTeamScoreString(i, 0));
1346 s = strcat(s, ":", ftos(i));
1347 if(to_console)
1348 LOG_HELP(s);
1349 if(to_eventlog)
1350 GameLogEcho(s);
1351 if(to_file)
1352 fputs(file, strcat(s, "\n"));
1353 }
1354 }
1355
1356 if(to_console)
1357 LOG_HELP(":end");
1358 if(to_eventlog)
1359 GameLogEcho(":end");
1360 if(to_file)
1361 {
1362 fputs(file, ":end\n");
1363 fclose(file);
1364 }
1365}
1366
1367// it should be called by gametypes where players can join a team but have to wait before playing
1368// it puts players who joined too late (without being able to play) back to spectators
1369// if prev_team_field is not team it also puts players who previously switched team (without being
1370// able to play on the new team) back to previous team
1372{
1373 bool fix_team = (teamplay && prev_team_field != team);
1374 FOREACH_CLIENT(true,
1375 {
1376 if (!IS_PLAYER(it) && INGAME_JOINING(it))
1377 {
1379 PutObserverInServer(it, true, false);
1380 bprint(playername(it.netname, it.team, false), " has been moved back to spectator");
1381 it.winning = false;
1382 }
1383 else if (fix_team && INGAME_JOINED(it) && it.(prev_team_field) && it.team != it.(prev_team_field))
1384 {
1386 if (MoveToTeam(it, Team_TeamToIndex(it.(prev_team_field)), 6))
1387 {
1388 string pl_name = playername(it.netname, it.team, false);
1389 bprint(pl_name, " has been moved back to the ", Team_ColoredFullName(it.team));
1390 }
1391 it.winning = (it.team == WinningConditionHelper_winnerteam);
1392 }
1393 });
1394}
1395
1400
1401/*
1402go to the next level for deathmatch
1403only called if a time or frag limit has expired
1404*/
1406{
1407 cvar_set("_endmatch", "0");
1408 game_stopped = true;
1409 intermission_running = true; // game over
1410
1411 // enforce a wait time before allowing changelevel
1412 if(player_count > 0)
1414 else
1416
1417 /*
1418 WriteByte (MSG_ALL, SVC_CDTRACK);
1419 WriteByte (MSG_ALL, 3);
1420 WriteByte (MSG_ALL, 3);
1421 // done in FixIntermission
1422 */
1423
1424 //pos = FindIntermission ();
1425
1426 VoteReset(true);
1427
1428 MUTATOR_CALLHOOK(MatchEnd_BeforeScores);
1429
1430 DumpStats(true);
1431
1432 // send statistics
1435
1436 Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_Null); // kill all centerprints now
1437
1439 GameLogEcho(":gameover");
1440
1441 GameLogClose();
1442
1443 int winner_team = 0;
1444 FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), {
1446 if(it.winning)
1447 {
1448 if (teamplay && !winner_team)
1449 {
1450 winner_team = it.team;
1451 bprint(Team_ColorCode(winner_team), Team_ColorName_Upper(winner_team), "^7 team wins the match\n");
1452 }
1453 bprint(playername(it.netname, it.team, false), " ^7wins\n");
1454 }
1455 });
1456
1458
1461
1462 MUTATOR_CALLHOOK(MatchEnd);
1463
1464 localcmd("\nsv_hook_gameend\n");
1465}
1466
1467
1469{
1470 // Check first whether normal overtimes could be added before initiating suddendeath mode
1471 // - for this timelimit_overtime needs to be >0 of course
1472 // - also check the winning condition calculated in the previous frame and only add normal overtime
1473 // again, if at the point at which timelimit would be extended again, still no winner was found
1477 {
1478 return 1; // need to call InitiateOvertime later
1479 }
1480 else
1481 {
1483 {
1485 {
1486 checkrules_suddendeathend = time; // no suddendeath in campaign
1487 }
1488 else
1489 {
1492 }
1495 }
1496 return 0;
1497 }
1498}
1499
1500void InitiateOvertime() // ONLY call this if InitiateSuddenDeath returned true
1501{
1503 // NOTE: here overtimes can never be < 0 so it can be safely sent as (unsigned) int stat; we ignore
1504 // the upper limit of OVERTIME_SUDDENDEATH - 1 = 16777215 - 1 that in practice can never be reached in game
1506 //add one more overtime by simply extending the timelimit
1508 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_TIME, autocvar_timelimit_overtime * 60);
1509}
1510
1511float GetWinningCode(float fraglimitreached, float equality)
1512{
1513 if(autocvar_g_campaign == 1)
1514 {
1515 if(fraglimitreached)
1516 return WINNING_YES;
1517 else
1518 return WINNING_NO;
1519 }
1520 else
1521 {
1522 if(equality)
1523 {
1524 if(fraglimitreached)
1526 else
1527 return WINNING_NEVER;
1528 }
1529 else
1530 {
1531 if(fraglimitreached)
1532 return WINNING_YES;
1533 else
1534 return WINNING_NO;
1535 }
1536 }
1537}
1538
1539// set the .winning flag for exactly those players with a given field value
1540void SetWinners(.float field, float value)
1541{
1542 FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), { it.winning = (it.(field) == value); });
1543}
1544
1545// set the .winning flag for those players with a given field value
1546void AddWinners(.float field, float value)
1547{
1548 FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), {
1549 if(it.(field) == value)
1550 it.winning = 1;
1551 });
1552}
1553
1554// clear the .winning flags
1556{
1557 FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), { it.winning = 0; });
1558}
1559
1561float WinningCondition_Scores(float limit, float leadlimit)
1562{
1563 // TODO make everything use THIS winning condition (except LMS)
1565
1566 if(teamplay)
1567 {
1568 for (int i = 1; i < 5; ++i)
1569 {
1572 }
1573 }
1574
1575 ClearWinners();
1580
1582 {
1585 limit = -limit;
1586 }
1587
1589 leadlimit = 0; // not supported in this mode
1590
1591 if(MUTATOR_CALLHOOK(Scores_CountFragsRemaining))
1592 {
1593 float fragsleft;
1595 {
1596 fragsleft = 1;
1597 }
1598 else
1599 {
1600 fragsleft = FLOAT_MAX;
1601 float leadingfragsleft = FLOAT_MAX;
1602 if (limit)
1603 fragsleft = limit - WinningConditionHelper_topscore;
1604 if (leadlimit)
1606
1607 if (limit && leadlimit && autocvar_leadlimit_and_fraglimit)
1608 fragsleft = max(fragsleft, leadingfragsleft);
1609 else
1610 fragsleft = min(fragsleft, leadingfragsleft);
1611 }
1612
1613 if (fragsleft_last != fragsleft) // do not announce same remaining frags multiple times
1614 {
1615 if (fragsleft == 1)
1616 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_REMAINING_FRAG_1);
1617 else if (fragsleft == 2)
1618 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_REMAINING_FRAG_2);
1619 else if (fragsleft == 3)
1620 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_REMAINING_FRAG_3);
1621
1622 fragsleft_last = fragsleft;
1623 }
1624 }
1625
1626 bool fraglimit_reached = (limit && WinningConditionHelper_topscore >= limit);
1627 bool leadlimit_reached = (leadlimit && WinningConditionHelper_topscore - WinningConditionHelper_secondscore >= leadlimit);
1628
1629 bool limit_reached;
1630 // only respect leadlimit_and_fraglimit when both limits are set or the game will never end
1631 if (limit && leadlimit && autocvar_leadlimit_and_fraglimit)
1632 limit_reached = (fraglimit_reached && leadlimit_reached);
1633 else
1634 limit_reached = (fraglimit_reached || leadlimit_reached);
1635
1636 return GetWinningCode(
1637 WinningConditionHelper_topscore && limit_reached,
1639 );
1640}
1641
1643{
1644 if(have_team_spawns <= 0)
1645 return WINNING_NO;
1646
1648 return WINNING_NO;
1649
1651 return WINNING_NO;
1652
1653 for (int i = 1; i < 5; ++i)
1654 {
1656 }
1657
1658 FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
1659 {
1660 if (Team_IsValidTeam(it.team))
1661 {
1662 Team_SetTeamScore(Team_GetTeam(it.team), 1);
1663 }
1664 });
1665
1666 IL_EACH(g_spawnpoints, true,
1667 {
1668 if (Team_IsValidTeam(it.team))
1669 {
1670 Team_SetTeamScore(Team_GetTeam(it.team), 1);
1671 }
1672 });
1673
1674 ClearWinners();
1675 float team1_score = Team_GetTeamScore(Team_GetTeamFromIndex(1));
1676 float team2_score = Team_GetTeamScore(Team_GetTeamFromIndex(2));
1677 float team3_score = Team_GetTeamScore(Team_GetTeamFromIndex(3));
1678 float team4_score = Team_GetTeamScore(Team_GetTeamFromIndex(4));
1679 if(team1_score + team2_score + team3_score + team4_score == 0)
1680 {
1681 checkrules_equality = true;
1682 return WINNING_YES;
1683 }
1684 else if(team1_score + team2_score + team3_score + team4_score == 1)
1685 {
1686 float t, i;
1687 if(team1_score)
1688 t = 1;
1689 else if(team2_score)
1690 t = 2;
1691 else if(team3_score)
1692 t = 3;
1693 else // if(team4_score)
1694 t = 4;
1696 for(i = 0; i < MAX_TEAMSCORE; ++i)
1697 {
1698 for (int j = 1; j <= NUM_TEAMS; ++j)
1699 {
1700 if (t == j)
1701 {
1702 continue;
1703 }
1704 if (!TeamBalance_IsTeamAllowed(balance, j))
1705 {
1706 continue;
1707 }
1709 }
1710 }
1711
1712 AddWinners(team, t);
1713 return WINNING_YES;
1714 }
1715 else
1716 return WINNING_NO;
1717}
1718
1719/*
1720============
1721CheckRules_World
1722
1723Exit deathmatch games upon conditions
1724============
1725*/
1727{
1728 VoteThink();
1729 MapVote_Think();
1730
1732
1733 if (intermission_running) // someone else quit the game already
1734 {
1735 if(player_count == 0) // Nobody there? Then let's go to the next map
1736 MapVote_Start();
1737 // this will actually check the player count in the next frame
1738 // again, but this shouldn't hurt
1739 return;
1740 }
1741
1742 float timelimit = autocvar_timelimit * 60;
1743 float fraglimit = autocvar_fraglimit;
1744 float leadlimit = autocvar_leadlimit;
1745 if (leadlimit < 0) leadlimit = 0;
1746
1747 if(warmup_stage || time <= game_starttime) // NOTE: this is <= to prevent problems in the very tic where the game starts
1748 {
1749 if(timelimit > 0)
1750 timelimit = 0; // timelimit is not made for warmup
1751 if(fraglimit > 0)
1752 fraglimit = 0; // no fraglimit for now
1753 leadlimit = 0; // no leadlimit for now
1754 }
1755
1756 if (autocvar__endmatch || timelimit < 0)
1757 {
1758 // endmatch
1759 NextLevel();
1760 return;
1761 }
1762
1763 if(timelimit > 0)
1764 timelimit += game_starttime;
1765
1766 int overtimes_prev = overtimes;
1767 int wantovertime = 0;
1768
1770 {
1772 {
1775 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_RACE_FINISHLAP);
1776 else
1777 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_FRAG);
1778 }
1779 }
1780 else
1781 {
1782 if (timelimit && time >= timelimit)
1783 {
1784 if(g_race && (g_race_qualifying == 2) && timelimit > 0)
1785 {
1786 float totalplayers;
1787 float playerswithlaps;
1788 float readyplayers;
1789 totalplayers = playerswithlaps = readyplayers = 0;
1791 ++totalplayers;
1792 if(GameRules_scoring_add(it, RACE_FASTEST, 0))
1793 ++playerswithlaps;
1794 if(it.ready)
1795 ++readyplayers;
1796 });
1797
1798 // at least 2 of the players have completed a lap: start the RACE
1799 // otherwise, the players should end the qualifying on their own
1800 if(readyplayers || playerswithlaps >= 2)
1801 {
1803 ReadyRestart(true); // go to race
1804 return;
1805 }
1806 else
1807 wantovertime |= InitiateSuddenDeath();
1808 }
1809 else
1810 wantovertime |= InitiateSuddenDeath();
1811 }
1812 }
1813
1815 {
1816 NextLevel();
1817 return;
1818 }
1819
1820 int checkrules_status = WinningCondition_RanOutOfSpawns();
1821 if(checkrules_status == WINNING_YES)
1822 bprint("Hey! Someone ran out of spawns!\n");
1823 else if(MUTATOR_CALLHOOK(CheckRules_World, checkrules_status, timelimit, fraglimit))
1824 checkrules_status = M_ARGV(0, float);
1825 else
1826 checkrules_status = WinningCondition_Scores(fraglimit, leadlimit);
1827
1828 if(checkrules_status == WINNING_STARTSUDDENDEATHOVERTIME)
1829 {
1830 checkrules_status = WINNING_NEVER;
1832 wantovertime |= InitiateSuddenDeath();
1833 }
1834
1835 if(checkrules_status == WINNING_NEVER)
1836 // equality cases! Nobody wins if the overtime ends in a draw.
1837 ClearWinners();
1838
1839 if(wantovertime)
1840 {
1841 if(checkrules_status == WINNING_NEVER)
1843 else
1844 checkrules_status = WINNING_YES;
1845 }
1846
1848 if(checkrules_status != WINNING_NEVER || time >= checkrules_suddendeathend)
1849 checkrules_status = WINNING_YES;
1850
1851 if(checkrules_status == WINNING_YES)
1852 {
1853 if (overtimes == OVERTIME_SUDDENDEATH && overtimes != overtimes_prev)
1854 {
1855 // if suddendeathend overtime has just begun, revert it
1857 overtimes = overtimes_prev;
1858 }
1859 //print("WINNING\n");
1860 NextLevel();
1861 }
1862}
1863
1864int want_weapon(entity weaponinfo, int allguns)
1865{
1866 int d = 0;
1867 bool allow_mutatorblocked = false;
1868
1869 if(!weaponinfo.m_id)
1870 return 0;
1871
1872 bool mutator_returnvalue = MUTATOR_CALLHOOK(WantWeapon, weaponinfo, d, allguns, allow_mutatorblocked);
1873 d = M_ARGV(1, float);
1874 allguns = M_ARGV(2, int);
1875 allow_mutatorblocked = M_ARGV(3, bool);
1876
1877 if(allguns == 1)
1878 d = boolean(!(weaponinfo.spawnflags & WEP_FLAG_HIDDEN));
1879 else if(allguns == 2)
1880 d = boolean((weaponinfo.spawnflags & WEP_FLAG_NORMAL) && !(weaponinfo.spawnflags & WEP_FLAG_HIDDEN));
1881 else if(!mutator_returnvalue)
1882 d = !(!weaponinfo.weaponstart);
1883
1884 if(!allow_mutatorblocked && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
1885 d = 0;
1886
1887 float t = weaponinfo.weaponstartoverride;
1888
1889 //LOG_INFOF("want_weapon: %s - d: %d t: %d\n", weaponinfo.netname, d, t);
1890
1891 // bit order in t:
1892 // 1: want or not
1893 // 2: is default?
1894 // 4: is set by default?
1895 if(t < 0)
1896 t = 4 | (3 * d);
1897 else
1898 t |= (2 * d);
1899
1900 return t;
1901}
1902
1905{
1906 WepSet ret = '0 0 0';
1907 FOREACH(Weapons, it != WEP_Null, {
1908 int w = want_weapon(it, false);
1909 if (w & 1)
1910 ret |= it.m_wepset;
1911 });
1912 return ret;
1913}
1914
1916{
1917 WepSet ret = '0 0 0';
1918 FOREACH(Weapons, it != WEP_Null, {
1919 if (!(it.spawnflags & (WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN)))
1920 ret |= it.m_wepset;
1921 });
1922 return ret;
1923}
1924
1926{
1927 WepSet ret = '0 0 0';
1928 FOREACH(Weapons, it != WEP_Null,
1929 {
1930 ret |= it.m_wepset;
1931 });
1932 return ret;
1933}
1934
1936{
1937 WepSet ret = '0 0 0';
1938 FOREACH(Weapons, it != WEP_Null, {
1939 if ((it.spawnflags & WEP_FLAG_NORMAL) && !(it.spawnflags & (WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN)))
1940 ret |= it.m_wepset;
1941 });
1942 return ret;
1943}
1944
1946{
1947 if (weaponsInMapAll)
1948 {
1950 }
1951 else
1952 {
1953 // if no weapons are available on the map, just fall back to all weapons arena
1955 }
1956}
1957
1959{
1960 if (weaponsInMapAll)
1961 {
1963 }
1964 else
1965 {
1966 // if no weapons are available on the map, just fall back to devall weapons arena
1968 }
1969}
1970
1972{
1973 if (weaponsInMapAll)
1974 {
1976 }
1977 else
1978 {
1979 // if no weapons are available on the map, just fall back to most weapons arena
1981 }
1982}
1983
1985{
1986 // initialize starting values for players
1987 start_weapons = '0 0 0';
1988 start_weapons_default = '0 0 0';
1989 start_weapons_defaultmask = '0 0 0';
1990 start_items = 0;
1992 start_ammo_nails = 0;
1994 start_ammo_cells = 0;
1995 if (random_start_ammo == NULL)
1996 {
1998 }
1999 start_health = cvar("g_balance_health_start");
2000 start_armorvalue = cvar("g_balance_armor_start");
2001
2002 g_weaponarena = 0;
2003 g_weaponarena_weapons = '0 0 0';
2004
2005 string s = cvar_string("g_weaponarena");
2006
2007 MUTATOR_CALLHOOK(SetWeaponArena, s);
2008 s = M_ARGV(0, string);
2009
2010 if (s == "0" || s == "")
2011 {
2012 // no arena
2013 }
2014 else if (s == "off")
2015 {
2016 // forcibly turn off weaponarena
2017 }
2018 else if (s == "all" || s == "1")
2019 {
2020 g_weaponarena = 1;
2021 g_weaponarena_list = "All Weapons Arena";
2023 }
2024 else if (s == "devall")
2025 {
2026 g_weaponarena = 1;
2027 g_weaponarena_list = "Dev All Weapons Arena";
2029 }
2030 else if (s == "most")
2031 {
2032 g_weaponarena = 1;
2033 g_weaponarena_list = "Most Weapons Arena";
2035 }
2036 else if (s == "all_available")
2037 {
2038 g_weaponarena = 1;
2039 g_weaponarena_list = "All Available Weapons Arena";
2040
2041 // this needs to run after weaponsInMapAll is initialized
2043 }
2044 else if (s == "devall_available")
2045 {
2046 g_weaponarena = 1;
2047 g_weaponarena_list = "Dev All Available Weapons Arena";
2048
2049 // this needs to run after weaponsInMapAll is initialized
2051 }
2052 else if (s == "most_available")
2053 {
2054 g_weaponarena = 1;
2055 g_weaponarena_list = "Most Available Weapons Arena";
2056
2057 // this needs to run after weaponsInMapAll is initialized
2059 }
2060 else if (s == "none")
2061 {
2062 g_weaponarena = 1;
2063 g_weaponarena_list = "No Weapons Arena";
2064 }
2065 else
2066 {
2067 g_weaponarena = 1;
2068 float t = tokenize_console(s);
2069 g_weaponarena_list = "";
2070 for (int j = 0; j < t; ++j)
2071 {
2072 s = argv(j);
2073 Weapon wep = Weapon_from_name(s);
2074 if(wep != WEP_Null)
2075 {
2076 g_weaponarena_weapons |= (wep.m_wepset);
2078 }
2079 }
2080 if (g_weaponarena_list != "") // remove trailing " & "
2082 else // no valid weapon found
2083 g_weaponarena_list = "No Weapons Arena";
2084 }
2085
2086 if (g_weaponarena)
2087 {
2088 g_weapon_stay = 0; // incompatible
2092 }
2093 else
2094 {
2095 FOREACH(Weapons, it != WEP_Null, {
2096 int w = want_weapon(it, false);
2097 WepSet s = it.m_wepset;
2098 if(w & 1)
2099 start_weapons |= s;
2100 if(w & 2)
2102 if(w & 4)
2104 });
2105 }
2106
2107 if(cvar("g_balance_superweapons_time") < 0)
2109
2110 if(!cvar("g_use_ammunition"))
2112
2113 start_ammo_shells = cvar("g_start_ammo_shells");
2114 start_ammo_nails = cvar("g_start_ammo_nails");
2115 start_ammo_rockets = cvar("g_start_ammo_rockets");
2116 start_ammo_cells = cvar("g_start_ammo_cells");
2117 start_ammo_fuel = cvar("g_start_ammo_fuel");
2118 random_start_weapons_count = cvar("g_random_start_weapons_count");
2119 SetResource(random_start_ammo, RES_SHELLS, cvar("g_random_start_shells"));
2120 SetResource(random_start_ammo, RES_BULLETS, cvar("g_random_start_bullets"));
2121 SetResource(random_start_ammo, RES_ROCKETS, cvar("g_random_start_rockets"));
2122 SetResource(random_start_ammo, RES_CELLS, cvar("g_random_start_cells"));
2123
2134
2135 if (!g_weaponarena)
2136 {
2137 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
2138 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
2139 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
2140 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
2141 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
2142 warmup_start_health = cvar("g_warmup_start_health");
2143 warmup_start_armorvalue = cvar("g_warmup_start_armor");
2144 warmup_start_weapons = '0 0 0';
2147 FOREACH(Weapons, it != WEP_Null, {
2149 WepSet s = it.m_wepset;
2150 if(w & 1)
2152 if(w & 2)
2154 if(w & 4)
2156 });
2157 }
2158
2160 start_items |= ITEM_Jetpack.m_itemid;
2161
2162 MUTATOR_CALLHOOK(SetStartItems);
2163
2164 if (start_items & ITEM_Jetpack.m_itemid)
2165 {
2166 start_items |= ITEM_FuelRegen.m_itemid;
2167 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
2168 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
2169 }
2170
2176 SetResource(random_start_ammo, RES_SHELLS, max(0, GetResource(random_start_ammo, RES_SHELLS)));
2177 SetResource(random_start_ammo, RES_BULLETS, max(0, GetResource(random_start_ammo, RES_BULLETS)));
2178 SetResource(random_start_ammo, RES_ROCKETS, max(0, GetResource(random_start_ammo, RES_ROCKETS)));
2179 SetResource(random_start_ammo, RES_CELLS, max(0, GetResource(random_start_ammo, RES_CELLS)));
2180
2186}
2187
2189{
2191 if(cvar("sv_allow_fullbright"))
2193
2195 if(cvar("sv_forbid_pickuptimer"))
2197
2198 sv_ready_restart_after_countdown = cvar("sv_ready_restart_after_countdown");
2199
2200 if(cvar("g_campaign"))
2201 warmup_stage = 0; // no warmup during campaign
2202 else
2203 {
2206 warmup_limit = -1; // don't start until there's enough players
2207 else if (warmup_stage == 1)
2208 {
2209 // this code is duplicated in ReadyCount()
2210 warmup_limit = cvar("g_warmup_limit");
2211 if(warmup_limit == 0)
2213 }
2214 }
2215
2216 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
2217 if(!g_weapon_stay)
2218 g_weapon_stay = cvar("g_weapon_stay");
2219
2220 MUTATOR_CALLHOOK(ReadLevelCvars);
2221
2223 game_starttime = time + cvar("g_start_delay");
2224
2225 FOREACH(Weapons, it != WEP_Null, { it.wr_init(it); });
2226
2228}
2229
2230void InitializeEntity(entity e, void(entity this) func, int order)
2231{
2232 entity prev, cur;
2233
2234 if (!e || e.initialize_entity)
2235 {
2236 // make a proxy initializer entity
2237 entity e_old = e;
2238 e = new(initialize_entity);
2239 e.enemy = e_old;
2240 }
2241
2242 e.initialize_entity = func;
2243 e.initialize_entity_order = order;
2244
2246 prev = NULL;
2247 for (;;)
2248 {
2249 if (!cur || cur.initialize_entity_order > order)
2250 {
2251 // insert between prev and cur
2252 if (prev)
2253 prev.initialize_entity_next = e;
2254 else
2256 e.initialize_entity_next = cur;
2257 return;
2258 }
2259 prev = cur;
2260 cur = cur.initialize_entity_next;
2261 }
2262}
2264{
2265 entity startoflist = initialize_entity_first;
2268 for (entity e = startoflist; e; e = e.initialize_entity_next)
2269 {
2270 e.remove_except_protected_forbidden = 1;
2271 }
2272 for (entity e = startoflist; e; )
2273 {
2274 e.remove_except_protected_forbidden = 0;
2275 e.initialize_entity_order = 0;
2276 entity next = e.initialize_entity_next;
2277 e.initialize_entity_next = NULL;
2278 var void(entity this) func = e.initialize_entity;
2279 e.initialize_entity = func_null;
2280 if (e.classname == "initialize_entity")
2281 {
2282 entity wrappee = e.enemy;
2283 builtin_remove(e);
2284 e = wrappee;
2285 }
2286 //dprint("Delayed initialization: ", e.classname, "\n");
2287 if (func)
2288 {
2289 func(e);
2290 }
2291 else
2292 {
2293 eprint(e);
2294 backtrace(strcat("Null function in: ", e.classname, "\n"));
2295 }
2296 e = next;
2297 }
2299}
2300
2301// originally ported from DP's droptofloor() builtin
2302// TODO: make a common function for the client-side?
2303// bones_was_here: when we have a use case for it, yes
2305{
2306 int nudgeresult;
2307
2308 if(!this || wasfreed(this))
2309 {
2310 LOG_WARN("DropToFloor_QC: can not modify free entity");
2311 return;
2312 }
2313
2314 /* Prior to sv_legacy_bbox_expand 0, both droptofloor and nudgeoutofsolid_OrFallback were done for items
2315 * using box '-16 -16 0' '16 16 48' (without the FL_ITEM expansion applied),
2316 * which often resulted in bboxes partially in solids once expansion was applied.
2317 * We don't want bboxes in solids (bad for gameplay and culling),
2318 * but we also don't want items to land on a "skirting board" or the base of a sloping wall.
2319 * For initial nudgeoutofsolid_OrFallback and droptofloor stages we use a small box
2320 * so they fall as far and in the same place as they traditionally would,
2321 * then we nudge the full size box out of solid, in a direction appropriate for the plane(s).
2322 */
2323 vector saved_mins = this.mins; // gmqcc's used-uninitialized check doesn't handle
2324 vector saved_maxs = this.maxs; // making these assignments FL_ITEM conditional.
2325 if (this.flags & FL_ITEM)
2326 {
2327 // Using the Q3 bbox for best compatibility with all maps, except...
2328 this.mins.x = -15;
2329 this.mins.y = -15;
2330 this.maxs.x = 15;
2331 this.maxs.y = 15;
2332 this.maxs.z = this.mins.z + 30; // ...Nex, Xon and Quake use a different vertical offset, see also: StartItem()
2333 }
2334
2335 /* NOTE: sv_gameplayfix_droptofloorstartsolid_nudgetocorrect isn't checked, so it won't need to be networked to CSQC.
2336 * It was enabled by default in all Xonotic releases and in Nexuiz, so now certain maps depend on it.
2337 * Example: on erbium 0.8.6 the shards @ crylink are too low (in collision with the floor),
2338 * so without this those fall through the floor.
2339 * Q3, Q2 and Quake don't try to move items out of solid.
2340 */
2341 if(!Q3COMPAT_COMMON && autocvar_sv_mapformat_is_quake3) // Xonotic, Nexuiz
2342 {
2343 nudgeresult = nudgeoutofsolid_OrFallback(this);
2344 if (!nudgeresult)
2345 LOG_WARNF("DropToFloor_QC at \"%v\": COULD NOT FIX badly placed entity \"%s\" before drop", this.origin, this.classname);
2346 else if (nudgeresult > 0)
2347 LOG_WARNF("DropToFloor_QC at \"%v\": FIXED badly placed entity \"%s\" before drop", this.origin, this.classname);
2348 }
2350 {
2351 if (this.flags & FL_ITEM)
2352 this.origin.z += 6;
2353 else
2354 this.origin.z += 1; // monsters 1, misc_explobox 2 but we don't support those currently
2355 }
2356
2357 vector end = this.origin;
2359 end.z -= 4096;
2361 end.z -= 128;
2362 else
2363 end.z -= 256; // Quake, QuakeWorld
2364 tracebox(this.origin, this.mins, this.maxs, end, MOVE_NOMONSTERS, this);
2365
2367 {
2368 // Quake games just delete badly placed items (and misc_explobox)...
2369 if (this.flags & FL_ITEM)
2370 {
2371 LOG_WARNF("DropToFloor_QC at \"%v\" (Quake compat): DELETING badly placed item \"%s\"", this.origin, this.classname);
2372 delete(this);
2373 return;
2374 }
2375 // ...not monsters though...
2376 LOG_WARNF("DropToFloor_QC at \"%v\" (Quake compat): badly placed entity \"%s\"", this.origin, this.classname);
2377 }
2379 {
2380 // ...but we can't do that on Q3 maps like jamdm1
2381 // because our tracebox hits things Q3's trace doesn't (patches?).
2382 LOG_WARNF("DropToFloor_QC at \"%v\" (Quake 3 compat): badly placed entity \"%s\"", this.origin, this.classname);
2383 }
2384
2385 /* NOTE: sv_gameplayfix_droptofloorstartsolid (fallback from tracebox to traceline) isn't implemented.
2386 * It was disabled by default in all Xonotic releases and in Nexuiz.
2387 * Q3 doesn't support it (always uses its '-15 -15 -15' '15 15 15' box when dropping items), neither does Quake or Q2.
2388 */
2389
2390 if (!autocvar_sv_mapformat_is_quake2) // Quake, Q3, Nexuiz, Xonotic
2391 // allow to ride movers (or unset if in freefall)
2392 this.groundentity = trace_ent;
2393
2395 // if support is destroyed, keep suspended (gross hack for floating items in various maps)
2396 // bones_was_here: is this for Q1BSP only? Which maps use it? Do we need it at all? Intentions unclear in DP...
2397 this.move_suspendedinair = true;
2398
2399 if (trace_fraction)
2400 this.origin = trace_endpos;
2401
2402 if (this.flags & FL_ITEM)
2403 {
2404 this.mins = saved_mins;
2405 this.maxs = saved_maxs;
2406
2407 // A side effect of using a small box to drop items (and do the initial nudge) is
2408 // the full size box can end up in collision with a sloping floor or terrain model.
2409 nudgeresult = nudgeoutofsolid_OrFallback(this);
2410 // No warns for successful nudge because it would spam about items on slopes/terrain.
2411 }
2412 else if (trace_allsolid && trace_fraction) // dropped using "proper" bbox but never left solid
2413 {
2414 nudgeresult = nudgeoutofsolid_OrFallback(this);
2415 if (nudgeresult > 0)
2416 LOG_WARNF("DropToFloor_QC at \"%v\": FIXED badly placed entity \"%s\" after drop", this.origin, this.classname);
2417 }
2418 else
2419 nudgeresult = -1;
2420
2421 if (!nudgeresult)
2422 if (!Q3COMPAT_COMMON) // to be expected on Q3 maps like gu3-pewter because we use bigger final bboxes
2423 LOG_WARNF("DropToFloor_QC at \"%v\": COULD NOT FIX stuck entity \"%s\" after drop", this.origin, this.classname);
2424
2425 setorigin(this, this.dropped_origin = this.origin);
2426}
2427
2432
2434void RunThink(entity this, float dt)
2435{
2436 // don't let things stay in the past.
2437 // it is possible to start that way by a trigger with a local time.
2439 return;
2440
2441 float oldtime = time; // do we need to save this?
2442
2443 for (int iterations = 0; iterations < 128 && !wasfreed(this); ++iterations)
2444 {
2445 time = max(oldtime, this.nextthink);
2446 this.nextthink = 0;
2447
2448 if(getthink(this))
2449 getthink(this)(this);
2450 // mods often set nextthink to time to cause a think every frame,
2451 // we don't want to loop in that case, so exit if the new nextthink is
2452 // <= the time the qc was told, also exit if it is past the end of the
2453 // frame
2455 break;
2456 }
2457
2458 time = oldtime;
2459}
2460
2463{
2465 return;
2466
2467 IL_EACH(g_moveables, true,
2468 {
2469 if(IS_CLIENT(it) || it.move_movetype == MOVETYPE_PHYSICS)
2470 continue;
2471
2472 //set_movetype(it, it.move_movetype);
2473 // inline the set_movetype function, since this is called a lot
2474 it.movetype = (it.move_qcphysics) ? MOVETYPE_QCENTITY : it.move_movetype;
2475
2476 if(it.move_qcphysics && it.move_movetype != MOVETYPE_NONE)
2478
2479 if(it.movetype >= MOVETYPE_USER_FIRST && it.movetype <= MOVETYPE_USER_LAST) // these cases have no think handling
2480 {
2481 if(it.move_movetype == MOVETYPE_PUSH || it.move_movetype == MOVETYPE_FAKEPUSH)
2482 continue; // these movetypes have no regular think function
2483 // handle thinking here
2484 if (getthink(it) && it.nextthink > 0 && it.nextthink <= time + PHYS_INPUT_TIMELENGTH)
2486 }
2487 });
2488
2490 return;
2491
2492 // make a second pass to see if any ents spawned this frame and make
2493 // sure they run their move/think. this is verified by checking .move_time, which will never be 0 if the entity has moved
2494 // MOVETYPE_NONE is also checked as .move_time WILL be 0 with that movetype
2495 IL_EACH(g_moveables, it.move_qcphysics,
2496 {
2497 if(IS_CLIENT(it) || it.move_time || it.move_movetype == MOVETYPE_NONE || it.move_movetype == MOVETYPE_PHYSICS)
2498 continue;
2499 Movetype_Physics_NoMatchTicrate(it, PHYS_INPUT_TIMELENGTH, false);
2500 });
2501}
2502
2503void systems_update();
2505{
2507
2508 Physics_Frame();
2509
2511 entity e = IS_SPEC(it) ? it.enemy : it;
2512 if (e.typehitsound) {
2513 STAT(TYPEHIT_TIME, it) = time;
2514 } else if (e.killsound) {
2515 STAT(KILL_TIME, it) = time;
2516 } else if (e.hitsound_damage_dealt) {
2517 STAT(HIT_TIME, it) = time;
2518 // NOTE: this is not accurate as client code doesn't need so much accuracy for its purposes
2519 STAT(HITSOUND_DAMAGE_DEALT_TOTAL, it) += ceil(e.hitsound_damage_dealt);
2520 }
2521 });
2522 // add 1 frametime because after this, engine SV_Physics
2523 // increases time by a frametime and then networks the frame
2524 // add another frametime because client shows everything with
2525 // 1 frame of lag (cl_nolerp 0). The last +1 however should not be
2526 // needed!
2527 float altime = time + frametime * (1 + autocvar_g_antilag_nudge);
2528 FOREACH_CLIENT(true, {
2529 it.typehitsound = false;
2530 it.hitsound_damage_dealt = 0;
2531 it.killsound = false;
2532 antilag_record(it, CS(it), altime);
2533 if(it.death_time == time && IS_PLAYER(it) && IS_DEAD(it))
2534 {
2535 // player's bbox gets resized now, instead of in the damage event that killed the player,
2536 // once all the damage events of this frame have been processed with normal size
2537 float h = ceil((it.mins.z + it.maxs.z) * PL_CORPSE_SCALE * 10) / 10;
2538 it.maxs.z = max(h, it.mins.z + 1);
2539 setsize(it, it.mins, it.maxs);
2540 }
2541 });
2542 IL_EACH(g_monsters, true,
2543 {
2544 antilag_record(it, it, altime);
2545 });
2546 IL_EACH(g_projectiles, it.classname == "nade",
2547 {
2548 antilag_record(it, it, altime);
2549 });
2551 IL_ENDFRAME();
2552}
2553
2554
2555/*
2556 * RedirectionThink:
2557 * returns true if redirecting
2558 */
2562{
2563 float clients_found;
2564
2565 if(redirection_target == "")
2566 return false;
2567
2569 {
2570 cvar_set("sv_public", "-2");
2571 redirection_timeout = time + 0.6; // this will only try twice... should be able to keep more clients
2572 if(redirection_target == "self")
2573 bprint("^3SERVER NOTICE:^7 restarting the server\n");
2574 else
2575 bprint("^3SERVER NOTICE:^7 redirecting everyone to ", redirection_target, "\n");
2576 }
2577
2579 return true;
2580
2582
2583 clients_found = 0;
2585 // TODO add timer
2586 LOG_INFO("Redirecting: sending connect command to ", it.netname);
2587 if(redirection_target == "self")
2588 stuffcmd(it, "\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " reconnect\n");
2589 else
2590 stuffcmd(it, strcat("\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " \"connect ", redirection_target, "\"\n"));
2591 ++clients_found;
2592 });
2593
2594 LOG_INFO("Redirecting: ", ftos(clients_found), " clients left.");
2595
2596 if(time > redirection_timeout || clients_found == 0)
2597 localcmd("\nwait; wait; wait; quit\n");
2598
2599 return true;
2600}
2601
2603{
2604 // Loaded from a save game
2605 // some things then break, so let's work around them...
2606
2607 // Progs DB (capture records)
2609
2610 // Mapinfo
2615
2617}
2618
2620{
2621 game_stopped = 2;
2622
2623 if(world_initialized > 0)
2624 {
2626
2627 // if a timeout is active, reset the slowmo value to normal
2629 cvar_set("slowmo", ftos(orig_slowmo));
2630
2631 LOG_TRACE("Saving persistent data...");
2632 Ban_SaveBans();
2633
2634 // playerstats with unfinished match
2636
2637 if(!cheatcount_total)
2638 {
2641 else
2643 }
2644 if(autocvar_developer > 0)
2645 {
2647 db_dump(TemporaryDB, "server-temp.db");
2648 else
2649 db_save(TemporaryDB, "server-temp.db");
2650 }
2651 CheatShutdown(); // must be after cheatcount check
2654 LOG_TRACE("Saving persistent data... done!");
2655 // tell the bot system the game is ending now
2656 bot_endgame();
2657
2660
2663 }
2664 else if(world_initialized == 0)
2665 {
2666 LOG_INFO("NOTE: crashed before even initializing the world, not saving persistent data");
2667 }
2668 else
2669 {
2671 }
2672}
void anticheat_endframe()
Definition anticheat.qc:249
void antilag_record(entity e, entity store, float t)
Definition antilag.qc:21
float autocvar_g_antilag_nudge
Definition antilag.qh:4
bool bot_waypoints_for_items
Definition api.qh:9
void bot_endgame()
Definition bot.qc:412
int player_count
Definition api.qh:103
bool autocvar_g_waypoints_for_items
Definition api.qh:8
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
#define BITSET(var, mask, flag)
Definition bits.qh:11
#define boolean(value)
Definition bool.qh:9
void CheatShutdown()
Definition cheats.qc:54
void CheatInit()
Definition cheats.qc:49
float cheatcount_total
Definition cheats.qh:10
void CheckEngineExtensions(void)
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
void SetResource(entity e, Resource res_type, float amount)
Sets the current amount of resource the given entity will have.
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
fields which are explicitly/manually set are marked with "M", fields set automatically are marked wit...
Definition weapon.qh:42
string netname
M: refname : reference name name.
Definition weapon.qh:84
float cnt
Definition powerups.qc:24
float ping
Definition main.qh:169
float ping_movementloss
Definition main.qh:169
float ping_packetloss
Definition main.qh:169
bool warmup_stage
Definition main.qh:120
int serverflags
Definition main.qh:211
int team
Definition main.qh:188
#define g_race
Definition race.qh:48
int spawnflags
Definition ammo.qh:15
const int IT_UNLIMITED_AMMO
Definition item.qh:23
const int IT_UNLIMITED_SUPERWEAPONS
Definition item.qh:24
#define M_ARGV(x, type)
Definition events.qh:17
#define PHYS_INPUT_TIMELENGTH
Definition player.qh:253
#define IS_CLIENT(s)
Definition player.qh:241
#define IS_DEAD(s)
Definition player.qh:244
#define IS_PLAYER(s)
Definition player.qh:242
#define MAX_TEAMSCORE
Definition scores.qh:149
float warmup_limit
Definition stats.qh:377
const int OVERTIME_SUDDENDEATH
Definition stats.qh:77
int timeout_status
Definition stats.qh:87
vector weaponsInMapAll
all the weapons placed by the mapper (weaponreplace applied), ignores most filters
Definition stats.qh:55
#define Q3COMPAT_COMMON
Definition stats.qh:370
#define autocvar_timelimit
Definition stats.qh:92
#define autocvar_fraglimit
Definition stats.qh:90
float game_starttime
Definition stats.qh:82
int autocvar_sv_gameplayfix_delayprojectiles
Definition stats.qh:217
float game_stopped
Definition stats.qh:81
int overtimes
Definition stats.qh:86
int autocvar_leadlimit
Definition stats.qh:84
string playername(string thename, int teamid, bool team_colorize)
Definition util.qc:2178
void get_mi_min_max(float mode)
Definition util.qc:674
string maplist_reply
Definition util.qh:161
string rankings_reply
Definition util.qh:161
vector mi_min
Definition util.qh:127
string records_reply[10]
Definition util.qh:162
string lsmaps_reply
Definition util.qh:161
string ladder_reply
Definition util.qh:161
vector mi_max
Definition util.qh:128
string monsterlist_reply
Definition util.qh:161
#define RACE_RECORD
Definition util.qh:97
const int INITPRIO_GAMETYPE_FALLBACK
Definition constants.qh:95
const float PL_CORPSE_SCALE
Definition constants.qh:60
const int SERVERFLAG_FORBID_PICKUPTIMER
Definition constants.qh:20
const int SERVERFLAG_ALLOW_FULLBRIGHT
Definition constants.qh:16
const int INITPRIO_DROPTOFLOOR
Definition constants.qh:97
const int INITPRIO_FINDTARGET
Definition constants.qh:96
const int FL_ITEM
Definition constants.qh:77
string classname
float Q3SURFACEFLAG_SKY
float flags
const float MOVE_NOMONSTERS
float trace_dphitcontents
float maxclients
entity trace_ent
float frametime
string mapname
const float MOVE_NORMAL
vector mins
string trace_dphittexturename
const float FILE_READ
float time
vector trace_endpos
float checkpvs(vector viewpos, entity viewee)
float trace_startsolid
vector maxs
float nextthink
float trace_dphitq3surfaceflags
vector origin
float trace_fraction
float trace_allsolid
const float FILE_APPEND
ERASEABLE bool cvar_value_issafe(string s)
Definition cvar.qh:11
void defer(entity this, float fdelay, void(entity) func)
Execute func() after time + fdelay.
Definition defer.qh:29
float MOVETYPE_USER_FIRST
float MOVETYPE_USER_LAST
#define strlen
#define tokenize_console
#define buf_create
#define g_duel
Definition duel.qh:32
ERASEABLE bool fexists(string f)
Definition file.qh:4
const float FLOAT_MAX
Definition float.qh:3
void GameLogInit()
Definition gamelog.qc:45
void GameLogClose()
Definition gamelog.qc:75
void GameLogEcho(string s)
Definition gamelog.qc:15
int autocvar_sv_eventlog_files_counter
Definition gamelog.qh:6
bool autocvar_sv_eventlog_console
Definition gamelog.qh:4
bool autocvar_sv_eventlog
Definition gamelog.qh:3
string getmonsterlist()
string getrecords(int page)
Definition getreplies.qc:35
string getladder()
Definition getreplies.qc:71
string getlsmaps()
string getrankings()
Definition getreplies.qc:46
string getmaplist()
Weapons
Definition guide.qh:113
bool tracebox_hits_trigger_hurt(vector start, vector e_min, vector e_max, vector end)
Definition hurt.qc:78
prev
Definition all.qh:73
next
Definition all.qh:95
string GetMapname()
void FixIntermissionClient(entity e)
string GetGametype()
float DoNextMapOverride(float reinit)
void Map_MarkAsRecent(string m)
void GotoNextMap(float reinit)
bool intermission_running
float intermission_exittime
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
ERASEABLE void IL_ENDFRAME()
#define IL_EACH(this, cond, body)
void Ban_SaveBans()
Definition ipban.qc:263
void Ban_LoadBans()
Definition ipban.qc:305
#define FOREACH(list, cond, body)
Definition iter.qh:19
int SendFlags
Definition net.qh:159
const int MSG_ENTITY
Definition net.qh:156
#define WriteHeader(to, id)
Definition net.qh:265
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:167
#define STAT(...)
Definition stats.qh:82
#define LOG_WARNF(...)
Definition log.qh:62
#define LOG_HELP(...)
Definition log.qh:85
#define LOG_INFO(...)
Definition log.qh:65
noref int autocvar_developer
Definition log.qh:96
#define LOG_TRACE(...)
Definition log.qh:76
#define LOG_DEBUG(...)
Definition log.qh:80
#define backtrace(msg)
Definition log.qh:99
#define LOG_INFOF(...)
Definition log.qh:66
#define LOG_WARN(...)
Definition log.qh:61
ERASEABLE void db_close(int db)
Definition map.qh:84
ERASEABLE int db_load(string filename)
Definition map.qh:35
ERASEABLE int db_create()
Definition map.qh:25
ERASEABLE void db_save(int db, string filename)
Definition map.qh:8
ERASEABLE void db_dump(int db, string filename)
Definition map.qh:69
bool MapReadSizes(string map)
Definition mapinfo.qc:1401
float MapInfo_FilterGametype(Gametype pGametype, int pFeatures, int pFlagsRequired, int pFlagsForbidden, bool pAbortOnGenerate)
Definition mapinfo.qc:176
void MapInfo_Shutdown()
Definition mapinfo.qc:1642
int MapInfo_RequiredFlags()
Definition mapinfo.qc:1670
Gametype MapInfo_CurrentGametype()
Definition mapinfo.qc:1482
string _MapInfo_FindArenaFile(string pFilename, string extension)
Definition mapinfo.qc:1002
int MapInfo_ForbiddenFlags()
Definition mapinfo.qc:1655
int MapInfo_CurrentFeatures()
Definition mapinfo.qc:1472
void MapInfo_LoadMapSettings(string s)
Definition mapinfo.qc:1571
void MapInfo_ClearTemps()
Definition mapinfo.qc:1630
void MapInfo_Enumerate()
Definition mapinfo.qc:134
int map_minplayers
Definition mapinfo.qh:190
int map_maxplayers
Definition mapinfo.qh:191
string MapInfo_Map_fog
Definition mapinfo.qh:12
string MapInfo_Map_clientstuff
Definition mapinfo.qh:11
bool autocvar_g_campaign
Definition menu.qc:752
void localcmd(string command,...)
void cvar_set(string name, string value)
string fgets(float fhandle)
void fclose(float fhandle)
float ceil(float f)
void fputs(float fhandle, string s)
entity nextent(entity e)
float bound(float min, float value, float max)
string substring(string s, float start, float length)
float cvar(string name)
float fopen(string filename, float mode)
entity find(entity start,.string field, string match)
float random(void)
void bprint(string text,...)
const string cvar_string(string name)
float vlen(vector v)
void WriteShort(float data, float dest, float desto)
vector vectoangles(vector v)
void changelevel(string map)
float min(float f,...)
void loadfromfile(string file)
float rint(float f)
vector normalize(vector v)
string ftos(float f)
void eprint(entity e)
void WriteByte(float data, float dest, float desto)
float floor(float f)
const string cvar_defstring(string name)
string strzone(string s)
float MSG_BROADCAST
Definition menudefs.qc:55
string argv(float n)
float max(float f,...)
void Movetype_Physics_NoMatchTicrate(entity this, float movedt, bool isclient)
Definition movetypes.qc:779
const int MOVETYPE_NONE
Definition movetypes.qh:129
const int MOVETYPE_FAKEPUSH
Definition movetypes.qh:153
const int MOVETYPE_PUSH
Definition movetypes.qh:136
entity groundentity
Definition movetypes.qh:93
const int MOVETYPE_PHYSICS
Definition movetypes.qh:142
const int MOVETYPE_QCENTITY
Definition movetypes.qh:151
float move_suspendedinair
Definition movetypes.qh:94
void target_music_kill()
Definition music.qc:48
void TargetMusic_RestoreGame()
Definition music.qc:85
var void func_null()
string string_null
Definition nil.qh:9
s1 s2 s1 s2 FLAG s1 s2 FLAG spree_cen s1 CPID_Null
Definition all.inc:619
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1573
void Kill_Notification(NOTIF broadcast, entity client, MSG net_type, CPID net_cpid)
Definition all.qc:1537
#define new_pure(class)
purely logical entities (not linked to the area grid)
Definition oo.qh:67
void PlayerStats_GameReport(bool finished)
void PlayerStats_GameReport_Init()
#define NULL
Definition post.qh:14
#define world
Definition post.qh:15
#define error
Definition pre.qh:6
#define stuffcmd(cl,...)
Definition progsdefs.qh:23
q3compat
Definition quake3.qc:59
#define Q3COMPAT_ARENA
Definition quake3.qh:4
#define Q3COMPAT_DEFI
Definition quake3.qh:5
string GetTeamScoreString(int tm, float shortString)
Definition scores.qc:671
float TeamScore_GetCompareValue(float t)
Returns a value indicating the team score (and higher is better).
Definition scores.qc:803
void WinningConditionHelper(entity this)
Sets the following results for the current scores entities.
Definition scores.qc:440
float TeamScore_AddToTeam(int t, float scorefield, float score)
Adds a score to the given team.
Definition scores.qc:108
string GetPlayerScoreString(entity pl, float shortString)
Returns score strings for eventlog etc.
Definition scores.qc:610
void ScoreRules_generic()
#define AVAILABLE_TEAMS
#define setthink(e, f)
#define getthink(e)
vector
Definition self.qh:92
vector org
Definition self.qh:92
void
Definition self.qh:72
void CampaignPostInit()
Definition campaign.qc:101
void CampaignPreInit()
Definition campaign.qc:49
void CampaignPreIntermission()
Definition campaign.qc:168
int GetPlayerLimit()
Definition client.qc:2128
void PutObserverInServer(entity this, bool is_forced, bool use_spawnpoint)
putting a client as observer in the server
Definition client.qc:261
void ClientInit_Spawn()
Definition client.qc:946
float autocvar_g_player_alpha
Definition client.qh:19
float jointime
Definition client.qh:66
int autocvar_g_maxplayers
Definition client.qh:44
float orig_slowmo
Definition common.qh:58
const float TIMEOUT_ACTIVE
Definition common.qh:49
float sys_frametime
Definition common.qh:57
void VoteThink()
Definition vote.qc:336
void Nagger_Init()
Definition vote.qc:97
void ReadyRestart(bool forceWarmupEnd)
Definition vote.qc:526
void VoteReset(bool verbose)
Definition vote.qc:129
IntrusiveList g_items
Definition items.qh:119
void remove_safely(entity e)
Definition main.qc:283
void Pause_TryPause_Dedicated(entity this)
Definition main.qc:198
void remove_unsafely(entity e)
Definition main.qc:276
string GetField_fullspawndata(entity e, string fieldname, bool vfspath)
Retrieves the value of a map entity field from fullspawndata.
Definition main.qc:451
void remove_except_protected(entity e)
Definition main.qc:269
bool autocvar_sv_autopause
Definition main.qh:19
void MapVote_Think()
Definition mapvoting.qc:758
void MapVote_Start()
Definition mapvoting.qc:743
void race_StartCompleting()
Definition race.qc:1214
int g_race_qualifying
Definition race.qh:13
float WinningConditionHelper_secondscore
second highest score
Definition scores.qh:108
entity scores_initialized
Definition scores.qh:7
float WinningConditionHelper_winnerteam
the color of the winning team, or -1 if none
Definition scores.qh:109
entity WinningConditionHelper_winner
the winning player, or NULL if none
Definition scores.qh:112
float WinningConditionHelper_topscore
highest score
Definition scores.qh:107
float WinningConditionHelper_equality
we have no winner
Definition scores.qh:111
float WinningConditionHelper_lowerisbetter
lower is better, duh
Definition scores.qh:114
float WinningConditionHelper_zeroisworst
zero is worst, duh
Definition scores.qh:115
bool autocvar_g_spawn_useallspawns
Definition spawnpoints.qh:5
int have_team_spawns
bool some_spawn_has_been_used
IntrusiveList g_spawnpoints
IntrusiveList g_projectiles
Definition common.qh:58
#define __spawnfunc_spawn_all()
Definition spawnfunc.qh:66
#define spawnfunc(id)
Definition spawnfunc.qh:96
ClientState CS(Client this)
Definition state.qh:47
#define static_init_late()
Definition static.qh:38
#define STATIC_INIT_EARLY(func)
before worldspawn
Definition static.qh:27
#define static_init_precache()
Definition static.qh:43
#define static_init()
Definition static.qh:33
#define endsWith(this, suffix)
Definition string.qh:245
#define VM_TEMPSTRING_MAXSIZE
Definition string.qh:8
#define strfree(this)
Definition string.qh:59
#define startsWith(haystack, needle)
Definition string.qh:236
#define strcpy(this, s)
Definition string.qh:52
ERASEABLE string cons(string a, string b)
Definition string.qh:276
ERASEABLE string strftime_s()
Definition string.qh:94
IntrusiveList g_monsters
void GameRules_limit_fallbacks()
Set any unspecified rules to their defaults.
Definition sv_rules.qc:62
void GameRules_teams(bool value)
Definition sv_rules.qc:3
int autocvar_leadlimit_and_fraglimit
Definition sv_rules.qh:8
#define INGAME(it)
Definition sv_rules.qh:24
#define GameRules_scoring_add(client, fld, value)
Definition sv_rules.qh:85
#define INGAME_STATUS_CLEAR(it)
Definition sv_rules.qh:22
#define INGAME_JOINED(it)
Definition sv_rules.qh:25
#define INGAME_JOINING(it)
Definition sv_rules.qh:26
var void delete_fn(entity e)
entity Team_GetTeam(int team_num)
Returns the global team entity that corresponds to the given TEAM_NUM value.
Definition teamplay.qc:66
void Team_SetTeamScore(entity team_ent, float score)
Sets the score of the team.
Definition teamplay.qc:80
float Team_GetTeamScore(entity team_ent)
Returns the score of the team.
Definition teamplay.qc:75
entity TeamBalance_CheckAllowedTeams(entity for_whom)
Checks whether the player can join teams according to global configuration and mutator settings.
Definition teamplay.qc:459
bool TeamBalance_IsTeamAllowed(entity balance, int index)
Returns whether the team change to the specified team is allowed.
Definition teamplay.qc:826
entity Team_GetTeamFromIndex(int index)
Returns the global team entity at the given index.
Definition teamplay.qc:57
void Player_SetForcedTeamIndex(entity player, int team_index)
Sets the index of the forced team of the given player.
Definition teamplay.qc:323
bool MoveToTeam(entity client, int team_index, int type)
Moves player to the specified team.
Definition teamplay.qc:299
@ TEAM_FORCE_DEFAULT
Don't force any team.
Definition teamplay.qh:138
bool Team_IsValidTeam(int team_num)
Returns whether team value is valid.
Definition teams.qh:133
int Team_TeamToIndex(int team_num)
Converts team value into team index.
Definition teams.qh:184
#define Team_ColorName_Upper(teamid)
Definition teams.qh:223
#define Team_ColoredFullName(teamid)
Definition teams.qh:232
int Team_IndexToTeam(int index)
Converts team index into team value.
Definition teams.qh:169
const int NUM_TEAMS
Number of teams in the game.
Definition teams.qh:3
bool teamplay
Definition teams.qh:59
string Team_ColorCode(int teamid)
Definition teams.qh:63
#define IS_SPEC(v)
Definition utils.qh:10
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:52
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition utils.qh:15
void WaypointSprite_Init()
Weapon Weapon_from_name(string s)
Definition all.qh:144
int max_shot_distance
Definition weapon.qh:245
const int WEP_FLAG_MUTATORBLOCKED
Definition weapon.qh:261
vector WepSet
Definition weapon.qh:14
const int WEP_FLAG_HIDDEN
Definition weapon.qh:258
const int WEP_FLAG_NORMAL
Definition weapon.qh:257
void WeaponStats_Init()
Definition weaponstats.qc:9
void WeaponStats_Shutdown()
int want_weapon(entity weaponinfo, int allguns)
Definition world.qc:1864
void RunThink(entity this, float dt)
Definition world.qc:2434
void RandomSeed_Think(entity this)
Definition world.qc:602
void InitiateOvertime()
Definition world.qc:1500
void DumpStats(float final)
Definition world.qc:1265
#define BADCVAR(p)
int InitiateSuddenDeath()
Definition world.qc:1468
void SetDefaultAlpha()
Definition world.qc:105
void ClearWinners()
Definition world.qc:1555
void systems_update()
Definition main.qc:7
WepSet weapons_most()
Definition world.qc:1935
void CheckRules_World()
Definition world.qc:1726
void InitGameplayMode()
Definition world.qc:711
int fragsleft_last
Definition world.qc:1560
void MatchEnd_RestoreSpectatorStatus()
Definition world.qc:1396
WepSet weapons_all()
Definition world.qc:1915
float redirection_nextthink
Definition world.qc:2560
entity pingplreport
Definition world.qc:57
void NextLevel()
Definition world.qc:1405
void InitializeEntitiesRun()
Definition world.qc:2263
float RedirectionThink()
Definition world.qc:2561
void SetWinners(.float field, float value)
Definition world.qc:1540
WepSet weapons_start()
Weapons the player normally starts with outside weapon arena.
Definition world.qc:1904
void AddWinners(.float field, float value)
Definition world.qc:1546
float latency_time
Definition world.qc:56
float WinningCondition_Scores(float limit, float leadlimit)
Definition world.qc:1561
bool RandomSeed_Send(entity this, entity to, int sf)
Definition world.qc:596
void MatchEnd_RestoreSpectatorAndTeamStatus(.int prev_team_field)
Definition world.qc:1371
void weaponarena_available_all_update(entity this)
Definition world.qc:1945
void RandomSeed_Spawn()
Definition world.qc:609
bool MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, int attempts, float maxaboveground, float minviewdistance, bool frompos)
Definition world.qc:1118
void PingPLReport_Think(entity this)
Definition world.qc:58
void GotoFirstMap(entity this)
Definition world.qc:116
#define BADPREFIX(p)
void __init_dedicated_server_shutdown()
Definition world.qc:655
float latency_cnt
Definition world.qc:55
void DropToFloor_QC_DelayedInit(entity this)
Definition world.qc:2428
float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
Definition world.qc:1252
void weaponarena_available_devall_update(entity this)
Definition world.qc:1958
void Physics_Frame()
Definition world.qc:2462
void EndFrame()
Definition world.qc:2504
float WinningCondition_RanOutOfSpawns()
Definition world.qc:1642
float GetWinningCode(float fraglimitreached, float equality)
Definition world.qc:1511
void GameplayMode_DelayedInit(entity this)
Definition world.qc:667
void Shutdown()
Definition world.qc:2619
void weaponarena_available_most_update(entity this)
Definition world.qc:1971
WepSet weapons_devall()
Definition world.qc:1925
#define BADVALUE(p, val)
entity randomseed
Definition world.qc:595
void readplayerstartcvars()
Definition world.qc:1984
bool autocvar_sv_freezenonclients
Definition world.qc:2461
const float SPAWNFLAG_NO_WAYPOINTS_FOR_ITEMS
Definition world.qc:103
void readlevelcvars()
Definition world.qc:2188
void PingPLReport_Spawn()
Definition world.qc:96
float redirection_timeout
Definition world.qc:2559
void cvar_changes_init()
Definition world.qc:145
#define BADPRESUFFIX(p, s)
float latency_sum
Definition world.qc:54
void InitializeEntity(entity e, void(entity this) func, int order)
Definition world.qc:2230
bool autocvar_sv_gameplayfix_multiplethinksperframe
Definition world.qc:2433
void DropToFloor_QC(entity this)
Definition world.qc:2304
bool world_already_spawned
Definition world.qc:763
#define BADPRESUFFIXVALUE(p, s, val)
const float LATENCY_THINKRATE
Definition world.qc:53
void RestoreGame()
Definition world.qc:2602
#define X(match)
const int WINNING_NEVER
Definition world.qh:134
entity initialize_entity_first
Definition world.qh:121
string matchid
Definition world.qh:63
string cvar_changes
Definition world.qh:45
string loaded_gametype_custom_string
Definition world.qh:53
string autocvar_sv_termsofservice_url
Definition world.qh:57
float checkrules_suddendeathwarning
Definition world.qh:36
bool autocvar_sv_db_saveasdump
Definition world.qh:18
WepSet start_weapons
Definition world.qh:80
float warmup_start_ammo_cells
Definition world.qh:105
string clientstuff
Definition world.qh:61
const int WINNING_STARTSUDDENDEATHOVERTIME
Definition world.qh:135
entity random_start_ammo
Entity that contains amount of ammo to give with random start weapons.
Definition world.qh:95
bool autocvar_g_jetpack
Definition world.qh:8
float start_ammo_shells
Definition world.qh:84
bool autocvar_sv_logscores_file
Definition world.qh:21
float warmup_start_ammo_rockets
Definition world.qh:104
float warmup_start_ammo_shells
Definition world.qh:102
bool autocvar__sv_init
Definition world.qh:5
bool gametype_custom_enabled
Definition world.qh:52
float start_ammo_fuel
Definition world.qh:88
int start_items
Definition world.qh:83
WepSet start_weapons_default
Definition world.qh:81
bool autocvar_sv_mapformat_is_quake3
Definition world.qh:32
float cvar_purechanges_count
Definition world.qh:47
int random_start_weapons_count
Number of random start weapons to give to players.
Definition world.qh:90
float warmup_start_ammo_nails
Definition world.qh:103
int world_initialized
Definition world.qh:43
float autocvar_sv_mapchange_delay
Definition world.qh:23
float autocvar_timelimit_suddendeath
Definition world.qh:30
float default_weapon_alpha
Definition world.qh:73
const int WINNING_NO
Definition world.qh:132
vector dropped_origin
Definition world.qh:154
int autocvar_g_warmup_allguns
Definition world.qh:10
string record_type
Definition world.qh:55
float TemporaryDB
Definition world.qh:129
int checkrules_overtimesadded
Definition world.qh:38
WepSet g_weaponarena_weapons
Definition world.qh:76
float checkrules_suddendeathend
Definition world.qh:37
float default_player_alpha
Definition world.qh:72
float start_ammo_cells
Definition world.qh:87
string cache_lastmutatormsg
Definition world.qh:70
IntrusiveList g_moveables
Definition world.qh:157
bool autocvar_sv_curl_serverpackages_auto
Definition world.qh:17
float g_weaponarena
Definition world.qh:75
string autocvar_sessionid
Definition world.qh:16
float warmup_start_health
Definition world.qh:107
float checkrules_equality
Definition world.qh:35
bool autocvar_sv_dedicated
Definition world.qh:41
WepSet start_weapons_defaultmask
Definition world.qh:82
float start_ammo_rockets
Definition world.qh:86
float g_weapon_stay
Definition world.qh:109
WepSet warmup_start_weapons_default
Definition world.qh:99
bool autocvar_sv_logscores_console
Definition world.qh:20
float autocvar_timelimit_overtime
Definition world.qh:28
float start_armorvalue
Definition world.qh:97
int autocvar_g_warmup
Definition world.qh:9
WepSet warmup_start_weapons_defaultmask
Definition world.qh:100
WepSet warmup_start_weapons
Definition world.qh:98
int autocvar_timelimit_overtimes
Definition world.qh:29
float warmup_start_ammo_fuel
Definition world.qh:106
bool autocvar__endmatch
Definition world.qh:6
bool autocvar_sv_mapformat_is_quake2
Definition world.qh:33
string g_weaponarena_list
Definition world.qh:78
string autocvar__sv_vote_gametype_custom
Definition world.qh:51
const int WINNING_YES
Definition world.qh:133
float start_health
Definition world.qh:96
float autocvar_quit_and_redirect_timer
Definition world.qh:14
bool sv_ready_restart_after_countdown
Definition world.qh:116
bool autocvar_sv_logscores_bots
Definition world.qh:19
string modname
Definition world.qh:49
string autocvar_sv_logscores_filename
Definition world.qh:22
float warmup_start_armorvalue
Definition world.qh:108
string sv_termsofservice_url_escaped
Definition world.qh:59
float ServerProgsDB
Definition world.qh:128
float start_ammo_nails
Definition world.qh:85
string cvar_purechanges
Definition world.qh:46
string cache_mutatormsg
Definition world.qh:69
string redirection_target
Definition world.qh:67