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>
18#include <common/net_linked.qh>
21#include <common/playerstats.qh>
22#include <common/state.qh>
23#include <common/stats.qh>
24#include <common/teams.qh>
25#include <common/util.qh>
29#include <server/anticheat.qh>
30#include <server/antilag.qh>
31#include <server/bot/api.qh>
32#include <server/campaign.qh>
33#include <server/cheats.qh>
34#include <server/client.qh>
39#include <server/damage.qh>
40#include <server/gamelog.qh>
41#include <server/hook.qh>
42#include <server/ipban.qh>
43#include <server/items/items.qh>
44#include <server/main.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_campcheck_distance");
365 BADCVAR("g_chatsounds");
366 BADCVAR("g_ca_point_leadlimit");
367 BADCVAR("g_ca_point_limit");
368 BADCVAR("g_ca_spectate_enemies");
369 BADCVAR("g_ctf_captimerecord_always");
370 BADCVAR("g_ctf_flag_glowtrails");
371 BADCVAR("g_ctf_dynamiclights");
372 BADCVAR("g_ctf_flag_pickup_verbosename");
373 BADCVAR("g_ctf_flagcarrier_auto_helpme_damage");
374 BADPRESUFFIX("g_ctf_flag_", "_model");
375 BADPRESUFFIX("g_ctf_flag_", "_skin");
376 BADCVAR("g_domination_point_leadlimit");
377 BADCVAR("g_forced_respawn");
378 BADCVAR("g_freezetag_point_leadlimit");
379 BADCVAR("g_freezetag_point_limit");
380 BADCVAR("g_glowtrails");
381 BADCVAR("g_hats");
382 BADCVAR("g_casings");
383 BADCVAR("g_invasion_point_limit");
384 BADCVAR("g_jump_grunt");
385 BADCVAR("g_keepaway_ballcarrier_effects");
386 BADCVAR("g_keepaway_point_limit");
387 BADCVAR("g_keepawayball_effects");
388 BADCVAR("g_keyhunt_point_leadlimit");
389 BADCVAR("g_nexball_goalleadlimit");
390 BADCVAR("g_new_toys_autoreplace");
391 BADCVAR("g_new_toys_use_pickupsound");
392 BADCVAR("g_onslaught_point_limit");
393 BADCVAR("g_physics_predictall");
394 BADCVAR("g_piggyback");
395 BADCVAR("g_playerclip_collisions");
396 BADCVAR("g_spawn_alloweffects");
397 BADCVAR("g_tdm_point_leadlimit");
398 BADCVAR("g_tdm_point_limit");
399 BADCVAR("g_tka_point_leadlimit");
400 BADCVAR("g_tka_point_limit");
401 BADCVAR("g_mayhem_point_limit");
402 BADCVAR("g_mayhem_point_leadlimit");
403 BADCVAR("g_tmayhem_point_limit");
404 BADCVAR("g_tmayhem_point_leadlimit");
405 BADCVAR("leadlimit_and_fraglimit");
406 BADCVAR("leadlimit_override");
407 BADCVAR("pausable");
408 BADCVAR("sv_announcer");
409 BADCVAR("sv_autopause");
410 BADCVAR("sv_checkforpacketsduringsleep");
411 BADCVAR("sv_damagetext");
412 BADCVAR("sv_db_saveasdump");
413 BADCVAR("sv_intermission_cdtrack");
414 BADCVAR("sv_mapchange_delay");
415 BADCVAR("sv_minigames");
416 BADCVAR("sv_namechangetimer");
417 BADCVAR("sv_precacheplayermodels");
418 BADCVAR("sv_qcphysics");
419 BADCVAR("sv_radio");
420 BADCVAR("sv_stepheight");
421 BADCVAR("sv_timeout");
422 BADCVAR("sv_weapons_modeloverride");
423 BADCVAR("w_prop_interval");
424 BADPREFIX("chat_");
425 BADPREFIX("crypto_");
426 BADPREFIX("g_chat_");
427 BADPREFIX("g_ctf_captimerecord_");
428 BADPREFIX("g_hats_");
429 BADPREFIX("g_maplist_");
430 BADPREFIX("g_mod_");
431 BADPREFIX("g_respawn_");
432 BADPREFIX("net_");
433 BADPREFIX("skill_");
434 BADPREFIX("sv_allow_");
435 BADPREFIX("sv_maxidle");
436 BADPREFIX("sv_minigames_");
437 BADPREFIX("sv_radio_");
438 BADPREFIX("sv_timeout_");
439 BADPREFIX("sv_vote_");
440 BADPREFIX("timelimit_");
441 BADPRESUFFIX("g_", "_round_timelimit");
442 BADPRESUFFIX("g_", "_round_enddelay");
443
444 // allowed changes to server admins (please sync this to server.cfg)
445 // vi commands:
446 // :/"impure"/,$d
447 // :g!,^\/\/[^ /],d
448 // :%s,//\‍([^ ]*\‍).*,BADCVAR("\1");,
449 // :%!sort
450 // yes, this does contain some redundant stuff, don't really care
451 BADPREFIX("bot_ai_");
452 BADCVAR("bot_config_file");
453 BADCVAR("bot_number");
454 BADCVAR("bot_prefix");
455 BADCVAR("bot_suffix");
456 BADCVAR("capturelimit_override");
457 BADCVAR("fraglimit_override");
458 BADCVAR("gametype");
459 BADCVAR("g_antilag");
460 BADCVAR("g_balance_teams");
461 BADCVAR("g_balance_teams_queue");
462 BADCVAR("g_balance_teams_remove");
463 BADCVAR("g_balance_teams_remove_wait");
464 BADCVAR("g_balance_teams_skill_significance_threshold");
465 BADCVAR("g_balance_teams_skill_unranked_factor");
466 BADCVAR("g_ban_sync_trusted_servers");
467 BADCVAR("g_ban_sync_uri");
468 BADCVAR("g_buffs");
469 BADCVAR("g_ca_teams_override");
470 BADCVAR("g_ca_prevent_stalemate");
471 BADCVAR("g_ctf_fullbrightflags");
472 BADCVAR("g_ctf_ignore_frags");
473 BADCVAR("g_ctf_leaderboard");
474 BADCVAR("g_domination_point_limit");
475 BADCVAR("g_domination_teams_override");
476 BADCVAR("g_freezetag_revive_spawnshield");
477 BADCVAR("g_freezetag_teams_override");
478 BADCVAR("g_friendlyfire");
479 BADCVAR("g_fullbrightitems");
480 BADCVAR("g_fullbrightplayers");
481 BADCVAR("g_keyhunt_point_limit");
482 BADCVAR("g_keyhunt_teams_override");
483 BADCVAR("g_lms_lives_override");
484 BADCVAR("g_mayhem_powerups");
485 BADCVAR("g_maplist");
486 BADCVAR("g_maxplayers");
487 BADCVAR("g_mirrordamage");
488 BADCVAR("g_nexball_goallimit");
489 BADCVAR("g_norecoil");
490 BADCVAR("g_physics_clientselect");
491 BADCVAR("g_pinata");
492 BADCVAR("g_powerups");
493 BADCVAR("g_powerups_drop_ondeath");
494 BADCVAR("g_player_brightness");
495 BADCVAR("g_rocket_flying");
496 BADCVAR("g_rocket_flying_disabledelays");
497 BADPREFIX("g_spawnshield");
498 BADCVAR("g_start_delay");
499 BADCVAR("g_superspectate");
500 BADCVAR("g_tdm_teams_override");
501 BADCVAR("g_tka_teams_override");
502 BADCVAR("g_tmayhem_teams_override");
503 BADCVAR("g_tmayhem_powerups");
504 BADCVAR("g_weapon_stay"); BADPRESUFFIX("g_", "_weapon_stay");
505 BADCVAR("hostname");
506 BADCVAR("log_file");
507 BADCVAR("maxplayers");
508 BADCVAR("minplayers");
509 BADCVAR("minplayers_per_team");
510 BADCVAR("net_address");
511 BADCVAR("port");
512 BADCVAR("rcon_password");
513 BADCVAR("rcon_restricted_commands");
514 BADCVAR("rcon_restricted_password");
515 BADCVAR("skill");
516 BADCVAR("sv_autoscreenshot");
517 BADCVAR("sv_autotaunt");
518 BADCVAR("sv_curl_defaulturl");
519 BADCVAR("sv_defaultcharacter");
520 BADCVAR("sv_defaultcharacterskin");
521 BADCVAR("sv_defaultplayercolors");
522 BADCVAR("sv_defaultplayermodel");
523 BADCVAR("sv_defaultplayerskin");
524 BADCVAR("sv_maxrate");
525 BADCVAR("sv_motd");
526 BADCVAR("sv_public");
527 BADCVAR("sv_showfps");
528 BADCVAR("sv_showskill");
529 BADCVAR("sv_showspectators");
530 BADCVAR("sv_status_privacy");
531 BADCVAR("sv_taunt");
532 BADCVAR("sv_vote_call");
533 BADCVAR("sv_vote_commands");
534 BADCVAR("sv_vote_majority_factor");
535 BADPREFIX("sv_vote_master");
536 BADCVAR("sv_vote_simple_majority_factor");
537 BADVALUE("sys_ticrate", "0.0078125");
538 BADVALUE("sys_ticrate", "0.015625");
539 BADVALUE("sys_ticrate", "0.03125");
540 BADCVAR("teamplay_mode");
541 BADCVAR("timelimit_override");
542 BADPREFIX("g_warmup");
543 BADPREFIX("sv_info_");
544 BADPREFIX("sv_ready_restart_");
545 BADCVAR("sv_teamnagger");
546
547 BADPRESUFFIXVALUE("g_", "_weaponarena", "most");
548 BADPRESUFFIXVALUE("g_", "_weaponarena", "most_available");
549
550 // mutators that announce themselves properly to the server browser
551 BADCVAR("g_instagib");
552 BADCVAR("g_new_toys");
553 BADCVAR("g_nix");
554 BADCVAR("g_grappling_hook");
555 BADCVAR("g_jetpack");
556
557#undef BADPRESUFFIX
558#undef BADPREFIX
559#undef BADCVAR
560#undef BADVALUE
561#undef BADPRESUFFIXVALUE
562
563 if(pureadding)
564 {
565 if (cvar_purechanges == "")
566 cvar_purechanges = "// this server runs at modified gameplay settings:\n";
567
568 cvar_purechanges = strcat(cvar_purechanges, k, " \"", v, "\" // \"", d, "\"\n");
570 {
571 cvar_purechanges = "// too many settings have been changed to show them here\n";
572 pureadding = 0;
573 }
574 }
576 // WARNING: this variable is used for the server list
577 // NEVER dare to skip this code!
578 // Hacks to intentionally appearing as "pure server" even though you DO have
579 // modified settings may be punished by removal from the server list.
580 // You can do to the variables cvar_changes and cvar_purechanges all you want,
581 // though.
582 }
583 buf_del(h);
584
585 if(cvar_changes == "")
586 cvar_changes = "// this server runs at default server settings\n";
588
589 if(cvar_purechanges == "")
590 cvar_purechanges = "// this server runs at default gameplay settings\n";
592}
593
595bool RandomSeed_Send(entity this, entity to, int sf)
596{
597 WriteHeader(MSG_ENTITY, ENT_CLIENT_RANDOMSEED);
599 return true;
600}
602{
603 this.cnt = bound(0, floor(random() * 65536), 65535);
604 this.nextthink = time + 5;
605
606 this.SendFlags |= 1;
607}
609{
613
614 getthink(randomseed)(randomseed); // sets random seed and nextthink
615}
616
617spawnfunc(__init_dedicated_server)
618{
619 // handler for _init/_init map (only for dedicated server initialization)
620
621 world_initialized = -1; // don't complain
622
624
625 entity e = new(GotoFirstMap);
627 e.nextthink = time; // this is usually 1 at this point
628
629 e = new(info_player_deathmatch); // safeguard against player joining
630
631 // assign reflectively to avoid "assignment to world" warning
632 for (int i = 0, n = numentityfields(); i < n; ++i)
633 {
634 string k = entityfieldname(i);
635 if (k == "classname")
636 {
637 // safeguard against various stuff ;)
638 putentityfieldstring(i, this, "worldspawn");
639 break;
640 }
641 }
642
643 // needs to be done so early because of the constants they create
644 static_init();
647
648 IL_PUSH(g_spawnpoints, e); // just incase
649
652}
653
657
659{
660 maxclients = 0;
661 for (entity head = nextent(NULL); head; head = nextent(head)) {
662 ++maxclients;
663 }
664}
665
667{
668 // at this stage team entities are spawned, teamplay contains the number of them
669
672
673 if (warmup_stage >= 0 && autocvar_g_maxplayers >= 0)
674 return;
675 if (!g_duel)
677
678 if (autocvar_g_maxplayers < 0)
679 {
680 if (map_maxplayers <= 0)
681 map_maxplayers = maxclients; // unlimited, but may need rounding
683 if (teamplay)
684 {
685 // automatic maxplayers should be a multiple of team count
686 int down = map_maxplayers % AVAILABLE_TEAMS;
687 int up = AVAILABLE_TEAMS - down;
688 map_maxplayers += (up < down && up + map_maxplayers <= maxclients) ? up : -down;
689 }
690 }
691
692 if (warmup_stage < 0)
693 {
694 int m = GetPlayerLimit();
696 if (teamplay)
697 {
698 // automatic minplayers should be a multiple of team count
699 int down = map_minplayers % AVAILABLE_TEAMS;
700 int up = AVAILABLE_TEAMS - down;
701 map_minplayers += (up <= down && up + map_minplayers <= m) ? up : -down;
702 }
703 }
704 else
705 map_minplayers = 0; // don't display a minimum if it's not used (g_maxplayers < 0 && g_warmup >= 0)
706}
707
709{
710 VoteReset(false);
711
712 // find out good world mins/maxs bounds, either the static bounds found by looking for solid, or the mapinfo specified bounds
714 // assign reflectively to avoid "assignment to world" warning
715 for (int i = 0, done = 0, n = numentityfields(); i < n; ++i)
716 {
717 string k = entityfieldname(i);
718 vector v = (k == "mins") ? mi_min : (k == "maxs") ? mi_max : '0 0 0';
719 if (v)
720 {
721 putentityfieldstring(i, world, sprintf("%v", v));
722 if (++done == 2) break;
723 }
724 }
725 // currently, NetRadiant's limit is 131072 qu for each side
726 // distance from one corner of a 131072qu cube to the opposite corner is approx. 227023 qu
727 // set the distance according to map size but don't go over the limit to avoid issues with float precision
728 // in case somebody makes extremely large maps
729 max_shot_distance = min(230000, vlen(world.maxs - world.mins));
730
732 GameRules_teams(false);
733
734 if (!cvar_value_issafe(world.fog))
735 {
736 LOG_INFO("The current map contains a potentially harmful fog setting, ignored");
737 world.fog = string_null;
738 }
739 if(MapInfo_Map_fog != "")
740 {
741 if(MapInfo_Map_fog == "none")
742 world.fog = string_null;
743 else
745 }
747
749
752 cvar_set("_sv_vote_gametype_custom", ""); // clear it immediately so it can't get stuck
753
756
758}
759
761spawnfunc(worldspawn)
762{
763 // Must be checked first because we don't always error() and don't want to print this twice.
765 {
766 string msg = "world already spawned - your map may have EXACTLY ONE worldspawn!";
767 if (q3compat) // must be set during (first) worldspawn
768 {
769 // Q3 ignores spurious/extra worldspawn entities, test map: q3dmz_carnage
770 LOG_WARN(msg);
771 delete(this);
772 return;
773 }
774 else
775 error(msg);
776 }
778
779#ifdef WATERMARK
780 string watermark_start = cvar_string("sv_watermark_start");
781 if (watermark_start == "") // always true on Xonotic (re)start
782 cvar_set("sv_watermark_start", WATERMARK);
783 else if (watermark_start != WATERMARK) // true when qc code has been recompiled on a different git commit
784 {
785 LOG_INFOF(
786 "\n^1 Warning: ^3the server QC program was updated without a full restart."
787 "\n^3 Please restart the Xonotic executable otherwise you may get random bugs."
788 "\n\n");
789 }
790#endif
791
793
794 cvar_set("_endmatch", "0");
795
797 {
799 }
800 else
801 {
803 }
804
805 bool wantrestart = false;
806 {
808 {
809 // DP unloads dlcache pk3s before starting a listen server since https://gitlab.com/xonotic/darkplaces/-/merge_requests/134
810 // restore csqc_progname too
811 string expect = "csprogs.dat";
812 wantrestart = cvar_string("csqc_progname") != expect;
813 cvar_set("csqc_progname", expect);
814 }
815 else
816 {
817 // Try to use versioned csprogs from pk3
818 // Only ever use versioned csprogs.dat files on dedicated servers;
819 // we need to reset csqc_progname on clients ourselves, and it's easier if the client's release name is constant
820 string pk3csprogs = "csprogs-" WATERMARK ".dat";
821 // This always works; fall back to it if a versioned csprogs.dat is suddenly missing
822 string select = "csprogs.dat";
823 if (fexists(pk3csprogs)) select = pk3csprogs;
824 if (cvar_string("csqc_progname") != select)
825 {
826 cvar_set("csqc_progname", select);
827 wantrestart = true;
828 }
829 // Check for updates on startup
830 // We do it this way for atomicity so that connecting clients still match the server progs and don't disconnect
831 int sentinel = fopen("progs.txt", FILE_READ);
832 if (sentinel >= 0)
833 {
834 string switchversion = fgets(sentinel);
835 fclose(sentinel);
836 if (switchversion != "" && switchversion != WATERMARK)
837 {
838 LOG_INFOF("Switching progs: " WATERMARK " -> %s", switchversion);
839 // if it doesn't exist, assume either:
840 // a) the current program was overwritten
841 // b) this is a client only update
842 string newprogs = sprintf("progs-%s.dat", switchversion);
843 if (fexists(newprogs))
844 {
845 cvar_set("sv_progs", newprogs);
846 wantrestart = true;
847 }
848 string newcsprogs = sprintf("csprogs-%s.dat", switchversion);
849 if (fexists(newcsprogs))
850 {
851 cvar_set("csqc_progname", newcsprogs);
852 wantrestart = true;
853 }
854 }
855 }
856 }
857 if (wantrestart)
858 {
859 LOG_INFO("Restart requested");
861 // let initialization continue, shutdown depends on it
862 }
863 }
864
865 delete_fn = remove_safely; // during spawning, watch what you remove!
866
867 cvar_changes_init(); // do this very early now so it REALLY matches the server config
868
869 // default to RACE_RECORD, can be overwritten by gametypes
871
872 // needs to be done so early because of the constants they create
873 static_init();
874
876
878
879 // 0 normal
880 lightstyle(0, "m");
881
882 // 1 FLICKER (first variety)
883 lightstyle(1, "mmnmmommommnonmmonqnmmo");
884
885 // 2 SLOW STRONG PULSE
886 lightstyle(2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba");
887
888 // 3 CANDLE (first variety)
889 lightstyle(3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg");
890
891 // 4 FAST STROBE
892 lightstyle(4, "mamamamamama");
893
894 // 5 GENTLE PULSE 1
895 lightstyle(5,"jklmnopqrstuvwxyzyxwvutsrqponmlkj");
896
897 // 6 FLICKER (second variety)
898 lightstyle(6, "nmonqnmomnmomomno");
899
900 // 7 CANDLE (second variety)
901 lightstyle(7, "mmmaaaabcdefgmmmmaaaammmaamm");
902
903 // 8 CANDLE (third variety)
904 lightstyle(8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa");
905
906 // 9 SLOW STROBE (fourth variety)
907 lightstyle(9, "aaaaaaaazzzzzzzz");
908
909 // 10 FLUORESCENT FLICKER
910 lightstyle(10, "mmamammmmammamamaaamammma");
911
912 // 11 SLOW PULSE NOT FADE TO BLACK
913 lightstyle(11, "abcdefghijklmnopqrrqponmlkjihgfedcba");
914
915 // styles 32-62 are assigned by the spawnfunc_light program for switchable lights
916
917 // 63 testing
918 lightstyle(63, "a");
919
922 else
923 PlayerStats_GameReport_Init(); // we need this to be initiated before InitGameplayMode
924
926
931
933
934 player_count = 0;
939
941
942 // NOTE for matchid:
943 // changing the logic generating it is okay. But:
944 // it HAS to stay <= 64 chars
945 // character set: ASCII 33-126 without the following characters: : ; ' " \ $
946 // strftime(false, "%s") isn't reliable, see strftime_s description
947 matchid = strzone(sprintf("%d.%s.%06d", autocvar_sv_eventlog_files_counter, strftime_s(), random() * 1000000));
948
950 GameLogInit(); // requires matchid to be set
951
953
956
957 Ban_LoadBans();
958
961
964
965 // quake 3 music support
966 // bones_was_here: Q3 doesn't support .noise but the Nexuiz _MapInfo_Generate() does.
967 // TODO: Q3 supports an optional intro file: "music/intro.wav music/loop.wav"
968 string music = GetField_fullspawndata(world, "music", true);
969 if (music || world.noise)
970 // prefer .music over .noise
971 strcpy(clientstuff, strcat(clientstuff, "cd loop \"", (music ? music : world.noise), "\"\n"));
972
973 if(whichpack(strcat("maps/", mapname, ".cfg")) != "")
974 {
975 int fd = fopen(strcat("maps/", mapname, ".cfg"), FILE_READ);
976 if(fd != -1)
977 {
978 string s;
979 while((s = fgets(fd)))
980 {
981 int l = tokenize_console(s);
982 if(l < 2)
983 continue;
984 if(argv(0) == "cd")
985 {
986 string trackname = argv(2);
987 LOG_INFO("Found ^1UNSUPPORTED^7 cd loop command in .cfg file; put this line in mapinfo instead:");
988 LOG_INFO(" cdtrack ", trackname);
989 if (cvar_value_issafe(trackname))
990 {
991 string newstuff = strcat(clientstuff, "cd loop \"", trackname, "\"\n");
992 strcpy(clientstuff, newstuff);
993 }
994 }
995 else if(argv(0) == "fog")
996 {
997 LOG_INFO("Found ^1UNSUPPORTED^7 fog command in .cfg file; put this line in worldspawn in the .map/.bsp/.ent file instead:");
998 LOG_INFO(" \"fog\" \"", s, "\"");
999 }
1000 else if(argv(0) == "set")
1001 {
1002 LOG_INFO("Found ^1UNSUPPORTED^7 set command in .cfg file; put this line in mapinfo instead:");
1003 LOG_INFO(" clientsettemp_for_type all ", argv(1), " ", argv(2));
1004 }
1005 else if(argv(0) != "//")
1006 {
1007 LOG_INFO("Found ^1UNSUPPORTED^7 set command in .cfg file; put this line in mapinfo instead:");
1008 LOG_INFO(" clientsettemp_for_type all ", argv(0), " ", argv(1));
1009 }
1010 }
1011 fclose(fd);
1012 }
1013 }
1014
1016
1017 Nagger_Init();
1018
1019 // set up information replies for clients and server to use
1023 bool records_available = false;
1024 for(int i = 0; i < 10; ++i)
1025 {
1026 string s = getrecords(i);
1027 if (s != "")
1028 {
1029 records_reply[i] = strzone(s);
1030 records_available = true;
1031 }
1032 }
1033 if (!records_available)
1034 records_reply[0] = "No records available for the current gametype.\n";
1037
1038 // begin other init
1042
1043 CheatInit();
1044
1045 if (!wantrestart) localcmd("\n_sv_hook_gamestart ", GetGametype(), "\n");
1046
1047 // fill sv_curl_serverpackages from .serverpackage files
1049 {
1050 string s = "csprogs-" WATERMARK ".dat";
1051 // remove automatically managed files from the list to prevent duplicates
1052 for (int i = 0, n = tokenize_console(cvar_string("sv_curl_serverpackages")); i < n; ++i)
1053 {
1054 string pkg = argv(i);
1055 if (startsWith(pkg, "csprogs-")) continue;
1056 if (endsWith(pkg, "-serverpackage.txt")) continue;
1057 if (endsWith(pkg, ".serverpackage")) continue; // OLD legacy
1058 s = cons(s, pkg);
1059 }
1060 // add automatically managed files to the list
1061 #define X(match) MACRO_BEGIN \
1062 int fd = search_begin(match, true, false); \
1063 if (fd >= 0) \
1064 { \
1065 for (int i = 0, j = search_getsize(fd); i < j; ++i) \
1066 { \
1067 s = cons(s, search_getfilename(fd, i)); \
1068 } \
1069 search_end(fd); \
1070 } \
1071 MACRO_END
1072 X("*-serverpackage.txt");
1073 X("*.serverpackage");
1074 #undef X
1075 cvar_set("sv_curl_serverpackages", s);
1076 }
1077
1078 // MOD AUTHORS: change this, and possibly remove a few of the blocks below to ignore certain changes
1079 modname = "Xonotic";
1080 // physics/balance/config changes that count as mod
1081 if(cvar_string("g_mod_physics") != cvar_defstring("g_mod_physics"))
1082 modname = cvar_string("g_mod_physics");
1083 if(cvar_string("g_mod_balance") != cvar_defstring("g_mod_balance") && cvar_string("g_mod_balance") != "Testing")
1084 modname = cvar_string("g_mod_balance");
1085 if(cvar_string("g_mod_config") != cvar_defstring("g_mod_config"))
1086 modname = cvar_string("g_mod_config");
1087 // extra mutators that deserve to count as mod
1088 MUTATOR_CALLHOOK(SetModname, modname);
1089 modname = M_ARGV(0, string);
1090
1091 // save it for later
1093
1094 WinningConditionHelper(this); // set worldstatus
1095
1096 if (autocvar_sv_autopause && autocvar_sv_dedicated && !wantrestart)
1097 // INITPRIO_LAST is too soon: bots either didn't join yet or didn't leave yet, see: bot_fixcount()
1099
1100 // load entity data outputted by create_scrshot_ent
1101 string filename = strcat("data/", mapname, "_scrshot_ent.txt");
1102 if(!find(NULL, classname, "info_autoscreenshot") && fexists(filename))
1103 loadfromfile(filename);
1104
1107}
1108
1110{
1111 //makestatic (this); // Who the f___ did that?
1112 delete(this);
1113}
1114
1115bool MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, int attempts, float maxaboveground, float minviewdistance, bool frompos)
1116{
1117 float m = e.dphitcontentsmask;
1118 e.dphitcontentsmask = goodcontents | badcontents;
1119
1120 vector org = boundmin;
1121 vector delta = boundmax - boundmin;
1122
1123 vector start, end;
1124 start = end = org;
1125 int j; // used after the loop
1126 for(j = 0; j < attempts; ++j)
1127 {
1128 start.x = org.x + random() * delta.x;
1129 start.y = org.y + random() * delta.y;
1130 start.z = org.z + random() * delta.z;
1131
1132 // rule 1: start inside world bounds, and outside
1133 // solid, and don't start from somewhere where you can
1134 // fall down to evil
1135 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1136 if (trace_fraction >= 1)
1137 continue;
1138 if (trace_startsolid)
1139 continue;
1140 if (trace_dphitcontents & badcontents)
1141 continue;
1142 if (trace_dphitq3surfaceflags & badsurfaceflags)
1143 continue;
1144
1145 // rule 2: if we are too high, lower the point
1146 if (trace_fraction * delta.z > maxaboveground)
1147 start = trace_endpos + '0 0 1' * maxaboveground;
1148 vector enddown = trace_endpos;
1149
1150 // rule 3: make sure we aren't outside the map. This only works
1151 // for somewhat well formed maps. A good rule of thumb is that
1152 // the map should have a convex outside hull.
1153 // these can be traceLINES as we already verified the starting box
1154 vector mstart = start + 0.5 * (e.mins + e.maxs);
1155 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1156 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1157 continue;
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 + '0 1 0' * delta.y, 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 0 1' * delta.z, MOVE_NORMAL, e);
1168 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1169 continue;
1170
1171 // rule 4: we must "be seen" by some spawnpoint or item
1172 // Note that checkpvs from mstart to item can detect visibility if mstart is behind
1173 // patch brushes (curved walls) that don't block visibility from the outside, however
1174 // the next traceline from item to mstart correctly detects invisibility in this case
1175 entity sp = NULL;
1176 if(frompos)
1177 {
1178 if((traceline(e.origin, mstart, MOVE_NORMAL, e), trace_fraction) >= 1)
1179 sp = e;
1180 }
1181 if(!sp)
1182 {
1183 IL_EACH(g_spawnpoints, checkpvs(mstart, it),
1184 {
1185 if((traceline(it.origin, mstart, MOVE_NORMAL, e), trace_fraction) >= 1)
1186 {
1187 sp = it;
1188 break;
1189 }
1190 });
1191 }
1192 if(!sp)
1193 {
1194 int items_checked = 0;
1195 IL_EACH(g_items, checkpvs(mstart, it),
1196 {
1197 if((traceline(it.origin + (it.mins + it.maxs) * 0.5, mstart, MOVE_NORMAL, e), trace_fraction) >= 1)
1198 {
1199 sp = it;
1200 break;
1201 }
1202
1203 ++items_checked;
1204 if(items_checked >= attempts)
1205 break; // sanity
1206 });
1207
1208 if(!sp)
1209 continue;
1210 }
1211
1212 float vlen_delta = vlen(delta);
1213 // find a random vector to "look at"
1214 end.x = org.x + random() * delta.x;
1215 end.y = org.y + random() * delta.y;
1216 end.z = org.z + random() * delta.z;
1217 end = start + normalize(end - start) * vlen_delta;
1218
1219 // rule 4: start TO end must not be too short
1220 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1222 continue;
1223 if(trace_fraction < minviewdistance / vlen_delta)
1224 continue;
1225
1226 // rule 5: don't want to look at sky
1228 continue;
1229
1230 // rule 6: we must not end up in trigger_hurt
1231 if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1232 continue;
1233
1234 break;
1235 }
1236
1237 e.dphitcontentsmask = m;
1238
1239 if(j < attempts)
1240 {
1241 setorigin(e, start);
1242 e.angles = vectoangles(end - start);
1243 LOG_DEBUG("Needed ", ftos(j + 1), " attempts");
1244 return true;
1245 }
1246 return false;
1247}
1248
1249float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1250{
1251 return MoveToRandomLocationWithinBounds(e, world.mins, world.maxs, goodcontents, badcontents, badsurfaceflags, attempts, maxaboveground, minviewdistance, false);
1252}
1253
1254/*
1255===============================================================================
1256
1257RULES
1258
1259===============================================================================
1260*/
1261
1262void DumpStats(float final)
1263{
1264 float file;
1265 string s;
1266 float to_console;
1267 float to_eventlog;
1268 float to_file;
1269 float i;
1270
1271 to_console = autocvar_sv_logscores_console;
1272 to_eventlog = autocvar_sv_eventlog;
1274
1275 if(!final)
1276 {
1277 to_console = true; // always print printstats replies
1278 to_eventlog = false; // but never print them to the event log
1279 }
1280
1281 if(to_eventlog)
1283 to_console = false; // otherwise we get the output twice
1284
1285 if(final)
1286 s = ":scores:";
1287 else
1288 s = ":status:";
1289 s = strcat(s, GetGametype(), "_", GetMapname(), ":", ftos(rint(time)));
1290
1291 if(to_console)
1292 LOG_HELP(s);
1293 if(to_eventlog)
1294 GameLogEcho(s);
1295
1296 file = -1;
1297 if(to_file)
1298 {
1300 if(file == -1)
1301 to_file = false;
1302 else
1303 fputs(file, strcat(s, "\n"));
1304 }
1305
1306 s = strcat(":labels:player:", GetPlayerScoreString(NULL, 0));
1307 if(to_console)
1308 LOG_HELP(s);
1309 if(to_eventlog)
1310 GameLogEcho(s);
1311 if(to_file)
1312 fputs(file, strcat(s, "\n"));
1313
1315 s = strcat(":player:see-labels:", GetPlayerScoreString(it, 0), ":");
1316 s = strcat(s, ftos(rint(time - CS(it).jointime)), ":");
1317 if(IS_PLAYER(it) || INGAME_JOINED(it))
1318 s = strcat(s, ftos(it.team), ":");
1319 else
1320 s = strcat(s, "spectator:");
1321
1322 if(to_console)
1323 LOG_HELP(s, playername(it.netname, it.team, false));
1324 if(to_eventlog)
1325 GameLogEcho(strcat(s, ftos(it.playerid), ":", playername(it.netname, it.team, false)));
1326 if(to_file)
1327 fputs(file, strcat(s, playername(it.netname, it.team, false), "\n"));
1328 });
1329
1330 if(teamplay)
1331 {
1332 s = strcat(":labels:teamscores:", GetTeamScoreString(0, 0));
1333 if(to_console)
1334 LOG_HELP(s);
1335 if(to_eventlog)
1336 GameLogEcho(s);
1337 if(to_file)
1338 fputs(file, strcat(s, "\n"));
1339
1340 for(i = 1; i < 16; ++i)
1341 {
1342 s = strcat(":teamscores:see-labels:", GetTeamScoreString(i, 0));
1343 s = strcat(s, ":", ftos(i));
1344 if(to_console)
1345 LOG_HELP(s);
1346 if(to_eventlog)
1347 GameLogEcho(s);
1348 if(to_file)
1349 fputs(file, strcat(s, "\n"));
1350 }
1351 }
1352
1353 if(to_console)
1354 LOG_HELP(":end");
1355 if(to_eventlog)
1356 GameLogEcho(":end");
1357 if(to_file)
1358 {
1359 fputs(file, ":end\n");
1360 fclose(file);
1361 }
1362}
1363
1364// it should be called by gametypes where players can join a team but have to wait before playing
1365// it puts players who joined too late (without being able to play) back to spectators
1366// if prev_team_field is not team it also puts players who previously switched team (without being
1367// able to play on the new team) back to previous team
1369{
1370 bool fix_team = (teamplay && prev_team_field != team);
1371 FOREACH_CLIENT(true,
1372 {
1373 if (!IS_PLAYER(it) && INGAME_JOINING(it))
1374 {
1376 PutObserverInServer(it, true, false);
1377 bprint(playername(it.netname, it.team, false), " has been moved back to spectator\n");
1378 it.winning = false;
1379 }
1380 else if (fix_team && INGAME_JOINED(it) && it.(prev_team_field) && it.team != it.(prev_team_field))
1381 {
1383 if (MoveToTeam(it, Team_TeamToIndex(it.(prev_team_field)), 6))
1384 {
1385 string pl_name = playername(it.netname, it.team, false);
1386 bprint(pl_name, " has been moved back to the ", Team_ColoredFullName(it.team), "\n");
1387 }
1388 it.winning = (it.team == WinningConditionHelper_winnerteam);
1389 }
1390 });
1391}
1392
1397
1398/*
1399go to the next level for deathmatch
1400only called if a time or frag limit has expired
1401*/
1403{
1404 cvar_set("_endmatch", "0");
1405 game_stopped = true;
1406 intermission_running = true; // game over
1407
1408 // enforce a wait time before allowing changelevel
1409 if(player_count > 0)
1411 else
1413
1414 /*
1415 WriteByte (MSG_ALL, SVC_CDTRACK);
1416 WriteByte (MSG_ALL, 3);
1417 WriteByte (MSG_ALL, 3);
1418 // done in FixIntermission
1419 */
1420
1421 //pos = FindIntermission ();
1422
1423 VoteReset(true);
1424
1425 MUTATOR_CALLHOOK(MatchEnd_BeforeScores);
1426
1427 DumpStats(true);
1428
1429 // send statistics
1432
1433 Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_Null); // kill all centerprints now
1434
1436 GameLogEcho(":gameover");
1437
1438 GameLogClose();
1439
1440 int winner_team = 0;
1441 FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), {
1443 if(it.winning)
1444 {
1445 if (teamplay && !winner_team)
1446 {
1447 winner_team = it.team;
1448 bprint(Team_ColorCode(winner_team), Team_ColorName_Upper(winner_team), "^7 team wins the match\n");
1449 }
1450 bprint(playername(it.netname, it.team, false), " ^7wins\n");
1451 }
1452 });
1453
1455
1458
1459 MUTATOR_CALLHOOK(MatchEnd);
1460
1461 localcmd("\nsv_hook_gameend\n");
1462}
1463
1464
1466{
1467 // Check first whether normal overtimes could be added before initiating suddendeath mode
1468 // - for this timelimit_overtime needs to be >0 of course
1469 // - also check the winning condition calculated in the previous frame and only add normal overtime
1470 // again, if at the point at which timelimit would be extended again, still no winner was found
1474 {
1475 return 1; // need to call InitiateOvertime later
1476 }
1477 else
1478 {
1480 {
1482 {
1483 checkrules_suddendeathend = time; // no suddendeath in campaign
1484 }
1485 else
1486 {
1489 }
1492 }
1493 return 0;
1494 }
1495}
1496
1497void InitiateOvertime() // ONLY call this if InitiateSuddenDeath returned true
1498{
1500 // NOTE: here overtimes can never be < 0 so it can be safely sent as (unsigned) int stat; we ignore
1501 // the upper limit of OVERTIME_SUDDENDEATH - 1 = 16777215 - 1 that in practice can never be reached in game
1503 //add one more overtime by simply extending the timelimit
1505 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_TIME, autocvar_timelimit_overtime * 60);
1506}
1507
1508float GetWinningCode(float fraglimitreached, float equality)
1509{
1510 if(autocvar_g_campaign == 1)
1511 {
1512 if(fraglimitreached)
1513 return WINNING_YES;
1514 else
1515 return WINNING_NO;
1516 }
1517 else
1518 {
1519 if(equality)
1520 {
1521 if(fraglimitreached)
1523 else
1524 return WINNING_NEVER;
1525 }
1526 else
1527 {
1528 if(fraglimitreached)
1529 return WINNING_YES;
1530 else
1531 return WINNING_NO;
1532 }
1533 }
1534}
1535
1536// set the .winning flag for exactly those players with a given field value
1537void SetWinners(.float field, float value)
1538{
1539 FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), { it.winning = (it.(field) == value); });
1540}
1541
1542// set the .winning flag for those players with a given field value
1543void AddWinners(.float field, float value)
1544{
1545 FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), {
1546 if(it.(field) == value)
1547 it.winning = 1;
1548 });
1549}
1550
1551// clear the .winning flags
1553{
1554 FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), { it.winning = 0; });
1555}
1556
1558float WinningCondition_Scores(float limit, float leadlimit)
1559{
1560 // TODO make everything use THIS winning condition (except LMS)
1562
1563 if(teamplay)
1564 {
1565 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
1566 {
1569 }
1570 }
1571
1572 ClearWinners();
1577
1579 {
1582 limit = -limit;
1583 }
1584
1586 leadlimit = 0; // not supported in this mode
1587
1588 if(MUTATOR_CALLHOOK(Scores_CountFragsRemaining))
1589 {
1590 float fragsleft;
1592 {
1593 fragsleft = 1;
1594 }
1595 else
1596 {
1597 fragsleft = FLOAT_MAX;
1598 float leadingfragsleft = FLOAT_MAX;
1599 if (limit)
1600 fragsleft = limit - WinningConditionHelper_topscore;
1601 if (leadlimit)
1603
1604 if (limit && leadlimit && autocvar_leadlimit_and_fraglimit)
1605 fragsleft = max(fragsleft, leadingfragsleft);
1606 else
1607 fragsleft = min(fragsleft, leadingfragsleft);
1608 }
1609
1610 if (fragsleft_last != fragsleft) // do not announce same remaining frags multiple times
1611 {
1612 if (fragsleft == 1)
1613 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_REMAINING_FRAG_1);
1614 else if (fragsleft == 2)
1615 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_REMAINING_FRAG_2);
1616 else if (fragsleft == 3)
1617 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_REMAINING_FRAG_3);
1618
1619 fragsleft_last = fragsleft;
1620 }
1621 }
1622
1623 bool fraglimit_reached = (limit && WinningConditionHelper_topscore >= limit);
1624 bool leadlimit_reached = (leadlimit && WinningConditionHelper_topscore - WinningConditionHelper_secondscore >= leadlimit);
1625
1626 bool limit_reached;
1627 // only respect leadlimit_and_fraglimit when both limits are set or the game will never end
1628 if (limit && leadlimit && autocvar_leadlimit_and_fraglimit)
1629 limit_reached = (fraglimit_reached && leadlimit_reached);
1630 else
1631 limit_reached = (fraglimit_reached || leadlimit_reached);
1632
1633 return GetWinningCode(
1634 WinningConditionHelper_topscore && limit_reached,
1636 );
1637}
1638
1640{
1641 if(have_team_spawns <= 0)
1642 return WINNING_NO;
1643
1645 return WINNING_NO;
1646
1648 return WINNING_NO;
1649
1650 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
1651 {
1653 }
1654
1655 FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
1656 {
1657 if (Team_IsValidTeam(it.team))
1658 {
1659 Team_SetTeamScore(Team_GetTeam(it.team), 1);
1660 }
1661 });
1662
1663 IL_EACH(g_spawnpoints, true,
1664 {
1665 if (Team_IsValidTeam(it.team))
1666 {
1667 Team_SetTeamScore(Team_GetTeam(it.team), 1);
1668 }
1669 });
1670
1671 ClearWinners();
1672 float team1_score = Team_GetTeamScore(Team_GetTeamFromIndex(1));
1673 float team2_score = Team_GetTeamScore(Team_GetTeamFromIndex(2));
1674 float team3_score = Team_GetTeamScore(Team_GetTeamFromIndex(3));
1675 float team4_score = Team_GetTeamScore(Team_GetTeamFromIndex(4));
1676 if(team1_score + team2_score + team3_score + team4_score == 0)
1677 {
1678 checkrules_equality = true;
1679 return WINNING_YES;
1680 }
1681 else if(team1_score + team2_score + team3_score + team4_score == 1)
1682 {
1683 float t, i;
1684 if(team1_score)
1685 t = 1;
1686 else if(team2_score)
1687 t = 2;
1688 else if(team3_score)
1689 t = 3;
1690 else // if(team4_score)
1691 t = 4;
1693 for(i = 0; i < MAX_TEAMSCORE; ++i)
1694 {
1695 for (int j = 1; j <= AVAILABLE_TEAMS; ++j)
1696 {
1697 if (t == j)
1698 {
1699 continue;
1700 }
1701 if (!TeamBalance_IsTeamAllowed(balance, j))
1702 {
1703 continue;
1704 }
1706 }
1707 }
1708
1709 AddWinners(team, t);
1710 return WINNING_YES;
1711 }
1712 else
1713 return WINNING_NO;
1714}
1715
1716/*
1717============
1718CheckRules_World
1719
1720Exit deathmatch games upon conditions
1721============
1722*/
1724{
1725 VoteThink();
1726 MapVote_Think();
1727
1729
1730 if (intermission_running) // someone else quit the game already
1731 {
1732 if(player_count == 0) // Nobody there? Then let's go to the next map
1733 MapVote_Start();
1734 // this will actually check the player count in the next frame
1735 // again, but this shouldn't hurt
1736 return;
1737 }
1738
1739 float timelimit = autocvar_timelimit * 60;
1740 float fraglimit = autocvar_fraglimit;
1741 float leadlimit = autocvar_leadlimit;
1742 if (leadlimit < 0) leadlimit = 0;
1743
1744 if(warmup_stage || time <= game_starttime) // NOTE: this is <= to prevent problems in the very tic where the game starts
1745 {
1746 if(timelimit > 0)
1747 timelimit = 0; // timelimit is not made for warmup
1748 if(fraglimit > 0)
1749 fraglimit = 0; // no fraglimit for now
1750 leadlimit = 0; // no leadlimit for now
1751 }
1752
1753 if (autocvar__endmatch || timelimit < 0)
1754 {
1755 // endmatch
1756 NextLevel();
1757 return;
1758 }
1759
1760 if(timelimit > 0)
1761 timelimit += game_starttime;
1762
1763 int overtimes_prev = overtimes;
1764 int wantovertime = 0;
1765
1767 {
1769 {
1772 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_RACE_FINISHLAP);
1773 else
1774 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_FRAG);
1775 }
1776 }
1777 else
1778 {
1779 if (timelimit && time >= timelimit)
1780 {
1781 if(g_race && (g_race_qualifying == 2) && timelimit > 0)
1782 {
1783 float totalplayers;
1784 float playerswithlaps;
1785 float readyplayers;
1786 totalplayers = playerswithlaps = readyplayers = 0;
1788 ++totalplayers;
1789 if(GameRules_scoring_add(it, RACE_FASTEST, 0))
1790 ++playerswithlaps;
1791 if(it.ready)
1792 ++readyplayers;
1793 });
1794
1795 // at least 2 of the players have completed a lap: start the RACE
1796 // otherwise, the players should end the qualifying on their own
1797 if(readyplayers || playerswithlaps >= 2)
1798 {
1800 ReadyRestart(true); // go to race
1801 return;
1802 }
1803 else
1804 wantovertime |= InitiateSuddenDeath();
1805 }
1806 else
1807 wantovertime |= InitiateSuddenDeath();
1808 }
1809 }
1810
1812 {
1813 NextLevel();
1814 return;
1815 }
1816
1817 int checkrules_status = WinningCondition_RanOutOfSpawns();
1818 if(checkrules_status == WINNING_YES)
1819 bprint("Hey! Someone ran out of spawns!\n");
1820 else if(MUTATOR_CALLHOOK(CheckRules_World, checkrules_status, timelimit, fraglimit))
1821 checkrules_status = M_ARGV(0, float);
1822 else
1823 checkrules_status = WinningCondition_Scores(fraglimit, leadlimit);
1824
1825 if(checkrules_status == WINNING_STARTSUDDENDEATHOVERTIME)
1826 {
1827 checkrules_status = WINNING_NEVER;
1829 wantovertime |= InitiateSuddenDeath();
1830 }
1831
1832 if(checkrules_status == WINNING_NEVER)
1833 // equality cases! Nobody wins if the overtime ends in a draw.
1834 ClearWinners();
1835
1836 if(wantovertime)
1837 {
1838 if(checkrules_status == WINNING_NEVER)
1840 else
1841 checkrules_status = WINNING_YES;
1842 }
1843
1845 if(checkrules_status != WINNING_NEVER || time >= checkrules_suddendeathend)
1846 checkrules_status = WINNING_YES;
1847
1848 if(checkrules_status == WINNING_YES)
1849 {
1850 if (overtimes == OVERTIME_SUDDENDEATH && overtimes != overtimes_prev)
1851 {
1852 // if suddendeathend overtime has just begun, revert it
1854 overtimes = overtimes_prev;
1855 }
1856 //print("WINNING\n");
1857 NextLevel();
1858 }
1859}
1860
1861int want_weapon(entity weaponinfo, int allguns)
1862{
1863 int d = 0;
1864 bool allow_mutatorblocked = false;
1865
1866 if(!weaponinfo.m_id)
1867 return 0;
1868
1869 bool mutator_returnvalue = MUTATOR_CALLHOOK(WantWeapon, weaponinfo, d, allguns, allow_mutatorblocked);
1870 d = M_ARGV(1, float);
1871 allguns = M_ARGV(2, int);
1872 allow_mutatorblocked = M_ARGV(3, bool);
1873
1874 if(allguns == 1)
1875 d = boolean(!(weaponinfo.spawnflags & WEP_FLAG_HIDDEN));
1876 else if(allguns == 2)
1877 d = boolean((weaponinfo.spawnflags & WEP_FLAG_NORMAL) && !(weaponinfo.spawnflags & WEP_FLAG_HIDDEN));
1878 else if(!mutator_returnvalue)
1879 d = !(!weaponinfo.weaponstart);
1880
1881 if(!allow_mutatorblocked && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
1882 d = 0;
1883
1884 float t = weaponinfo.weaponstartoverride;
1885
1886 //LOG_INFOF("want_weapon: %s - d: %d t: %d\n", weaponinfo.netname, d, t);
1887
1888 // bit order in t:
1889 // 1: want or not
1890 // 2: is default?
1891 // 4: is set by default?
1892 if(t < 0)
1893 t = 4 | (3 * d);
1894 else
1895 t |= (2 * d);
1896
1897 return t;
1898}
1899
1902{
1903 WepSet ret = '0 0 0';
1904 FOREACH(Weapons, it != WEP_Null, {
1905 int w = want_weapon(it, false);
1906 if (w & 1)
1907 ret |= it.m_wepset;
1908 });
1909 return ret;
1910}
1911
1913{
1914 WepSet ret = '0 0 0';
1915 FOREACH(Weapons, it != WEP_Null, {
1916 if (!(it.spawnflags & (WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN)))
1917 ret |= it.m_wepset;
1918 });
1919 return ret;
1920}
1921
1923{
1924 WepSet ret = '0 0 0';
1925 FOREACH(Weapons, it != WEP_Null,
1926 {
1927 ret |= it.m_wepset;
1928 });
1929 return ret;
1930}
1931
1933{
1934 WepSet ret = '0 0 0';
1935 FOREACH(Weapons, it != WEP_Null, {
1936 if ((it.spawnflags & WEP_FLAG_NORMAL) && !(it.spawnflags & (WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN)))
1937 ret |= it.m_wepset;
1938 });
1939 return ret;
1940}
1941
1943{
1944 if (weaponsInMapAll)
1945 {
1947 }
1948 else
1949 {
1950 // if no weapons are available on the map, just fall back to all weapons arena
1952 }
1953}
1954
1956{
1957 if (weaponsInMapAll)
1958 {
1960 }
1961 else
1962 {
1963 // if no weapons are available on the map, just fall back to devall weapons arena
1965 }
1966}
1967
1969{
1970 if (weaponsInMapAll)
1971 {
1973 }
1974 else
1975 {
1976 // if no weapons are available on the map, just fall back to most weapons arena
1978 }
1979}
1980
1982{
1983 // initialize starting values for players
1984 start_weapons = '0 0 0';
1985 start_weapons_default = '0 0 0';
1986 start_weapons_defaultmask = '0 0 0';
1987 start_items = 0;
1989 start_ammo_nails = 0;
1991 start_ammo_cells = 0;
1992 if (random_start_ammo == NULL)
1993 {
1995 }
1996 start_health = cvar("g_balance_health_start");
1997 start_armorvalue = cvar("g_balance_armor_start");
1998
1999 g_weaponarena = 0;
2000 g_weaponarena_weapons = '0 0 0';
2001
2002 string s = cvar_string("g_weaponarena");
2003
2004 MUTATOR_CALLHOOK(SetWeaponArena, s);
2005 s = M_ARGV(0, string);
2006
2007 if (s == "0" || s == "")
2008 {
2009 // no arena
2010 }
2011 else if (s == "off")
2012 {
2013 // forcibly turn off weaponarena
2014 }
2015 else if (s == "all" || s == "1")
2016 {
2017 g_weaponarena = 1;
2018 g_weaponarena_list = "All Weapons Arena";
2020 }
2021 else if (s == "devall")
2022 {
2023 g_weaponarena = 1;
2024 g_weaponarena_list = "Dev All Weapons Arena";
2026 }
2027 else if (s == "most")
2028 {
2029 g_weaponarena = 1;
2030 g_weaponarena_list = "Most Weapons Arena";
2032 }
2033 else if (s == "all_available")
2034 {
2035 g_weaponarena = 1;
2036 g_weaponarena_list = "All Available Weapons Arena";
2037
2038 // this needs to run after weaponsInMapAll is initialized
2040 }
2041 else if (s == "devall_available")
2042 {
2043 g_weaponarena = 1;
2044 g_weaponarena_list = "Dev All Available Weapons Arena";
2045
2046 // this needs to run after weaponsInMapAll is initialized
2048 }
2049 else if (s == "most_available")
2050 {
2051 g_weaponarena = 1;
2052 g_weaponarena_list = "Most Available Weapons Arena";
2053
2054 // this needs to run after weaponsInMapAll is initialized
2056 }
2057 else if (s == "none")
2058 {
2059 g_weaponarena = 1;
2060 g_weaponarena_list = "No Weapons Arena";
2061 }
2062 else
2063 {
2064 g_weaponarena = 1;
2065 float t = tokenize_console(s);
2066 g_weaponarena_list = "";
2067 for (int j = 0; j < t; ++j)
2068 {
2069 s = argv(j);
2070 Weapon wep = Weapon_from_name(s);
2071 if(wep != WEP_Null)
2072 {
2073 g_weaponarena_weapons |= (wep.m_wepset);
2075 }
2076 }
2077 if (g_weaponarena_list != "") // remove trailing " & "
2079 else // no valid weapon found
2080 g_weaponarena_list = "No Weapons Arena";
2081 }
2082
2083 if (g_weaponarena)
2084 {
2085 g_weapon_stay = 0; // incompatible
2089 }
2090 else
2091 {
2092 FOREACH(Weapons, it != WEP_Null, {
2093 int w = want_weapon(it, false);
2094 WepSet s = it.m_wepset;
2095 if(w & 1)
2096 start_weapons |= s;
2097 if(w & 2)
2099 if(w & 4)
2101 });
2102 }
2103
2104 if(cvar("g_balance_superweapons_time") < 0)
2106
2107 if(!cvar("g_use_ammunition"))
2109
2110 start_ammo_shells = cvar("g_start_ammo_shells");
2111 start_ammo_nails = cvar("g_start_ammo_nails");
2112 start_ammo_rockets = cvar("g_start_ammo_rockets");
2113 start_ammo_cells = cvar("g_start_ammo_cells");
2114 start_ammo_fuel = cvar("g_start_ammo_fuel");
2115 random_start_weapons_count = cvar("g_random_start_weapons_count");
2116 SetResource(random_start_ammo, RES_SHELLS, cvar("g_random_start_shells"));
2117 SetResource(random_start_ammo, RES_BULLETS, cvar("g_random_start_bullets"));
2118 SetResource(random_start_ammo, RES_ROCKETS, cvar("g_random_start_rockets"));
2119 SetResource(random_start_ammo, RES_CELLS, cvar("g_random_start_cells"));
2120
2131
2132 if (!g_weaponarena)
2133 {
2134 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
2135 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
2136 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
2137 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
2138 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
2139 warmup_start_health = cvar("g_warmup_start_health");
2140 warmup_start_armorvalue = cvar("g_warmup_start_armor");
2141 warmup_start_weapons = '0 0 0';
2144 FOREACH(Weapons, it != WEP_Null, {
2146 WepSet s = it.m_wepset;
2147 if(w & 1)
2149 if(w & 2)
2151 if(w & 4)
2153 });
2154 }
2155
2157 start_items |= ITEM_Jetpack.m_itemid;
2158
2159 MUTATOR_CALLHOOK(SetStartItems);
2160
2161 if (start_items & ITEM_Jetpack.m_itemid)
2162 {
2163 start_items |= ITEM_FuelRegen.m_itemid;
2164 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
2165 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
2166 }
2167
2173 SetResource(random_start_ammo, RES_SHELLS, max(0, GetResource(random_start_ammo, RES_SHELLS)));
2174 SetResource(random_start_ammo, RES_BULLETS, max(0, GetResource(random_start_ammo, RES_BULLETS)));
2175 SetResource(random_start_ammo, RES_ROCKETS, max(0, GetResource(random_start_ammo, RES_ROCKETS)));
2176 SetResource(random_start_ammo, RES_CELLS, max(0, GetResource(random_start_ammo, RES_CELLS)));
2177
2183}
2184
2186{
2188 if(cvar("sv_allow_fullbright"))
2190
2192 if(cvar("sv_forbid_pickuptimer"))
2194
2195 sv_ready_restart_after_countdown = cvar("sv_ready_restart_after_countdown");
2196
2197 if(cvar("g_campaign"))
2198 warmup_stage = 0; // no warmup during campaign
2199 else
2200 {
2203 warmup_limit = -1; // don't start until there's enough players
2204 else if (warmup_stage == 1)
2205 {
2206 // this code is duplicated in ReadyCount()
2207 warmup_limit = cvar("g_warmup_limit");
2208 if(warmup_limit == 0)
2210 }
2211 }
2212
2213 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
2214 if(!g_weapon_stay)
2215 g_weapon_stay = cvar("g_weapon_stay");
2216
2217 MUTATOR_CALLHOOK(ReadLevelCvars);
2218
2220 game_starttime = time + cvar("g_start_delay");
2221
2222 FOREACH(Weapons, it != WEP_Null, { it.wr_init(it); });
2223
2225}
2226
2227void InitializeEntity(entity e, void(entity this) func, int order)
2228{
2229 entity prev, cur;
2230
2231 if (!e || e.initialize_entity)
2232 {
2233 // make a proxy initializer entity
2234 entity e_old = e;
2235 e = new(initialize_entity);
2236 e.enemy = e_old;
2237 }
2238
2239 e.initialize_entity = func;
2240 e.initialize_entity_order = order;
2241
2243 prev = NULL;
2244 for (;;)
2245 {
2246 if (!cur || cur.initialize_entity_order > order)
2247 {
2248 // insert between prev and cur
2249 if (prev)
2250 prev.initialize_entity_next = e;
2251 else
2253 e.initialize_entity_next = cur;
2254 return;
2255 }
2256 prev = cur;
2257 cur = cur.initialize_entity_next;
2258 }
2259}
2261{
2262 entity startoflist = initialize_entity_first;
2265 for (entity e = startoflist; e; e = e.initialize_entity_next)
2266 {
2267 e.remove_except_protected_forbidden = 1;
2268 }
2269 for (entity e = startoflist; e; )
2270 {
2271 e.remove_except_protected_forbidden = 0;
2272 e.initialize_entity_order = 0;
2273 entity next = e.initialize_entity_next;
2274 e.initialize_entity_next = NULL;
2275 var void(entity this) func = e.initialize_entity;
2276 e.initialize_entity = func_null;
2277 if (e.classname == "initialize_entity")
2278 {
2279 entity wrappee = e.enemy;
2280 builtin_remove(e);
2281 e = wrappee;
2282 }
2283 //dprint("Delayed initialization: ", e.classname, "\n");
2284 if (func)
2285 {
2286 func(e);
2287 }
2288 else
2289 {
2290 eprint(e);
2291 backtrace(strcat("Null function in: ", e.classname, "\n"));
2292 }
2293 e = next;
2294 }
2296}
2297
2298// originally ported from DP's droptofloor() builtin
2299// TODO: make a common function for the client-side?
2300// bones_was_here: when we have a use case for it, yes
2302{
2303 int nudgeresult;
2304
2305 if(!this || wasfreed(this))
2306 {
2307 LOG_WARN("DropToFloor_QC: can not modify free entity");
2308 return;
2309 }
2310
2311 /* Prior to sv_legacy_bbox_expand 0, both droptofloor and nudgeoutofsolid_OrFallback were done for items
2312 * using box '-16 -16 0' '16 16 48' (without the FL_ITEM expansion applied),
2313 * which often resulted in bboxes partially in solids once expansion was applied.
2314 * We don't want bboxes in solids (bad for gameplay and culling),
2315 * but we also don't want items to land on a "skirting board" or the base of a sloping wall.
2316 * For initial nudgeoutofsolid_OrFallback and droptofloor stages we use a small box
2317 * so they fall as far and in the same place as they traditionally would,
2318 * then we nudge the full size box out of solid, in a direction appropriate for the plane(s).
2319 */
2320 vector saved_mins = this.mins; // gmqcc's used-uninitialized check doesn't handle
2321 vector saved_maxs = this.maxs; // making these assignments FL_ITEM conditional.
2322 if (this.flags & FL_ITEM)
2323 {
2324 // Using the Q3 bbox for best compatibility with all maps, except...
2325 this.mins.x = -15;
2326 this.mins.y = -15;
2327 this.maxs.x = 15;
2328 this.maxs.y = 15;
2329 this.maxs.z = this.mins.z + 30; // ...Nex, Xon and Quake use a different vertical offset, see also: StartItem()
2330 }
2331
2332 /* NOTE: sv_gameplayfix_droptofloorstartsolid_nudgetocorrect isn't checked, so it won't need to be networked to CSQC.
2333 * It was enabled by default in all Xonotic releases and in Nexuiz, so now certain maps depend on it.
2334 * Example: on erbium 0.8.6 the shards @ crylink are too low (in collision with the floor),
2335 * so without this those fall through the floor.
2336 * Q3, Q2 and Quake don't try to move items out of solid.
2337 */
2338 if(!Q3COMPAT_COMMON && autocvar_sv_mapformat_is_quake3) // Xonotic, Nexuiz
2339 {
2340 nudgeresult = nudgeoutofsolid_OrFallback(this);
2341 if (!nudgeresult)
2342 LOG_WARNF("DropToFloor_QC at \"%v\": COULD NOT FIX badly placed entity \"%s\" before drop", this.origin, this.classname);
2343 else if (nudgeresult > 0)
2344 LOG_WARNF("DropToFloor_QC at \"%v\": FIXED badly placed entity \"%s\" before drop", this.origin, this.classname);
2345 }
2347 {
2348 if (this.flags & FL_ITEM)
2349 this.origin.z += 6;
2350 else
2351 this.origin.z += 1; // monsters 1, misc_explobox 2 but we don't support those currently
2352 }
2353
2354 vector end = this.origin;
2356 end.z -= 4096;
2358 end.z -= 128;
2359 else
2360 end.z -= 256; // Quake, QuakeWorld
2361 tracebox(this.origin, this.mins, this.maxs, end, MOVE_NOMONSTERS, this);
2362
2364 {
2365 // Quake games just delete badly placed items (and misc_explobox)...
2366 if (this.flags & FL_ITEM)
2367 {
2368 LOG_WARNF("DropToFloor_QC at \"%v\" (Quake compat): DELETING badly placed item \"%s\"", this.origin, this.classname);
2369 delete(this);
2370 return;
2371 }
2372 // ...not monsters though...
2373 LOG_WARNF("DropToFloor_QC at \"%v\" (Quake compat): badly placed entity \"%s\"", this.origin, this.classname);
2374 }
2376 {
2377 // ...but we can't do that on Q3 maps like jamdm1
2378 // because our tracebox hits things Q3's trace doesn't (patches?).
2379 LOG_WARNF("DropToFloor_QC at \"%v\" (Quake 3 compat): badly placed entity \"%s\"", this.origin, this.classname);
2380 }
2381
2382 /* NOTE: sv_gameplayfix_droptofloorstartsolid (fallback from tracebox to traceline) isn't implemented.
2383 * It was disabled by default in all Xonotic releases and in Nexuiz.
2384 * Q3 doesn't support it (always uses its '-15 -15 -15' '15 15 15' box when dropping items), neither does Quake or Q2.
2385 */
2386
2387 if (!autocvar_sv_mapformat_is_quake2) // Quake, Q3, Nexuiz, Xonotic
2388 // allow to ride movers (or unset if in freefall)
2389 this.groundentity = trace_ent;
2390
2392 // if support is destroyed, keep suspended (gross hack for floating items in various maps)
2393 // bones_was_here: is this for Q1BSP only? Which maps use it? Do we need it at all? Intentions unclear in DP...
2394 this.move_suspendedinair = true;
2395
2396 if (trace_fraction)
2397 this.origin = trace_endpos;
2398
2399 if (this.flags & FL_ITEM)
2400 {
2401 this.mins = saved_mins;
2402 this.maxs = saved_maxs;
2403
2404 // A side effect of using a small box to drop items (and do the initial nudge) is
2405 // the full size box can end up in collision with a sloping floor or terrain model.
2406 nudgeresult = nudgeoutofsolid_OrFallback(this);
2407 // No warns for successful nudge because it would spam about items on slopes/terrain.
2408 }
2409 else if (trace_allsolid && trace_fraction) // dropped using "proper" bbox but never left solid
2410 {
2411 nudgeresult = nudgeoutofsolid_OrFallback(this);
2412 if (nudgeresult > 0)
2413 LOG_WARNF("DropToFloor_QC at \"%v\": FIXED badly placed entity \"%s\" after drop", this.origin, this.classname);
2414 }
2415 else
2416 nudgeresult = -1;
2417
2418 if (!nudgeresult)
2419 if (!Q3COMPAT_COMMON) // to be expected on Q3 maps like gu3-pewter because we use bigger final bboxes
2420 LOG_WARNF("DropToFloor_QC at \"%v\": COULD NOT FIX stuck entity \"%s\" after drop", this.origin, this.classname);
2421
2422 setorigin(this, this.dropped_origin = this.origin);
2423}
2424
2429
2431void RunThink(entity this, float dt)
2432{
2433 // don't let things stay in the past.
2434 // it is possible to start that way by a trigger with a local time.
2436 return;
2437
2438 float oldtime = time; // do we need to save this?
2439
2440 for (int iterations = 0; iterations < 128 && !wasfreed(this); ++iterations)
2441 {
2442 time = max(oldtime, this.nextthink);
2443 this.nextthink = 0;
2444
2445 if(getthink(this))
2446 getthink(this)(this);
2447 // mods often set nextthink to time to cause a think every frame,
2448 // we don't want to loop in that case, so exit if the new nextthink is
2449 // <= the time the qc was told, also exit if it is past the end of the
2450 // frame
2452 break;
2453 }
2454
2455 time = oldtime;
2456}
2457
2460{
2462 return;
2463
2464 IL_EACH(g_moveables, true,
2465 {
2466 if(IS_CLIENT(it) || it.move_movetype == MOVETYPE_PHYSICS)
2467 continue;
2468
2469 //set_movetype(it, it.move_movetype);
2470 // inline the set_movetype function, since this is called a lot
2471 it.movetype = (it.move_qcphysics) ? MOVETYPE_QCENTITY : it.move_movetype;
2472
2473 if(it.move_qcphysics && it.move_movetype != MOVETYPE_NONE)
2475
2476 if(it.movetype >= MOVETYPE_USER_FIRST && it.movetype <= MOVETYPE_USER_LAST) // these cases have no think handling
2477 {
2478 if(it.move_movetype == MOVETYPE_PUSH || it.move_movetype == MOVETYPE_FAKEPUSH)
2479 continue; // these movetypes have no regular think function
2480 // handle thinking here
2481 if (getthink(it) && it.nextthink > 0 && it.nextthink <= time + PHYS_INPUT_TIMELENGTH)
2483 }
2484 });
2485
2487 return;
2488
2489 // make a second pass to see if any ents spawned this frame and make
2490 // sure they run their move/think. this is verified by checking .move_time, which will never be 0 if the entity has moved
2491 // MOVETYPE_NONE is also checked as .move_time WILL be 0 with that movetype
2492 IL_EACH(g_moveables, it.move_qcphysics,
2493 {
2494 if(IS_CLIENT(it) || it.move_time || it.move_movetype == MOVETYPE_NONE || it.move_movetype == MOVETYPE_PHYSICS)
2495 continue;
2496 Movetype_Physics_NoMatchTicrate(it, PHYS_INPUT_TIMELENGTH, false);
2497 });
2498}
2499
2500void systems_update();
2502{
2504
2505 Physics_Frame();
2506
2508 entity e = IS_SPEC(it) ? it.enemy : it;
2509 if (e.typehitsound) {
2510 STAT(TYPEHIT_TIME, it) = time;
2511 } else if (e.killsound) {
2512 STAT(KILL_TIME, it) = time;
2513 } else if (e.hitsound_damage_dealt) {
2514 STAT(HIT_TIME, it) = time;
2515 // NOTE: this is not accurate as client code doesn't need so much accuracy for its purposes
2516 STAT(HITSOUND_DAMAGE_DEALT_TOTAL, it) += ceil(e.hitsound_damage_dealt);
2517 }
2518 });
2519 // add 1 frametime because after this, engine SV_Physics
2520 // increases time by a frametime and then networks the frame
2521 // add another frametime because client shows everything with
2522 // 1 frame of lag (cl_nolerp 0). The last +1 however should not be
2523 // needed!
2524 float altime = time + frametime * (1 + autocvar_g_antilag_nudge);
2525 FOREACH_CLIENT(true, {
2526 it.typehitsound = false;
2527 it.hitsound_damage_dealt = 0;
2528 it.killsound = false;
2529 antilag_record(it, CS(it), altime);
2530 if(it.death_time == time && IS_PLAYER(it) && IS_DEAD(it))
2531 {
2532 // player's bbox gets resized now, instead of in the damage event that killed the player,
2533 // once all the damage events of this frame have been processed with normal size
2534 float h = ceil((it.mins.z + it.maxs.z) * PL_CORPSE_SCALE * 10) / 10;
2535 it.maxs.z = max(h, it.mins.z + 1);
2536 setsize(it, it.mins, it.maxs);
2537 }
2538 });
2539 IL_EACH(g_monsters, true,
2540 {
2541 antilag_record(it, it, altime);
2542 });
2543 IL_EACH(g_projectiles, it.classname == "nade",
2544 {
2545 antilag_record(it, it, altime);
2546 });
2548 IL_ENDFRAME();
2549}
2550
2551
2552/*
2553 * RedirectionThink:
2554 * returns true if redirecting
2555 */
2559{
2560 float clients_found;
2561
2562 if(redirection_target == "")
2563 return false;
2564
2566 {
2567 cvar_set("sv_public", "-2");
2568 redirection_timeout = time + 0.6; // this will only try twice... should be able to keep more clients
2569 if(redirection_target == "self")
2570 bprint("^3SERVER NOTICE:^7 restarting the server\n");
2571 else
2572 bprint("^3SERVER NOTICE:^7 redirecting everyone to ", redirection_target, "\n");
2573 }
2574
2576 return true;
2577
2579
2580 clients_found = 0;
2582 // TODO add timer
2583 LOG_INFO("Redirecting: sending connect command to ", it.netname);
2584 if(redirection_target == "self")
2585 stuffcmd(it, "\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " reconnect\n");
2586 else
2587 stuffcmd(it, strcat("\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " \"connect ", redirection_target, "\"\n"));
2588 ++clients_found;
2589 });
2590
2591 LOG_INFO("Redirecting: ", ftos(clients_found), " clients left.");
2592
2593 if(time > redirection_timeout || clients_found == 0)
2594 localcmd("\nwait; wait; wait; quit\n");
2595
2596 return true;
2597}
2598
2600{
2601 // Loaded from a save game
2602 // some things then break, so let's work around them...
2603
2604 // Progs DB (capture records)
2606
2607 // Mapinfo
2612
2614}
2615
2617{
2618 game_stopped = 2;
2619
2620 if(world_initialized > 0)
2621 {
2623
2624 // if a timeout is active, reset the slowmo value to normal
2626 cvar_set("slowmo", ftos(orig_slowmo));
2627
2628 LOG_TRACE("Saving persistent data...");
2629 Ban_SaveBans();
2630
2631 // playerstats with unfinished match
2633
2634 if(!cheatcount_total)
2635 {
2638 else
2640 }
2641 if(autocvar_developer > 0)
2642 {
2644 db_dump(TemporaryDB, "server-temp.db");
2645 else
2646 db_save(TemporaryDB, "server-temp.db");
2647 }
2648 CheatShutdown(); // must be after cheatcount check
2651 LOG_TRACE("Saving persistent data... done!");
2652 // tell the bot system the game is ending now
2653 bot_endgame();
2654
2657
2660 }
2661 else if(world_initialized == 0)
2662 {
2663 LOG_INFO("NOTE: crashed before even initializing the world, not saving persistent data");
2664 }
2665 else
2666 {
2668 }
2669}
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
const int SFL_SORT_PRIO_PRIMARY
Definition scores.qh:134
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:2190
void get_mi_min_max(float mode)
Definition util.qc:686
string maplist_reply
Definition util.qh:163
string rankings_reply
Definition util.qh:163
vector mi_min
Definition util.qh:129
string records_reply[10]
Definition util.qh:164
string lsmaps_reply
Definition util.qh:163
string ladder_reply
Definition util.qh:163
vector mi_max
Definition util.qh:130
string monsterlist_reply
Definition util.qh:163
#define RACE_RECORD
Definition util.qh:99
const int INITPRIO_GAMETYPE_FALLBACK
Definition constants.qh:87
const float PL_CORPSE_SCALE
Definition constants.qh:57
const int SERVERFLAG_FORBID_PICKUPTIMER
Definition constants.qh:21
const int SERVERFLAG_ALLOW_FULLBRIGHT
Definition constants.qh:17
const int INITPRIO_DROPTOFLOOR
Definition constants.qh:89
const int INITPRIO_FINDTARGET
Definition constants.qh:88
const int FL_ITEM
Definition constants.qh:69
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:26
float MOVETYPE_USER_FIRST
float MOVETYPE_USER_LAST
#define strlen
#define tokenize_console
#define buf_create
#define g_duel
Definition duel.qh:32
WriteByte(chan, ent.angles.y/DEC_FACTOR)
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:53
next
Definition all.qh:75
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:94
#define LOG_WARNF(...)
Definition log.qh:59
#define LOG_HELP(...)
Definition log.qh:83
#define LOG_INFO(...)
Definition log.qh:62
noref int autocvar_developer
Definition log.qh:93
#define LOG_TRACE(...)
Definition log.qh:74
#define LOG_DEBUG(...)
Definition log.qh:78
#define backtrace(msg)
Definition log.qh:96
#define LOG_INFOF(...)
Definition log.qh:63
#define LOG_WARN(...)
Definition log.qh:58
ERASEABLE void db_close(int db)
Definition map.qh:86
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:70
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)
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:133
const int MOVETYPE_FAKEPUSH
Definition movetypes.qh:157
const int MOVETYPE_PUSH
Definition movetypes.qh:140
entity groundentity
Definition movetypes.qh:97
const int MOVETYPE_PHYSICS
Definition movetypes.qh:146
const int MOVETYPE_QCENTITY
Definition movetypes.qh:155
float move_suspendedinair
Definition movetypes.qh:98
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
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
s1 s2 s1 s2 FLAG s1 s2 FLAG spree_cen s1 CPID_Null
Definition all.inc:625
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc:1500
void Kill_Notification(NOTIF broadcast, entity client, MSG net_type, CPID net_cpid)
Definition all.qc:1464
#define new_pure(class)
purely logical entities (not linked to the area grid)
Definition oo.qh:66
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
#define AVAILABLE_TEAMS
Number of teams that exist currently.
#define setthink(e, f)
#define getthink(e)
vector
Definition self.qh:96
vector org
Definition self.qh:96
void
Definition self.qh:76
void CampaignPostInit()
Definition campaign.qc:101
void CampaignPreInit()
Definition campaign.qc:49
void CampaignPreIntermission()
Definition campaign.qc:168
int GetPlayerLimit()
Definition client.qc:2156
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:948
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 race_StartCompleting()
Definition race.qc:1214
int g_race_qualifying
Definition race.qh:11
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:72
#define spawnfunc(id)
Definition spawnfunc.qh:107
ClientState CS(Client this)
Definition state.qh:47
#define static_init_late()
Definition static.qh:39
#define STATIC_INIT_EARLY(func)
before worldspawn
Definition static.qh:28
#define static_init_precache()
Definition static.qh:44
#define static_init()
Definition static.qh:34
#define endsWith(this, suffix)
Definition string.qh:243
#define VM_TEMPSTRING_MAXSIZE
Definition string.qh:8
#define strfree(this)
Definition string.qh:57
#define startsWith(haystack, needle)
Definition string.qh:234
#define strcpy(this, s)
Definition string.qh:51
ERASEABLE string cons(string a, string b)
Definition string.qh:277
ERASEABLE string strftime_s()
Returns the number of seconds since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
Definition string.qh:91
void MapVote_Think()
void MapVote_Start()
IntrusiveList g_monsters
void GameRules_limit_fallbacks()
Set any unspecified rules to their defaults.
Definition sv_rules.qc:61
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 GameRules_scoring(teams, spprio, stprio, fields)
Definition sv_rules.qh:58
#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:95
void Team_SetTeamScore(entity team_ent, float score)
Sets the score of the team.
Definition teamplay.qc:109
float Team_GetTeamScore(entity team_ent)
Returns the score of the team.
Definition teamplay.qc:104
entity TeamBalance_CheckAllowedTeams(entity for_whom)
Checks whether the player can join teams according to global configuration and mutator settings.
Definition teamplay.qc:424
bool TeamBalance_IsTeamAllowed(entity balance, int index)
Returns whether the team change to the specified team is allowed.
Definition teamplay.qc:738
entity Team_GetTeamFromIndex(int index)
Returns the global team entity at the given index.
Definition teamplay.qc:86
void Player_SetForcedTeamIndex(entity player, int team_index)
Sets the index of the forced team of the given player.
Definition teamplay.qc:345
bool MoveToTeam(entity client, int team_index, int type)
Moves player to the specified team.
Definition teamplay.qc:331
@ TEAM_FORCE_DEFAULT
Don't force any team.
Definition teamplay.qh:153
int teamplay_bitmask
The set of currently available teams (AVAILABLE_TEAMS is the number of them).
Definition teamplay.qh:18
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
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
const int WEP_FLAG_HIDDEN
Definition weapon.qh:258
vector WepSet
Definition weapon.qh:14
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:1861
void RunThink(entity this, float dt)
Definition world.qc:2431
void RandomSeed_Think(entity this)
Definition world.qc:601
void InitiateOvertime()
Definition world.qc:1497
void DumpStats(float final)
Definition world.qc:1262
#define BADCVAR(p)
int InitiateSuddenDeath()
Definition world.qc:1465
void SetDefaultAlpha()
Definition world.qc:105
void ClearWinners()
Definition world.qc:1552
void systems_update()
Definition main.qc:7
WepSet weapons_most()
Definition world.qc:1932
void CheckRules_World()
Definition world.qc:1723
void InitGameplayMode()
Definition world.qc:708
int fragsleft_last
Definition world.qc:1557
void MatchEnd_RestoreSpectatorStatus()
Definition world.qc:1393
WepSet weapons_all()
Definition world.qc:1912
float redirection_nextthink
Definition world.qc:2557
entity pingplreport
Definition world.qc:57
void NextLevel()
Definition world.qc:1402
void InitializeEntitiesRun()
Definition world.qc:2260
float RedirectionThink()
Definition world.qc:2558
void SetWinners(.float field, float value)
Definition world.qc:1537
WepSet weapons_start()
Weapons the player normally starts with outside weapon arena.
Definition world.qc:1901
void AddWinners(.float field, float value)
Definition world.qc:1543
float latency_time
Definition world.qc:56
float WinningCondition_Scores(float limit, float leadlimit)
Definition world.qc:1558
bool RandomSeed_Send(entity this, entity to, int sf)
Definition world.qc:595
void MatchEnd_RestoreSpectatorAndTeamStatus(.int prev_team_field)
Definition world.qc:1368
void weaponarena_available_all_update(entity this)
Definition world.qc:1942
void RandomSeed_Spawn()
Definition world.qc:608
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:1115
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:654
float latency_cnt
Definition world.qc:55
void DropToFloor_QC_DelayedInit(entity this)
Definition world.qc:2425
float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
Definition world.qc:1249
void weaponarena_available_devall_update(entity this)
Definition world.qc:1955
void Physics_Frame()
Definition world.qc:2459
void EndFrame()
Definition world.qc:2501
float WinningCondition_RanOutOfSpawns()
Definition world.qc:1639
float GetWinningCode(float fraglimitreached, float equality)
Definition world.qc:1508
void GameplayMode_DelayedInit(entity this)
Definition world.qc:666
void Shutdown()
Definition world.qc:2616
void weaponarena_available_most_update(entity this)
Definition world.qc:1968
WepSet weapons_devall()
Definition world.qc:1922
#define BADVALUE(p, val)
entity randomseed
Definition world.qc:594
void readplayerstartcvars()
Definition world.qc:1981
bool autocvar_sv_freezenonclients
Definition world.qc:2458
const float SPAWNFLAG_NO_WAYPOINTS_FOR_ITEMS
Definition world.qc:103
void readlevelcvars()
Definition world.qc:2185
void PingPLReport_Spawn()
Definition world.qc:96
float redirection_timeout
Definition world.qc:2556
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:2227
bool autocvar_sv_gameplayfix_multiplethinksperframe
Definition world.qc:2430
void DropToFloor_QC(entity this)
Definition world.qc:2301
bool world_already_spawned
Definition world.qc:760
#define BADPRESUFFIXVALUE(p, s, val)
const float LATENCY_THINKRATE
Definition world.qc:53
void RestoreGame()
Definition world.qc:2599
#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