Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
world.qc
Go to the documentation of this file.
1#include "world.qh"
2
4#include <common/constants.qh>
9#include <common/mapinfo.qh>
17#include <common/net_linked.qh>
20#include <common/playerstats.qh>
21#include <common/state.qh>
22#include <common/stats.qh>
23#include <common/teams.qh>
24#include <common/util.qh>
28#include <server/anticheat.qh>
29#include <server/antilag.qh>
30#include <server/bot/api.qh>
31#include <server/campaign.qh>
32#include <server/cheats.qh>
33#include <server/client.qh>
38#include <server/damage.qh>
39#include <server/gamelog.qh>
40#include <server/hook.qh>
41#include <server/ipban.qh>
42#include <server/items/items.qh>
43#include <server/main.qh>
44#include <server/mapvoting.qh>
46#include <server/race.qh>
47#include <server/scores.qh>
49#include <server/spawnpoints.qh>
50#include <server/teamplay.qh>
52
53const float LATENCY_THINKRATE = 10;
59{
60 float delta;
61 entity e;
62
63 delta = 3 / maxclients;
64 if(delta < sys_frametime)
65 delta = 0;
66 this.nextthink = time + delta;
67
68 e = edict_num(this.cnt + 1);
69 if(IS_CLIENT(e) && IS_REAL_CLIENT(e))
70 {
71 WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
73 WriteShort(MSG_BROADCAST, bound(1, rint(CS(e).ping), 32767));
76
77 // record latency times for clients throughout the match so we can report it to playerstats
79 {
80 CS(e).latency_sum += CS(e).ping;
81 ++CS(e).latency_cnt;
82 CS(e).latency_time = time;
83 //print("sum: ", ftos(CS(e).latency_sum), ", cnt: ", ftos(CS(e).latency_cnt), ", avg: ", ftos(CS(e).latency_sum / CS(e).latency_cnt), ".\n");
84 }
85 }
86 else
87 {
88 WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
93 }
94 this.cnt = (this.cnt + 1) % maxclients;
95}
102
104
115
117{
118 float n;
120 {
121 // cvar_set("_sv_init", "0");
122 // we do NOT set this to 0 any more, so someone "accidentally" changing
123 // to this "init" map on a dedicated server will cause no permanent harm
124
127
128 if(!DoNextMapOverride(1))
129 GotoNextMap(1);
130
131 return;
132 }
133
134 if(time < 5)
135 {
136 this.nextthink = time;
137 }
138 else
139 {
140 this.nextthink = time + 1;
141 LOG_INFO("Waiting for _sv_init being set to 1 by initialization scripts...");
142 }
143}
144
146{
147 float h;
148 string k, v, d;
149 float n, i, adding, pureadding;
150
154
155 h = buf_create();
156 buf_cvarlist(h, "", "_"); // exclude all _ cvars as they are temporary
157 n = buf_getsize(h);
158
159 adding = true;
160 pureadding = true;
161
162 for(i = 0; i < n; ++i)
163 {
164 k = bufstr_get(h, i);
165
166#define BADPREFIX_COND(p) (substring(k, 0, strlen(p)) == p)
167#define BADSUFFIX_COND(s) (substring(k, -strlen(s), -1) == s)
168
169#define BADPREFIX(p) if(BADPREFIX_COND(p)) continue
170#define BADPRESUFFIX(p, s) if(BADPREFIX_COND(p) && BADSUFFIX_COND(s)) continue
171#define BADCVAR(p) if(k == p) continue
172#define BADVALUE(p, val) if (k == p && v == val) continue
173#define BADPRESUFFIXVALUE(p, s, val) if(BADPREFIX_COND(p) && BADSUFFIX_COND(s) && v == val) continue
174
175 // general excludes and namespaces for server admin used cvars
176 BADPREFIX("help_"); // PN's server has this listed as changed, let's not rat him out for THAT
177
178 // internal
179 BADPREFIX("csqc_");
180 BADPREFIX("cvar_check_");
181 BADCVAR("gamecfg");
182 BADCVAR("g_configversion");
183 BADCVAR("halflifebsp");
184 BADCVAR("sv_mapformat_is_quake2");
185 BADCVAR("sv_mapformat_is_quake3");
186 BADPREFIX("sv_world");
187
188 // client
189 BADPREFIX("chase_");
190 BADPREFIX("cl_");
191 BADPREFIX("con_");
192 BADPREFIX("scoreboard_");
193 BADPREFIX("g_campaign");
194 BADPREFIX("g_waypointsprite_");
195 BADPREFIX("gl_");
196 BADPREFIX("joy");
197 BADPREFIX("hud_");
198 BADPREFIX("m_");
199 BADPREFIX("menu_");
200 BADPREFIX("net_slist_");
201 BADPREFIX("r_");
202 BADPREFIX("sbar_");
203 BADPREFIX("scr_");
204 BADPREFIX("snd_");
205 BADPREFIX("show");
206 BADPREFIX("sensitivity");
207 BADPREFIX("userbind");
208 BADPREFIX("v_");
209 BADPREFIX("vid_");
210 BADPREFIX("crosshair");
211 BADPREFIX("notification_");
212 BADPREFIX("prvm_");
213 BADCVAR("mod_q3bsp_lightmapmergepower");
214 BADCVAR("mod_q3bsp_nolightmaps");
215 BADCVAR("fov");
216 BADCVAR("mastervolume");
217 BADCVAR("volume");
218 BADCVAR("bgmvolume");
219 BADCVAR("in_pitch_min");
220 BADCVAR("in_pitch_max");
221 BADCVAR("bottomcolor");
222 BADCVAR("topcolor");
223 BADCVAR("playermodel");
224
225 // private
226 BADCVAR("developer");
227 BADCVAR("log_dest_udp");
228 BADCVAR("net_address");
229 BADCVAR("net_address_ipv6");
230 BADCVAR("port");
231 BADCVAR("savedgamecfg");
232 BADCVAR("serverconfig");
233 BADCVAR("sv_autoscreenshot");
234 BADCVAR("sv_heartbeatperiod");
235 BADCVAR("sv_vote_master_ids");
236 BADCVAR("sv_vote_master_password");
237 BADCVAR("sys_colortranslation");
238 BADCVAR("sys_specialcharactertranslation");
239 BADCVAR("timeformat");
240 BADCVAR("timestamps");
241 BADCVAR("g_require_stats");
242 BADCVAR("g_chatban_list");
243 BADCVAR("g_playban_list");
244 BADCVAR("g_playban_minigames");
245 BADCVAR("g_voteban_list");
246 BADPREFIX("developer_");
247 BADPREFIX("g_ban_");
248 BADPREFIX("g_banned_list");
249 BADPREFIX("g_require_stats_");
250 BADPREFIX("g_chat_flood_");
251 BADPREFIX("g_ghost_items");
252 BADPREFIX("g_playerstats_");
253 BADPREFIX("g_voice_flood_");
254 BADPREFIX("log_file");
255 BADPREFIX("quit_");
256 BADPREFIX("rcon_");
257 BADPREFIX("sv_allowdownloads");
258 BADPREFIX("sv_autodemo");
259 BADPREFIX("sv_curl_");
260 BADPREFIX("sv_eventlog");
261 BADPREFIX("sv_logscores_");
262 BADPREFIX("sv_master");
263 BADPREFIX("sv_weaponstats_");
264 BADPREFIX("sv_waypointsprite_");
265 BADCVAR("rescan_pending");
266
267 // these can contain player IDs, so better hide
268 BADPREFIX("g_forced_team_");
269 BADCVAR("sv_allow_customplayermodels_idlist");
270 BADCVAR("sv_allow_customplayermodels_speciallist");
271
272 // mapinfo
273 BADCVAR("fraglimit");
274 BADCVAR("g_arena");
275 BADCVAR("g_assault");
276 BADCVAR("g_ca");
277 BADCVAR("g_ca_teams");
278 BADCVAR("g_conquest");
279 BADCVAR("g_conquest_teams");
280 BADCVAR("g_ctf");
281 BADCVAR("g_cts");
282 BADCVAR("g_dotc");
283 BADCVAR("g_dm");
284 BADCVAR("g_domination");
285 BADCVAR("g_domination_default_teams");
286 BADCVAR("g_duel");
287 BADCVAR("g_duel_not_dm_maps");
288 BADCVAR("g_freezetag");
289 BADCVAR("g_freezetag_teams");
290 BADCVAR("g_invasion_type");
291 BADCVAR("g_jailbreak");
292 BADCVAR("g_jailbreak_teams");
293 BADCVAR("g_keepaway");
294 BADCVAR("g_keyhunt");
295 BADCVAR("g_keyhunt_teams");
296 BADCVAR("g_lms");
297 BADCVAR("g_mayhem");
298 BADCVAR("g_nexball");
299 BADCVAR("g_onslaught");
300 BADCVAR("g_race");
301 BADCVAR("g_race_laps_limit");
302 BADCVAR("g_race_qualifying_timelimit");
303 BADCVAR("g_race_qualifying_timelimit_override");
304 BADCVAR("g_runematch");
305 BADCVAR("g_shootfromeye");
306 BADCVAR("g_snafu");
307 BADCVAR("g_survival");
308 BADCVAR("g_survival_not_dm_maps");
309 BADCVAR("g_tdm");
310 BADCVAR("g_tdm_on_dm_maps");
311 BADCVAR("g_tdm_teams");
312 BADCVAR("g_tka");
313 BADCVAR("g_tka_on_ka_maps");
314 BADCVAR("g_tka_on_tdm_maps");
315 BADCVAR("g_tka_teams");
316 BADCVAR("g_tmayhem");
317 BADCVAR("g_tmayhem_teams");
318 BADCVAR("g_vip");
319 BADCVAR("leadlimit");
320 BADCVAR("teamplay");
321 BADCVAR("timelimit");
322 BADCVAR("g_mapinfo_q3compat");
323 BADCVAR("g_mapinfo_settemp_acl");
324 BADCVAR("g_mapinfo_ignore_warnings");
325 BADCVAR("g_maplist_ignore_sizes");
326 BADCVAR("g_maplist_sizes_count_bots");
327
328 // long
329 BADCVAR("hostname");
330 BADCVAR("g_maplist");
331 BADCVAR("g_maplist_mostrecent");
332 BADCVAR("sv_motd");
333 BADCVAR("sv_termsofservice_url");
334 BADCVAR("sv_adminnick");
335
336 v = cvar_string(k);
337 d = cvar_defstring(k);
338 if(v == d)
339 continue;
340
341 if(adding)
342 {
343 if (cvar_changes == "")
344 cvar_changes = "// this server runs at modified server settings:\n";
345
346 cvar_changes = strcat(cvar_changes, k, " \"", v, "\" // \"", d, "\"\n");
348 {
349 cvar_changes = "// too many settings have been changed to show them here\n";
350 adding = 0;
351 }
352 }
353
354 // now check if the changes are actually gameplay relevant
355
356 // does nothing gameplay relevant
357 BADCVAR("captureleadlimit_override");
358 BADCVAR("condump_stripcolors");
359 BADCVAR("fs_gamedir");
360 BADCVAR("g_allow_oldvortexbeam");
361 BADCVAR("g_balance_kill_delay");
362 BADCVAR("g_buffs_pickup_anyway");
363 BADCVAR("g_buffs_randomize");
364 BADCVAR("g_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();
695 if (m <= 0)
696 m = maxclients;
698 if (teamplay)
699 {
700 // automatic minplayers should be a multiple of team count
701 int down = map_minplayers % AVAILABLE_TEAMS;
702 int up = AVAILABLE_TEAMS - down;
703 map_minplayers += (up <= down && up + map_minplayers <= m) ? up : -down;
704 }
705 }
706 else
707 map_minplayers = 0; // don't display a minimum if it's not used (g_maxplayers < 0 && g_warmup >= 0)
708}
709
711{
712 VoteReset(false);
713
714 // find out good world mins/maxs bounds, either the static bounds found by looking for solid, or the mapinfo specified bounds
716 // assign reflectively to avoid "assignment to world" warning
717 for (int i = 0, done = 0, n = numentityfields(); i < n; ++i)
718 {
719 string k = entityfieldname(i);
720 vector v = (k == "mins") ? mi_min : (k == "maxs") ? mi_max : '0 0 0';
721 if (v)
722 {
723 putentityfieldstring(i, world, sprintf("%v", v));
724 if (++done == 2) break;
725 }
726 }
727 // currently, NetRadiant's limit is 131072 qu for each side
728 // distance from one corner of a 131072qu cube to the opposite corner is approx. 227023 qu
729 // set the distance according to map size but don't go over the limit to avoid issues with float precision
730 // in case somebody makes extremely large maps
731 max_shot_distance = min(230000, vlen(world.maxs - world.mins));
732
734 GameRules_teams(false);
735
736 if (!cvar_value_issafe(world.fog))
737 {
738 LOG_INFO("The current map contains a potentially harmful fog setting, ignored");
739 world.fog = string_null;
740 }
741 if(MapInfo_Map_fog != "")
742 {
743 if(MapInfo_Map_fog == "none")
744 world.fog = string_null;
745 else
747 }
749
751
754 cvar_set("_sv_vote_gametype_custom", ""); // clear it immediately so it can't get stuck
755
758
760}
761
763spawnfunc(worldspawn)
764{
765 // Must be checked first because we don't always error() and don't want to print this twice.
767 {
768 string msg = "world already spawned - your map may have EXACTLY ONE worldspawn!";
769 if (q3compat) // must be set during (first) worldspawn
770 {
771 // Q3 ignores spurious/extra worldspawn entities, test map: q3dmz_carnage
772 LOG_WARN(msg);
773 delete(this);
774 return;
775 }
776 else
777 error(msg);
778 }
780
781#ifdef WATERMARK
782 string watermark_start = cvar_string("sv_watermark_start");
783 if (watermark_start == "") // always true on Xonotic (re)start
784 cvar_set("sv_watermark_start", WATERMARK);
785 else if (watermark_start != WATERMARK) // true when qc code has been recompiled on a different git commit
786 {
787 LOG_INFOF(
788 "\n^1 Warning: ^3the server QC program was updated without a full restart."
789 "\n^3 Please restart the Xonotic executable otherwise you may get random bugs."
790 "\n\n");
791 }
792#endif
793
795
796 cvar_set("_endmatch", "0");
797
799 {
801 }
802 else
803 {
805 }
806
807 bool wantrestart = false;
808 {
810 {
811 // DP unloads dlcache pk3s before starting a listen server since https://gitlab.com/xonotic/darkplaces/-/merge_requests/134
812 // restore csqc_progname too
813 string expect = "csprogs.dat";
814 wantrestart = cvar_string("csqc_progname") != expect;
815 cvar_set("csqc_progname", expect);
816 }
817 else
818 {
819 // Try to use versioned csprogs from pk3
820 // Only ever use versioned csprogs.dat files on dedicated servers;
821 // we need to reset csqc_progname on clients ourselves, and it's easier if the client's release name is constant
822 string pk3csprogs = "csprogs-" WATERMARK ".dat";
823 // This always works; fall back to it if a versioned csprogs.dat is suddenly missing
824 string select = "csprogs.dat";
825 if (fexists(pk3csprogs)) select = pk3csprogs;
826 if (cvar_string("csqc_progname") != select)
827 {
828 cvar_set("csqc_progname", select);
829 wantrestart = true;
830 }
831 // Check for updates on startup
832 // We do it this way for atomicity so that connecting clients still match the server progs and don't disconnect
833 int sentinel = fopen("progs.txt", FILE_READ);
834 if (sentinel >= 0)
835 {
836 string switchversion = fgets(sentinel);
837 fclose(sentinel);
838 if (switchversion != "" && switchversion != WATERMARK)
839 {
840 LOG_INFOF("Switching progs: " WATERMARK " -> %s", switchversion);
841 // if it doesn't exist, assume either:
842 // a) the current program was overwritten
843 // b) this is a client only update
844 string newprogs = sprintf("progs-%s.dat", switchversion);
845 if (fexists(newprogs))
846 {
847 cvar_set("sv_progs", newprogs);
848 wantrestart = true;
849 }
850 string newcsprogs = sprintf("csprogs-%s.dat", switchversion);
851 if (fexists(newcsprogs))
852 {
853 cvar_set("csqc_progname", newcsprogs);
854 wantrestart = true;
855 }
856 }
857 }
858 }
859 if (wantrestart)
860 {
861 LOG_INFO("Restart requested");
863 // let initialization continue, shutdown depends on it
864 }
865 }
866
867 delete_fn = remove_safely; // during spawning, watch what you remove!
868
869 cvar_changes_init(); // do this very early now so it REALLY matches the server config
870
871 // default to RACE_RECORD, can be overwritten by gametypes
873
874 // needs to be done so early because of the constants they create
875 static_init();
876
878
880
881 // 0 normal
882 lightstyle(0, "m");
883
884 // 1 FLICKER (first variety)
885 lightstyle(1, "mmnmmommommnonmmonqnmmo");
886
887 // 2 SLOW STRONG PULSE
888 lightstyle(2, "abcdefghijklmnopqrstuvwxyzyxwvutsrqponmlkjihgfedcba");
889
890 // 3 CANDLE (first variety)
891 lightstyle(3, "mmmmmaaaaammmmmaaaaaabcdefgabcdefg");
892
893 // 4 FAST STROBE
894 lightstyle(4, "mamamamamama");
895
896 // 5 GENTLE PULSE 1
897 lightstyle(5,"jklmnopqrstuvwxyzyxwvutsrqponmlkj");
898
899 // 6 FLICKER (second variety)
900 lightstyle(6, "nmonqnmomnmomomno");
901
902 // 7 CANDLE (second variety)
903 lightstyle(7, "mmmaaaabcdefgmmmmaaaammmaamm");
904
905 // 8 CANDLE (third variety)
906 lightstyle(8, "mmmaaammmaaammmabcdefaaaammmmabcdefmmmaaaa");
907
908 // 9 SLOW STROBE (fourth variety)
909 lightstyle(9, "aaaaaaaazzzzzzzz");
910
911 // 10 FLUORESCENT FLICKER
912 lightstyle(10, "mmamammmmammamamaaamammma");
913
914 // 11 SLOW PULSE NOT FADE TO BLACK
915 lightstyle(11, "abcdefghijklmnopqrrqponmlkjihgfedcba");
916
917 // styles 32-62 are assigned by the spawnfunc_light program for switchable lights
918
919 // 63 testing
920 lightstyle(63, "a");
921
924 else
925 PlayerStats_GameReport_Init(); // we need this to be initiated before InitGameplayMode
926
928
933
935
936 player_count = 0;
941
943
944 // NOTE for matchid:
945 // changing the logic generating it is okay. But:
946 // it HAS to stay <= 64 chars
947 // character set: ASCII 33-126 without the following characters: : ; ' " \ $
948 // strftime(false, "%s") isn't reliable, see strftime_s description
949 matchid = strzone(sprintf("%d.%s.%06d", autocvar_sv_eventlog_files_counter, strftime_s(), random() * 1000000));
950
952 GameLogInit(); // requires matchid to be set
953
955
958
959 Ban_LoadBans();
960
963
966
967 // quake 3 music support
968 // bones_was_here: Q3 doesn't support .noise but the Nexuiz _MapInfo_Generate() does.
969 // TODO: Q3 supports an optional intro file: "music/intro.wav music/loop.wav"
970 string music = GetField_fullspawndata(world, "music", true);
971 if (music || world.noise)
972 // prefer .music over .noise
973 strcpy(clientstuff, strcat(clientstuff, "cd loop \"", (music ? music : world.noise), "\"\n"));
974
975 if(whichpack(strcat("maps/", mapname, ".cfg")) != "")
976 {
977 int fd = fopen(strcat("maps/", mapname, ".cfg"), FILE_READ);
978 if(fd != -1)
979 {
980 string s;
981 while((s = fgets(fd)))
982 {
983 int l = tokenize_console(s);
984 if(l < 2)
985 continue;
986 if(argv(0) == "cd")
987 {
988 string trackname = argv(2);
989 LOG_INFO("Found ^1UNSUPPORTED^7 cd loop command in .cfg file; put this line in mapinfo instead:");
990 LOG_INFO(" cdtrack ", trackname);
991 if (cvar_value_issafe(trackname))
992 {
993 string newstuff = strcat(clientstuff, "cd loop \"", trackname, "\"\n");
994 strcpy(clientstuff, newstuff);
995 }
996 }
997 else if(argv(0) == "fog")
998 {
999 LOG_INFO("Found ^1UNSUPPORTED^7 fog command in .cfg file; put this line in worldspawn in the .map/.bsp/.ent file instead:");
1000 LOG_INFO(" \"fog\" \"", s, "\"");
1001 }
1002 else if(argv(0) == "set")
1003 {
1004 LOG_INFO("Found ^1UNSUPPORTED^7 set command in .cfg file; put this line in mapinfo instead:");
1005 LOG_INFO(" clientsettemp_for_type all ", argv(1), " ", argv(2));
1006 }
1007 else if(argv(0) != "//")
1008 {
1009 LOG_INFO("Found ^1UNSUPPORTED^7 set command in .cfg file; put this line in mapinfo instead:");
1010 LOG_INFO(" clientsettemp_for_type all ", argv(0), " ", argv(1));
1011 }
1012 }
1013 fclose(fd);
1014 }
1015 }
1016
1018
1019 Nagger_Init();
1020
1021 // set up information replies for clients and server to use
1025 bool records_available = false;
1026 for(int i = 0; i < 10; ++i)
1027 {
1028 string s = getrecords(i);
1029 if (s != "")
1030 {
1031 records_reply[i] = strzone(s);
1032 records_available = true;
1033 }
1034 }
1035 if (!records_available)
1036 records_reply[0] = "No records available for the current gametype.\n";
1039
1040 // begin other init
1044
1045 CheatInit();
1046
1047 if (!wantrestart) localcmd("\n_sv_hook_gamestart ", GetGametype(), "\n");
1048
1049 // fill sv_curl_serverpackages from .serverpackage files
1051 {
1052 string s = "csprogs-" WATERMARK ".dat";
1053 // remove automatically managed files from the list to prevent duplicates
1054 for (int i = 0, n = tokenize_console(cvar_string("sv_curl_serverpackages")); i < n; ++i)
1055 {
1056 string pkg = argv(i);
1057 if (startsWith(pkg, "csprogs-")) continue;
1058 if (endsWith(pkg, "-serverpackage.txt")) continue;
1059 if (endsWith(pkg, ".serverpackage")) continue; // OLD legacy
1060 s = cons(s, pkg);
1061 }
1062 // add automatically managed files to the list
1063 #define X(match) MACRO_BEGIN \
1064 int fd = search_begin(match, true, false); \
1065 if (fd >= 0) \
1066 { \
1067 for (int i = 0, j = search_getsize(fd); i < j; ++i) \
1068 { \
1069 s = cons(s, search_getfilename(fd, i)); \
1070 } \
1071 search_end(fd); \
1072 } \
1073 MACRO_END
1074 X("*-serverpackage.txt");
1075 X("*.serverpackage");
1076 #undef X
1077 cvar_set("sv_curl_serverpackages", s);
1078 }
1079
1080 // MOD AUTHORS: change this, and possibly remove a few of the blocks below to ignore certain changes
1081 modname = "Xonotic";
1082 // physics/balance/config changes that count as mod
1083 if(cvar_string("g_mod_physics") != cvar_defstring("g_mod_physics"))
1084 modname = cvar_string("g_mod_physics");
1085 if(cvar_string("g_mod_balance") != cvar_defstring("g_mod_balance") && cvar_string("g_mod_balance") != "Testing")
1086 modname = cvar_string("g_mod_balance");
1087 if(cvar_string("g_mod_config") != cvar_defstring("g_mod_config"))
1088 modname = cvar_string("g_mod_config");
1089 // extra mutators that deserve to count as mod
1090 MUTATOR_CALLHOOK(SetModname, modname);
1091 modname = M_ARGV(0, string);
1092
1093 // save it for later
1095
1096 WinningConditionHelper(this); // set worldstatus
1097
1098 if (autocvar_sv_autopause && autocvar_sv_dedicated && !wantrestart)
1099 // INITPRIO_LAST is too soon: bots either didn't join yet or didn't leave yet, see: bot_fixcount()
1101
1102 // load entity data outputted by create_scrshot_ent
1103 string filename = strcat("data/", mapname, "_scrshot_ent.txt");
1104 if(!find(NULL, classname, "info_autoscreenshot") && fexists(filename))
1105 loadfromfile(filename);
1106
1109}
1110
1112{
1113 //makestatic (this); // Who the f___ did that?
1114 delete(this);
1115}
1116
1117bool MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundmax, float goodcontents, float badcontents, float badsurfaceflags, int attempts, float maxaboveground, float minviewdistance, bool frompos)
1118{
1119 float m = e.dphitcontentsmask;
1120 e.dphitcontentsmask = goodcontents | badcontents;
1121
1122 vector org = boundmin;
1123 vector delta = boundmax - boundmin;
1124
1125 vector start, end;
1126 start = end = org;
1127 int j; // used after the loop
1128 for(j = 0; j < attempts; ++j)
1129 {
1130 start.x = org.x + random() * delta.x;
1131 start.y = org.y + random() * delta.y;
1132 start.z = org.z + random() * delta.z;
1133
1134 // rule 1: start inside world bounds, and outside
1135 // solid, and don't start from somewhere where you can
1136 // fall down to evil
1137 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1138 if (trace_fraction >= 1)
1139 continue;
1140 if (trace_startsolid)
1141 continue;
1142 if (trace_dphitcontents & badcontents)
1143 continue;
1144 if (trace_dphitq3surfaceflags & badsurfaceflags)
1145 continue;
1146
1147 // rule 2: if we are too high, lower the point
1148 if (trace_fraction * delta.z > maxaboveground)
1149 start = trace_endpos + '0 0 1' * maxaboveground;
1150 vector enddown = trace_endpos;
1151
1152 // rule 3: make sure we aren't outside the map. This only works
1153 // for somewhat well formed maps. A good rule of thumb is that
1154 // the map should have a convex outside hull.
1155 // these can be traceLINES as we already verified the starting box
1156 vector mstart = start + 0.5 * (e.mins + e.maxs);
1157 traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1158 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1159 continue;
1160 traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1161 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1162 continue;
1163 traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1164 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1165 continue;
1166 traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1167 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1168 continue;
1169 traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1170 if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1171 continue;
1172
1173 // rule 4: we must "be seen" by some spawnpoint or item
1174 // Note that checkpvs from mstart to item can detect visibility if mstart is behind
1175 // patch brushes (curved walls) that don't block visibility from the outside, however
1176 // the next traceline from item to mstart correctly detects invisibility in this case
1177 entity sp = NULL;
1178 if(frompos)
1179 {
1180 if((traceline(e.origin, mstart, MOVE_NORMAL, e), trace_fraction) >= 1)
1181 sp = e;
1182 }
1183 if(!sp)
1184 {
1185 IL_EACH(g_spawnpoints, checkpvs(mstart, it),
1186 {
1187 if((traceline(it.origin, mstart, MOVE_NORMAL, e), trace_fraction) >= 1)
1188 {
1189 sp = it;
1190 break;
1191 }
1192 });
1193 }
1194 if(!sp)
1195 {
1196 int items_checked = 0;
1197 IL_EACH(g_items, checkpvs(mstart, it),
1198 {
1199 if((traceline(it.origin + (it.mins + it.maxs) * 0.5, mstart, MOVE_NORMAL, e), trace_fraction) >= 1)
1200 {
1201 sp = it;
1202 break;
1203 }
1204
1205 ++items_checked;
1206 if(items_checked >= attempts)
1207 break; // sanity
1208 });
1209
1210 if(!sp)
1211 continue;
1212 }
1213
1214 float vlen_delta = vlen(delta);
1215 // find a random vector to "look at"
1216 end.x = org.x + random() * delta.x;
1217 end.y = org.y + random() * delta.y;
1218 end.z = org.z + random() * delta.z;
1219 end = start + normalize(end - start) * vlen_delta;
1220
1221 // rule 4: start TO end must not be too short
1222 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1224 continue;
1225 if(trace_fraction < minviewdistance / vlen_delta)
1226 continue;
1227
1228 // rule 5: don't want to look at sky
1230 continue;
1231
1232 // rule 6: we must not end up in trigger_hurt
1233 if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1234 continue;
1235
1236 break;
1237 }
1238
1239 e.dphitcontentsmask = m;
1240
1241 if(j < attempts)
1242 {
1243 setorigin(e, start);
1244 e.angles = vectoangles(end - start);
1245 LOG_DEBUG("Needed ", ftos(j + 1), " attempts");
1246 return true;
1247 }
1248 return false;
1249}
1250
1251float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1252{
1253 return MoveToRandomLocationWithinBounds(e, world.mins, world.maxs, goodcontents, badcontents, badsurfaceflags, attempts, maxaboveground, minviewdistance, false);
1254}
1255
1256/*
1257===============================================================================
1258
1259RULES
1260
1261===============================================================================
1262*/
1263
1264void DumpStats(float final)
1265{
1266 float file;
1267 string s;
1268 float to_console;
1269 float to_eventlog;
1270 float to_file;
1271 float i;
1272
1273 to_console = autocvar_sv_logscores_console;
1274 to_eventlog = autocvar_sv_eventlog;
1276
1277 if(!final)
1278 {
1279 to_console = true; // always print printstats replies
1280 to_eventlog = false; // but never print them to the event log
1281 }
1282
1283 if(to_eventlog)
1285 to_console = false; // otherwise we get the output twice
1286
1287 if(final)
1288 s = ":scores:";
1289 else
1290 s = ":status:";
1291 s = strcat(s, GetGametype(), "_", GetMapname(), ":", ftos(rint(time)));
1292
1293 if(to_console)
1294 LOG_HELP(s);
1295 if(to_eventlog)
1296 GameLogEcho(s);
1297
1298 file = -1;
1299 if(to_file)
1300 {
1302 if(file == -1)
1303 to_file = false;
1304 else
1305 fputs(file, strcat(s, "\n"));
1306 }
1307
1308 s = strcat(":labels:player:", GetPlayerScoreString(NULL, 0));
1309 if(to_console)
1310 LOG_HELP(s);
1311 if(to_eventlog)
1312 GameLogEcho(s);
1313 if(to_file)
1314 fputs(file, strcat(s, "\n"));
1315
1317 s = strcat(":player:see-labels:", GetPlayerScoreString(it, 0), ":");
1318 s = strcat(s, ftos(rint(time - CS(it).jointime)), ":");
1319 if(IS_PLAYER(it) || INGAME_JOINED(it))
1320 s = strcat(s, ftos(it.team), ":");
1321 else
1322 s = strcat(s, "spectator:");
1323
1324 if(to_console)
1325 LOG_HELP(s, playername(it.netname, it.team, false));
1326 if(to_eventlog)
1327 GameLogEcho(strcat(s, ftos(it.playerid), ":", playername(it.netname, it.team, false)));
1328 if(to_file)
1329 fputs(file, strcat(s, playername(it.netname, it.team, false), "\n"));
1330 });
1331
1332 if(teamplay)
1333 {
1334 s = strcat(":labels:teamscores:", GetTeamScoreString(0, 0));
1335 if(to_console)
1336 LOG_HELP(s);
1337 if(to_eventlog)
1338 GameLogEcho(s);
1339 if(to_file)
1340 fputs(file, strcat(s, "\n"));
1341
1342 for(i = 1; i < 16; ++i)
1343 {
1344 s = strcat(":teamscores:see-labels:", GetTeamScoreString(i, 0));
1345 s = strcat(s, ":", ftos(i));
1346 if(to_console)
1347 LOG_HELP(s);
1348 if(to_eventlog)
1349 GameLogEcho(s);
1350 if(to_file)
1351 fputs(file, strcat(s, "\n"));
1352 }
1353 }
1354
1355 if(to_console)
1356 LOG_HELP(":end");
1357 if(to_eventlog)
1358 GameLogEcho(":end");
1359 if(to_file)
1360 {
1361 fputs(file, ":end\n");
1362 fclose(file);
1363 }
1364}
1365
1366// it should be called by gametypes where players can join a team but have to wait before playing
1367// it puts players who joined too late (without being able to play) back to spectators
1368// if prev_team_field is not team it also puts players who previously switched team (without being
1369// able to play on the new team) back to previous team
1371{
1372 bool fix_team = (teamplay && prev_team_field != team);
1373 FOREACH_CLIENT(true,
1374 {
1375 if (!IS_PLAYER(it) && INGAME_JOINING(it))
1376 {
1378 PutObserverInServer(it, true, false);
1379 bprint(playername(it.netname, it.team, false), " has been moved back to spectator\n");
1380 it.winning = false;
1381 }
1382 else if (fix_team && INGAME_JOINED(it) && it.(prev_team_field) && it.team != it.(prev_team_field))
1383 {
1385 if (MoveToTeam(it, Team_TeamToIndex(it.(prev_team_field)), 6))
1386 {
1387 string pl_name = playername(it.netname, it.team, false);
1388 bprint(pl_name, " has been moved back to the ", Team_ColoredFullName(it.team), "\n");
1389 }
1390 it.winning = (it.team == WinningConditionHelper_winnerteam);
1391 }
1392 });
1393}
1394
1399
1400/*
1401go to the next level for deathmatch
1402only called if a time or frag limit has expired
1403*/
1405{
1406 cvar_set("_endmatch", "0");
1407 game_stopped = true;
1408 intermission_running = true; // game over
1409
1410 // enforce a wait time before allowing changelevel
1411 if(player_count > 0)
1413 else
1415
1416 /*
1417 WriteByte (MSG_ALL, SVC_CDTRACK);
1418 WriteByte (MSG_ALL, 3);
1419 WriteByte (MSG_ALL, 3);
1420 // done in FixIntermission
1421 */
1422
1423 //pos = FindIntermission ();
1424
1425 VoteReset(true);
1426
1427 MUTATOR_CALLHOOK(MatchEnd_BeforeScores);
1428
1429 DumpStats(true);
1430
1431 // send statistics
1434
1435 Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_Null); // kill all centerprints now
1436
1438 GameLogEcho(":gameover");
1439
1440 GameLogClose();
1441
1442 int winner_team = 0;
1443 FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), {
1445 if(it.winning)
1446 {
1447 if (teamplay && !winner_team)
1448 {
1449 winner_team = it.team;
1450 bprint(Team_ColorCode(winner_team), Team_ColorName_Upper(winner_team), "^7 team wins the match\n");
1451 }
1452 bprint(playername(it.netname, it.team, false), " ^7wins\n");
1453 }
1454 });
1455
1457
1460
1461 MUTATOR_CALLHOOK(MatchEnd);
1462
1463 localcmd("\nsv_hook_gameend\n");
1464}
1465
1466
1468{
1469 // Check first whether normal overtimes could be added before initiating suddendeath mode
1470 // - for this timelimit_overtime needs to be >0 of course
1471 // - also check the winning condition calculated in the previous frame and only add normal overtime
1472 // again, if at the point at which timelimit would be extended again, still no winner was found
1476 {
1477 return 1; // need to call InitiateOvertime later
1478 }
1479 else
1480 {
1482 {
1484 {
1485 checkrules_suddendeathend = time; // no suddendeath in campaign
1486 }
1487 else
1488 {
1491 }
1494 }
1495 return 0;
1496 }
1497}
1498
1499void InitiateOvertime() // ONLY call this if InitiateSuddenDeath returned true
1500{
1502 // NOTE: here overtimes can never be < 0 so it can be safely sent as (unsigned) int stat; we ignore
1503 // the upper limit of OVERTIME_SUDDENDEATH - 1 = 16777215 - 1 that in practice can never be reached in game
1505 //add one more overtime by simply extending the timelimit
1507 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_TIME, autocvar_timelimit_overtime * 60);
1508}
1509
1510float GetWinningCode(float fraglimitreached, float equality)
1511{
1512 if(autocvar_g_campaign == 1)
1513 {
1514 if(fraglimitreached)
1515 return WINNING_YES;
1516 else
1517 return WINNING_NO;
1518 }
1519 else
1520 {
1521 if(equality)
1522 {
1523 if(fraglimitreached)
1525 else
1526 return WINNING_NEVER;
1527 }
1528 else
1529 {
1530 if(fraglimitreached)
1531 return WINNING_YES;
1532 else
1533 return WINNING_NO;
1534 }
1535 }
1536}
1537
1538// set the .winning flag for exactly those players with a given field value
1539void SetWinners(.float field, float value)
1540{
1541 FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), { it.winning = (it.(field) == value); });
1542}
1543
1544// set the .winning flag for those players with a given field value
1545void AddWinners(.float field, float value)
1546{
1547 FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), {
1548 if(it.(field) == value)
1549 it.winning = 1;
1550 });
1551}
1552
1553// clear the .winning flags
1555{
1556 FOREACH_CLIENT(IS_PLAYER(it) || INGAME(it), { it.winning = 0; });
1557}
1558
1560float WinningCondition_Scores(float limit, float leadlimit)
1561{
1562 // TODO make everything use THIS winning condition (except LMS)
1564
1565 if(teamplay)
1566 {
1567 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
1568 {
1571 }
1572 }
1573
1574 ClearWinners();
1579
1581 {
1584 limit = -limit;
1585 }
1586
1588 leadlimit = 0; // not supported in this mode
1589
1590 if(MUTATOR_CALLHOOK(Scores_CountFragsRemaining))
1591 {
1592 float fragsleft;
1594 {
1595 fragsleft = 1;
1596 }
1597 else
1598 {
1599 fragsleft = FLOAT_MAX;
1600 float leadingfragsleft = FLOAT_MAX;
1601 if (limit)
1602 fragsleft = limit - WinningConditionHelper_topscore;
1603 if (leadlimit)
1605
1606 if (limit && leadlimit && autocvar_leadlimit_and_fraglimit)
1607 fragsleft = max(fragsleft, leadingfragsleft);
1608 else
1609 fragsleft = min(fragsleft, leadingfragsleft);
1610 }
1611
1612 if (fragsleft_last != fragsleft) // do not announce same remaining frags multiple times
1613 {
1614 if (fragsleft == 1)
1615 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_REMAINING_FRAG_1);
1616 else if (fragsleft == 2)
1617 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_REMAINING_FRAG_2);
1618 else if (fragsleft == 3)
1619 Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_REMAINING_FRAG_3);
1620
1621 fragsleft_last = fragsleft;
1622 }
1623 }
1624
1625 bool fraglimit_reached = (limit && WinningConditionHelper_topscore >= limit);
1626 bool leadlimit_reached = (leadlimit && WinningConditionHelper_topscore - WinningConditionHelper_secondscore >= leadlimit);
1627
1628 bool limit_reached;
1629 // only respect leadlimit_and_fraglimit when both limits are set or the game will never end
1630 if (limit && leadlimit && autocvar_leadlimit_and_fraglimit)
1631 limit_reached = (fraglimit_reached && leadlimit_reached);
1632 else
1633 limit_reached = (fraglimit_reached || leadlimit_reached);
1634
1635 return GetWinningCode(
1636 WinningConditionHelper_topscore && limit_reached,
1638 );
1639}
1640
1642{
1643 if(have_team_spawns <= 0)
1644 return WINNING_NO;
1645
1647 return WINNING_NO;
1648
1650 return WINNING_NO;
1651
1652 for (int i = 1; i <= AVAILABLE_TEAMS; ++i)
1653 {
1655 }
1656
1657 FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
1658 {
1659 if (Team_IsValidTeam(it.team))
1660 {
1661 Team_SetTeamScore(Team_GetTeam(it.team), 1);
1662 }
1663 });
1664
1665 IL_EACH(g_spawnpoints, true,
1666 {
1667 if (Team_IsValidTeam(it.team))
1668 {
1669 Team_SetTeamScore(Team_GetTeam(it.team), 1);
1670 }
1671 });
1672
1673 ClearWinners();
1674 float team1_score = Team_GetTeamScore(Team_GetTeamFromIndex(1));
1675 float team2_score = Team_GetTeamScore(Team_GetTeamFromIndex(2));
1676 float team3_score = Team_GetTeamScore(Team_GetTeamFromIndex(3));
1677 float team4_score = Team_GetTeamScore(Team_GetTeamFromIndex(4));
1678 if(team1_score + team2_score + team3_score + team4_score == 0)
1679 {
1680 checkrules_equality = true;
1681 return WINNING_YES;
1682 }
1683 else if(team1_score + team2_score + team3_score + team4_score == 1)
1684 {
1685 float t, i;
1686 if(team1_score)
1687 t = 1;
1688 else if(team2_score)
1689 t = 2;
1690 else if(team3_score)
1691 t = 3;
1692 else // if(team4_score)
1693 t = 4;
1695 for(i = 0; i < MAX_TEAMSCORE; ++i)
1696 {
1697 for (int j = 1; j <= AVAILABLE_TEAMS; ++j)
1698 {
1699 if (t == j)
1700 {
1701 continue;
1702 }
1703 if (!TeamBalance_IsTeamAllowed(balance, j))
1704 {
1705 continue;
1706 }
1708 }
1709 }
1710
1711 AddWinners(team, t);
1712 return WINNING_YES;
1713 }
1714 else
1715 return WINNING_NO;
1716}
1717
1718/*
1719============
1720CheckRules_World
1721
1722Exit deathmatch games upon conditions
1723============
1724*/
1726{
1727 VoteThink();
1728 MapVote_Think();
1729
1731
1732 if (intermission_running) // someone else quit the game already
1733 {
1734 if(player_count == 0) // Nobody there? Then let's go to the next map
1735 MapVote_Start();
1736 // this will actually check the player count in the next frame
1737 // again, but this shouldn't hurt
1738 return;
1739 }
1740
1741 float timelimit = autocvar_timelimit * 60;
1742 float fraglimit = autocvar_fraglimit;
1743 float leadlimit = autocvar_leadlimit;
1744 if (leadlimit < 0) leadlimit = 0;
1745
1746 if(warmup_stage || time <= game_starttime) // NOTE: this is <= to prevent problems in the very tic where the game starts
1747 {
1748 if(timelimit > 0)
1749 timelimit = 0; // timelimit is not made for warmup
1750 if(fraglimit > 0)
1751 fraglimit = 0; // no fraglimit for now
1752 leadlimit = 0; // no leadlimit for now
1753 }
1754
1755 if (autocvar__endmatch || timelimit < 0)
1756 {
1757 // endmatch
1758 NextLevel();
1759 return;
1760 }
1761
1762 if(timelimit > 0)
1763 timelimit += game_starttime;
1764
1765 int overtimes_prev = overtimes;
1766 int wantovertime = 0;
1767
1769 {
1771 {
1774 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_RACE_FINISHLAP);
1775 else
1776 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_FRAG);
1777 }
1778 }
1779 else
1780 {
1781 if (timelimit && time >= timelimit)
1782 {
1783 if(g_race && (g_race_qualifying == 2) && timelimit > 0)
1784 {
1785 float totalplayers;
1786 float playerswithlaps;
1787 float readyplayers;
1788 totalplayers = playerswithlaps = readyplayers = 0;
1790 ++totalplayers;
1791 if(GameRules_scoring_add(it, RACE_FASTEST, 0))
1792 ++playerswithlaps;
1793 if(it.ready)
1794 ++readyplayers;
1795 });
1796
1797 // at least 2 of the players have completed a lap: start the RACE
1798 // otherwise, the players should end the qualifying on their own
1799 if(readyplayers || playerswithlaps >= 2)
1800 {
1802 ReadyRestart(true); // go to race
1803 return;
1804 }
1805 else
1806 wantovertime |= InitiateSuddenDeath();
1807 }
1808 else
1809 wantovertime |= InitiateSuddenDeath();
1810 }
1811 }
1812
1814 {
1815 NextLevel();
1816 return;
1817 }
1818
1819 int checkrules_status = WinningCondition_RanOutOfSpawns();
1820 if(checkrules_status == WINNING_YES)
1821 bprint("Hey! Someone ran out of spawns!\n");
1822 else if(MUTATOR_CALLHOOK(CheckRules_World, checkrules_status, timelimit, fraglimit))
1823 checkrules_status = M_ARGV(0, float);
1824 else
1825 checkrules_status = WinningCondition_Scores(fraglimit, leadlimit);
1826
1827 if(checkrules_status == WINNING_STARTSUDDENDEATHOVERTIME)
1828 {
1829 checkrules_status = WINNING_NEVER;
1831 wantovertime |= InitiateSuddenDeath();
1832 }
1833
1834 if(checkrules_status == WINNING_NEVER)
1835 // equality cases! Nobody wins if the overtime ends in a draw.
1836 ClearWinners();
1837
1838 if(wantovertime)
1839 {
1840 if(checkrules_status == WINNING_NEVER)
1842 else
1843 checkrules_status = WINNING_YES;
1844 }
1845
1847 if(checkrules_status != WINNING_NEVER || time >= checkrules_suddendeathend)
1848 checkrules_status = WINNING_YES;
1849
1850 if(checkrules_status == WINNING_YES)
1851 {
1852 if (overtimes == OVERTIME_SUDDENDEATH && overtimes != overtimes_prev)
1853 {
1854 // if suddendeathend overtime has just begun, revert it
1856 overtimes = overtimes_prev;
1857 }
1858 //print("WINNING\n");
1859 NextLevel();
1860 }
1861}
1862
1863int want_weapon(entity weaponinfo, int allguns)
1864{
1865 int d = 0;
1866 bool allow_mutatorblocked = false;
1867
1868 if(!weaponinfo.m_id)
1869 return 0;
1870
1871 bool mutator_returnvalue = MUTATOR_CALLHOOK(WantWeapon, weaponinfo, d, allguns, allow_mutatorblocked);
1872 d = M_ARGV(1, float);
1873 allguns = M_ARGV(2, int);
1874 allow_mutatorblocked = M_ARGV(3, bool);
1875
1876 if(allguns == 1)
1877 d = boolean(!(weaponinfo.spawnflags & WEP_FLAG_HIDDEN));
1878 else if(allguns == 2)
1879 d = boolean((weaponinfo.spawnflags & WEP_FLAG_NORMAL) && !(weaponinfo.spawnflags & WEP_FLAG_HIDDEN));
1880 else if(!mutator_returnvalue)
1881 d = !(!weaponinfo.weaponstart);
1882
1883 if(!allow_mutatorblocked && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
1884 d = 0;
1885
1886 float t = weaponinfo.weaponstartoverride;
1887
1888 //LOG_INFOF("want_weapon: %s - d: %d t: %d\n", weaponinfo.netname, d, t);
1889
1890 // bit order in t:
1891 // 1: want or not
1892 // 2: is default?
1893 // 4: is set by default?
1894 if(t < 0)
1895 t = 4 | (3 * d);
1896 else
1897 t |= (2 * d);
1898
1899 return t;
1900}
1901
1904{
1905 WepSet ret = '0 0 0';
1906 FOREACH(Weapons, it != WEP_Null, {
1907 int w = want_weapon(it, false);
1908 if (w & 1)
1909 ret |= it.m_wepset;
1910 });
1911 return ret;
1912}
1913
1915{
1916 WepSet ret = '0 0 0';
1917 FOREACH(Weapons, it != WEP_Null, {
1918 if (!(it.spawnflags & (WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN)))
1919 ret |= it.m_wepset;
1920 });
1921 return ret;
1922}
1923
1925{
1926 WepSet ret = '0 0 0';
1927 FOREACH(Weapons, it != WEP_Null,
1928 {
1929 ret |= it.m_wepset;
1930 });
1931 return ret;
1932}
1933
1935{
1936 WepSet ret = '0 0 0';
1937 FOREACH(Weapons, it != WEP_Null, {
1938 if ((it.spawnflags & WEP_FLAG_NORMAL) && !(it.spawnflags & (WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN)))
1939 ret |= it.m_wepset;
1940 });
1941 return ret;
1942}
1943
1945{
1946 if (weaponsInMapAll)
1947 {
1949 }
1950 else
1951 {
1952 // if no weapons are available on the map, just fall back to all weapons arena
1954 }
1955}
1956
1958{
1959 if (weaponsInMapAll)
1960 {
1962 }
1963 else
1964 {
1965 // if no weapons are available on the map, just fall back to devall weapons arena
1967 }
1968}
1969
1971{
1972 if (weaponsInMapAll)
1973 {
1975 }
1976 else
1977 {
1978 // if no weapons are available on the map, just fall back to most weapons arena
1980 }
1981}
1982
1984{
1985 // initialize starting values for players
1986 start_weapons = '0 0 0';
1987 start_weapons_default = '0 0 0';
1988 start_weapons_defaultmask = '0 0 0';
1989 start_items = 0;
1991 start_ammo_nails = 0;
1993 start_ammo_cells = 0;
1994 if (random_start_ammo == NULL)
1995 {
1997 }
1998 start_health = cvar("g_balance_health_start");
1999 start_armorvalue = cvar("g_balance_armor_start");
2000
2001 g_weaponarena = 0;
2002 g_weaponarena_weapons = '0 0 0';
2003
2004 string s = cvar_string("g_weaponarena");
2005
2006 MUTATOR_CALLHOOK(SetWeaponArena, s);
2007 s = M_ARGV(0, string);
2008
2009 if (s == "0" || s == "")
2010 {
2011 // no arena
2012 }
2013 else if (s == "off")
2014 {
2015 // forcibly turn off weaponarena
2016 }
2017 else if (s == "all" || s == "1")
2018 {
2019 g_weaponarena = 1;
2020 g_weaponarena_list = "All Weapons Arena";
2022 }
2023 else if (s == "devall")
2024 {
2025 g_weaponarena = 1;
2026 g_weaponarena_list = "Dev All Weapons Arena";
2028 }
2029 else if (s == "most")
2030 {
2031 g_weaponarena = 1;
2032 g_weaponarena_list = "Most Weapons Arena";
2034 }
2035 else if (s == "all_available")
2036 {
2037 g_weaponarena = 1;
2038 g_weaponarena_list = "All Available Weapons Arena";
2039
2040 // this needs to run after weaponsInMapAll is initialized
2042 }
2043 else if (s == "devall_available")
2044 {
2045 g_weaponarena = 1;
2046 g_weaponarena_list = "Dev All Available Weapons Arena";
2047
2048 // this needs to run after weaponsInMapAll is initialized
2050 }
2051 else if (s == "most_available")
2052 {
2053 g_weaponarena = 1;
2054 g_weaponarena_list = "Most Available Weapons Arena";
2055
2056 // this needs to run after weaponsInMapAll is initialized
2058 }
2059 else if (s == "none")
2060 {
2061 g_weaponarena = 1;
2062 g_weaponarena_list = "No Weapons Arena";
2063 }
2064 else
2065 {
2066 g_weaponarena = 1;
2067 float t = tokenize_console(s);
2068 g_weaponarena_list = "";
2069 for (int j = 0; j < t; ++j)
2070 {
2071 s = argv(j);
2072 Weapon wep = Weapon_from_name(s);
2073 if(wep != WEP_Null)
2074 {
2075 g_weaponarena_weapons |= (wep.m_wepset);
2077 }
2078 }
2079 if (g_weaponarena_list != "") // remove trailing " & "
2081 else // no valid weapon found
2082 g_weaponarena_list = "No Weapons Arena";
2083 }
2084
2085 if (g_weaponarena)
2086 {
2087 g_weapon_stay = 0; // incompatible
2091 }
2092 else
2093 {
2094 FOREACH(Weapons, it != WEP_Null, {
2095 int w = want_weapon(it, false);
2096 WepSet s = it.m_wepset;
2097 if(w & 1)
2098 start_weapons |= s;
2099 if(w & 2)
2101 if(w & 4)
2103 });
2104 }
2105
2106 if(cvar("g_balance_superweapons_time") < 0)
2108
2109 if(!cvar("g_use_ammunition"))
2111
2112 start_ammo_shells = cvar("g_start_ammo_shells");
2113 start_ammo_nails = cvar("g_start_ammo_nails");
2114 start_ammo_rockets = cvar("g_start_ammo_rockets");
2115 start_ammo_cells = cvar("g_start_ammo_cells");
2116 start_ammo_fuel = cvar("g_start_ammo_fuel");
2117 random_start_weapons_count = cvar("g_random_start_weapons_count");
2118 SetResource(random_start_ammo, RES_SHELLS, cvar("g_random_start_shells"));
2119 SetResource(random_start_ammo, RES_BULLETS, cvar("g_random_start_bullets"));
2120 SetResource(random_start_ammo, RES_ROCKETS, cvar("g_random_start_rockets"));
2121 SetResource(random_start_ammo, RES_CELLS, cvar("g_random_start_cells"));
2122
2133
2134 if (!g_weaponarena)
2135 {
2136 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
2137 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
2138 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
2139 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
2140 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
2141 warmup_start_health = cvar("g_warmup_start_health");
2142 warmup_start_armorvalue = cvar("g_warmup_start_armor");
2143 warmup_start_weapons = '0 0 0';
2146 FOREACH(Weapons, it != WEP_Null, {
2148 WepSet s = it.m_wepset;
2149 if(w & 1)
2151 if(w & 2)
2153 if(w & 4)
2155 });
2156 }
2157
2159 start_items |= ITEM_Jetpack.m_itemid;
2160
2161 MUTATOR_CALLHOOK(SetStartItems);
2162
2163 if (start_items & ITEM_Jetpack.m_itemid)
2164 {
2165 start_items |= ITEM_FuelRegen.m_itemid;
2166 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
2167 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
2168 }
2169
2175 SetResource(random_start_ammo, RES_SHELLS, max(0, GetResource(random_start_ammo, RES_SHELLS)));
2176 SetResource(random_start_ammo, RES_BULLETS, max(0, GetResource(random_start_ammo, RES_BULLETS)));
2177 SetResource(random_start_ammo, RES_ROCKETS, max(0, GetResource(random_start_ammo, RES_ROCKETS)));
2178 SetResource(random_start_ammo, RES_CELLS, max(0, GetResource(random_start_ammo, RES_CELLS)));
2179
2185}
2186
2188{
2190 if(cvar("sv_allow_fullbright"))
2192
2194 if(cvar("sv_forbid_pickuptimer"))
2196
2197 sv_ready_restart_after_countdown = cvar("sv_ready_restart_after_countdown");
2198
2199 if(cvar("g_campaign"))
2200 warmup_stage = 0; // no warmup during campaign
2201 else
2202 {
2205 warmup_limit = -1; // don't start until there's enough players
2206 else if (warmup_stage == 1)
2207 {
2208 // this code is duplicated in ReadyCount()
2209 warmup_limit = cvar("g_warmup_limit");
2210 if(warmup_limit == 0)
2212 }
2213 }
2214
2215 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
2216 if(!g_weapon_stay)
2217 g_weapon_stay = cvar("g_weapon_stay");
2218
2219 MUTATOR_CALLHOOK(ReadLevelCvars);
2220
2222 game_starttime = time + cvar("g_start_delay");
2223
2224 FOREACH(Weapons, it != WEP_Null, { it.wr_init(it); });
2225
2227}
2228
2229void InitializeEntity(entity e, void(entity this) func, int order)
2230{
2231 entity prev, cur;
2232
2233 if (!e || e.initialize_entity)
2234 {
2235 // make a proxy initializer entity
2236 entity e_old = e;
2237 e = new(initialize_entity);
2238 e.enemy = e_old;
2239 }
2240
2241 e.initialize_entity = func;
2242 e.initialize_entity_order = order;
2243
2245 prev = NULL;
2246 for (;;)
2247 {
2248 if (!cur || cur.initialize_entity_order > order)
2249 {
2250 // insert between prev and cur
2251 if (prev)
2252 prev.initialize_entity_next = e;
2253 else
2255 e.initialize_entity_next = cur;
2256 return;
2257 }
2258 prev = cur;
2259 cur = cur.initialize_entity_next;
2260 }
2261}
2263{
2264 entity startoflist = initialize_entity_first;
2267 for (entity e = startoflist; e; e = e.initialize_entity_next)
2268 {
2269 e.remove_except_protected_forbidden = 1;
2270 }
2271 for (entity e = startoflist; e; )
2272 {
2273 e.remove_except_protected_forbidden = 0;
2274 e.initialize_entity_order = 0;
2275 entity next = e.initialize_entity_next;
2276 e.initialize_entity_next = NULL;
2277 var void(entity this) func = e.initialize_entity;
2278 e.initialize_entity = func_null;
2279 if (e.classname == "initialize_entity")
2280 {
2281 entity wrappee = e.enemy;
2282 builtin_remove(e);
2283 e = wrappee;
2284 }
2285 //dprint("Delayed initialization: ", e.classname, "\n");
2286 if (func)
2287 {
2288 func(e);
2289 }
2290 else
2291 {
2292 eprint(e);
2293 backtrace(strcat("Null function in: ", e.classname, "\n"));
2294 }
2295 e = next;
2296 }
2298}
2299
2300// originally ported from DP's droptofloor() builtin
2301// TODO: make a common function for the client-side?
2302// bones_was_here: when we have a use case for it, yes
2304{
2305 int nudgeresult;
2306
2307 if(!this || wasfreed(this))
2308 {
2309 LOG_WARN("DropToFloor_QC: can not modify free entity");
2310 return;
2311 }
2312
2313 /* Prior to sv_legacy_bbox_expand 0, both droptofloor and nudgeoutofsolid_OrFallback were done for items
2314 * using box '-16 -16 0' '16 16 48' (without the FL_ITEM expansion applied),
2315 * which often resulted in bboxes partially in solids once expansion was applied.
2316 * We don't want bboxes in solids (bad for gameplay and culling),
2317 * but we also don't want items to land on a "skirting board" or the base of a sloping wall.
2318 * For initial nudgeoutofsolid_OrFallback and droptofloor stages we use a small box
2319 * so they fall as far and in the same place as they traditionally would,
2320 * then we nudge the full size box out of solid, in a direction appropriate for the plane(s).
2321 */
2322 vector saved_mins = this.mins; // gmqcc's used-uninitialized check doesn't handle
2323 vector saved_maxs = this.maxs; // making these assignments FL_ITEM conditional.
2324 if (this.flags & FL_ITEM)
2325 {
2326 // Using the Q3 bbox for best compatibility with all maps, except...
2327 this.mins.x = -15;
2328 this.mins.y = -15;
2329 this.maxs.x = 15;
2330 this.maxs.y = 15;
2331 this.maxs.z = this.mins.z + 30; // ...Nex, Xon and Quake use a different vertical offset, see also: StartItem()
2332 }
2333
2334 /* NOTE: sv_gameplayfix_droptofloorstartsolid_nudgetocorrect isn't checked, so it won't need to be networked to CSQC.
2335 * It was enabled by default in all Xonotic releases and in Nexuiz, so now certain maps depend on it.
2336 * Example: on erbium 0.8.6 the shards @ crylink are too low (in collision with the floor),
2337 * so without this those fall through the floor.
2338 * Q3, Q2 and Quake don't try to move items out of solid.
2339 */
2340 if(!Q3COMPAT_COMMON && autocvar_sv_mapformat_is_quake3) // Xonotic, Nexuiz
2341 {
2342 nudgeresult = nudgeoutofsolid_OrFallback(this);
2343 if (!nudgeresult)
2344 LOG_WARNF("DropToFloor_QC at \"%v\": COULD NOT FIX badly placed entity \"%s\" before drop", this.origin, this.classname);
2345 else if (nudgeresult > 0)
2346 LOG_WARNF("DropToFloor_QC at \"%v\": FIXED badly placed entity \"%s\" before drop", this.origin, this.classname);
2347 }
2349 {
2350 if (this.flags & FL_ITEM)
2351 this.origin.z += 6;
2352 else
2353 this.origin.z += 1; // monsters 1, misc_explobox 2 but we don't support those currently
2354 }
2355
2356 vector end = this.origin;
2358 end.z -= 4096;
2360 end.z -= 128;
2361 else
2362 end.z -= 256; // Quake, QuakeWorld
2363 tracebox(this.origin, this.mins, this.maxs, end, MOVE_NOMONSTERS, this);
2364
2366 {
2367 // Quake games just delete badly placed items (and misc_explobox)...
2368 if (this.flags & FL_ITEM)
2369 {
2370 LOG_WARNF("DropToFloor_QC at \"%v\" (Quake compat): DELETING badly placed item \"%s\"", this.origin, this.classname);
2371 delete(this);
2372 return;
2373 }
2374 // ...not monsters though...
2375 LOG_WARNF("DropToFloor_QC at \"%v\" (Quake compat): badly placed entity \"%s\"", this.origin, this.classname);
2376 }
2378 {
2379 // ...but we can't do that on Q3 maps like jamdm1
2380 // because our tracebox hits things Q3's trace doesn't (patches?).
2381 LOG_WARNF("DropToFloor_QC at \"%v\" (Quake 3 compat): badly placed entity \"%s\"", this.origin, this.classname);
2382 }
2383
2384 /* NOTE: sv_gameplayfix_droptofloorstartsolid (fallback from tracebox to traceline) isn't implemented.
2385 * It was disabled by default in all Xonotic releases and in Nexuiz.
2386 * Q3 doesn't support it (always uses its '-15 -15 -15' '15 15 15' box when dropping items), neither does Quake or Q2.
2387 */
2388
2389 if (!autocvar_sv_mapformat_is_quake2) // Quake, Q3, Nexuiz, Xonotic
2390 // allow to ride movers (or unset if in freefall)
2391 this.groundentity = trace_ent;
2392
2394 // if support is destroyed, keep suspended (gross hack for floating items in various maps)
2395 // bones_was_here: is this for Q1BSP only? Which maps use it? Do we need it at all? Intentions unclear in DP...
2396 this.move_suspendedinair = true;
2397
2398 if (trace_fraction)
2399 this.origin = trace_endpos;
2400
2401 if (this.flags & FL_ITEM)
2402 {
2403 this.mins = saved_mins;
2404 this.maxs = saved_maxs;
2405
2406 // A side effect of using a small box to drop items (and do the initial nudge) is
2407 // the full size box can end up in collision with a sloping floor or terrain model.
2408 nudgeresult = nudgeoutofsolid_OrFallback(this);
2409 // No warns for successful nudge because it would spam about items on slopes/terrain.
2410 }
2411 else if (trace_allsolid && trace_fraction) // dropped using "proper" bbox but never left solid
2412 {
2413 nudgeresult = nudgeoutofsolid_OrFallback(this);
2414 if (nudgeresult > 0)
2415 LOG_WARNF("DropToFloor_QC at \"%v\": FIXED badly placed entity \"%s\" after drop", this.origin, this.classname);
2416 }
2417 else
2418 nudgeresult = -1;
2419
2420 if (!nudgeresult)
2421 if (!Q3COMPAT_COMMON) // to be expected on Q3 maps like gu3-pewter because we use bigger final bboxes
2422 LOG_WARNF("DropToFloor_QC at \"%v\": COULD NOT FIX stuck entity \"%s\" after drop", this.origin, this.classname);
2423
2424 setorigin(this, this.dropped_origin = this.origin);
2425}
2426
2431
2433void RunThink(entity this, float dt)
2434{
2435 // don't let things stay in the past.
2436 // it is possible to start that way by a trigger with a local time.
2438 return;
2439
2440 float oldtime = time; // do we need to save this?
2441
2442 for (int iterations = 0; iterations < 128 && !wasfreed(this); ++iterations)
2443 {
2444 time = max(oldtime, this.nextthink);
2445 this.nextthink = 0;
2446
2447 if(getthink(this))
2448 getthink(this)(this);
2449 // mods often set nextthink to time to cause a think every frame,
2450 // we don't want to loop in that case, so exit if the new nextthink is
2451 // <= the time the qc was told, also exit if it is past the end of the
2452 // frame
2454 break;
2455 }
2456
2457 time = oldtime;
2458}
2459
2462{
2464 return;
2465
2466 IL_EACH(g_moveables, true,
2467 {
2468 if(IS_CLIENT(it) || it.move_movetype == MOVETYPE_PHYSICS)
2469 continue;
2470
2471 //set_movetype(it, it.move_movetype);
2472 // inline the set_movetype function, since this is called a lot
2473 it.movetype = (it.move_qcphysics) ? MOVETYPE_QCENTITY : it.move_movetype;
2474
2475 if(it.move_qcphysics && it.move_movetype != MOVETYPE_NONE)
2477
2478 if(it.movetype >= MOVETYPE_USER_FIRST && it.movetype <= MOVETYPE_USER_LAST) // these cases have no think handling
2479 {
2480 if(it.move_movetype == MOVETYPE_PUSH || it.move_movetype == MOVETYPE_FAKEPUSH)
2481 continue; // these movetypes have no regular think function
2482 // handle thinking here
2483 if (getthink(it) && it.nextthink > 0 && it.nextthink <= time + PHYS_INPUT_TIMELENGTH)
2485 }
2486 });
2487
2489 return;
2490
2491 // make a second pass to see if any ents spawned this frame and make
2492 // sure they run their move/think. this is verified by checking .move_time, which will never be 0 if the entity has moved
2493 // MOVETYPE_NONE is also checked as .move_time WILL be 0 with that movetype
2494 IL_EACH(g_moveables, it.move_qcphysics,
2495 {
2496 if(IS_CLIENT(it) || it.move_time || it.move_movetype == MOVETYPE_NONE || it.move_movetype == MOVETYPE_PHYSICS)
2497 continue;
2498 Movetype_Physics_NoMatchTicrate(it, PHYS_INPUT_TIMELENGTH, false);
2499 });
2500}
2501
2502void systems_update();
2504{
2506
2507 Physics_Frame();
2508
2510 entity e = IS_SPEC(it) ? it.enemy : it;
2511 if (e.typehitsound) {
2512 STAT(TYPEHIT_TIME, it) = time;
2513 } else if (e.killsound) {
2514 STAT(KILL_TIME, it) = time;
2515 } else if (e.hitsound_damage_dealt) {
2516 STAT(HIT_TIME, it) = time;
2517 // NOTE: this is not accurate as client code doesn't need so much accuracy for its purposes
2518 STAT(HITSOUND_DAMAGE_DEALT_TOTAL, it) += ceil(e.hitsound_damage_dealt);
2519 }
2520 });
2521 // add 1 frametime because after this, engine SV_Physics
2522 // increases time by a frametime and then networks the frame
2523 // add another frametime because client shows everything with
2524 // 1 frame of lag (cl_nolerp 0). The last +1 however should not be
2525 // needed!
2526 float altime = time + frametime * (1 + autocvar_g_antilag_nudge);
2527 FOREACH_CLIENT(true, {
2528 it.typehitsound = false;
2529 it.hitsound_damage_dealt = 0;
2530 it.killsound = false;
2531 antilag_record(it, CS(it), altime);
2532 if(it.death_time == time && IS_PLAYER(it) && IS_DEAD(it))
2533 {
2534 // player's bbox gets resized now, instead of in the damage event that killed the player,
2535 // once all the damage events of this frame have been processed with normal size
2536 float h = ceil((it.mins.z + it.maxs.z) * PL_CORPSE_SCALE * 10) / 10;
2537 it.maxs.z = max(h, it.mins.z + 1);
2538 setsize(it, it.mins, it.maxs);
2539 }
2540 });
2541 IL_EACH(g_monsters, true,
2542 {
2543 antilag_record(it, it, altime);
2544 });
2545 IL_EACH(g_projectiles, it.classname == "nade",
2546 {
2547 antilag_record(it, it, altime);
2548 });
2550 IL_ENDFRAME();
2551}
2552
2553
2554/*
2555 * RedirectionThink:
2556 * returns true if redirecting
2557 */
2561{
2562 float clients_found;
2563
2564 if(redirection_target == "")
2565 return false;
2566
2568 {
2569 cvar_set("sv_public", "-2");
2570 redirection_timeout = time + 0.6; // this will only try twice... should be able to keep more clients
2571 if(redirection_target == "self")
2572 bprint("^3SERVER NOTICE:^7 restarting the server\n");
2573 else
2574 bprint("^3SERVER NOTICE:^7 redirecting everyone to ", redirection_target, "\n");
2575 }
2576
2578 return true;
2579
2581
2582 clients_found = 0;
2584 // TODO add timer
2585 LOG_INFO("Redirecting: sending connect command to ", it.netname);
2586 if(redirection_target == "self")
2587 stuffcmd(it, "\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " reconnect\n");
2588 else
2589 stuffcmd(it, strcat("\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " \"connect ", redirection_target, "\"\n"));
2590 ++clients_found;
2591 });
2592
2593 LOG_INFO("Redirecting: ", ftos(clients_found), " clients left.");
2594
2595 if(time > redirection_timeout || clients_found == 0)
2596 localcmd("\nwait; wait; wait; quit\n");
2597
2598 return true;
2599}
2600
2602{
2603 // Loaded from a save game
2604 // some things then break, so let's work around them...
2605
2606 // Progs DB (capture records)
2608
2609 // Mapinfo
2614
2616}
2617
2619{
2620 game_stopped = 2;
2621
2622 if(world_initialized > 0)
2623 {
2625
2626 // if a timeout is active, reset the slowmo value to normal
2628 cvar_set("slowmo", ftos(orig_slowmo));
2629
2630 LOG_TRACE("Saving persistent data...");
2631 Ban_SaveBans();
2632
2633 // playerstats with unfinished match
2635
2636 if(!cheatcount_total)
2637 {
2640 else
2642 }
2643 if(autocvar_developer > 0)
2644 {
2646 db_dump(TemporaryDB, "server-temp.db");
2647 else
2648 db_save(TemporaryDB, "server-temp.db");
2649 }
2650 CheatShutdown(); // must be after cheatcount check
2653 LOG_TRACE("Saving persistent data... done!");
2654 // tell the bot system the game is ending now
2655 bot_endgame();
2656
2659
2662 }
2663 else if(world_initialized == 0)
2664 {
2665 LOG_INFO("NOTE: crashed before even initializing the world, not saving persistent data");
2666 }
2667 else
2668 {
2670 }
2671}
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:2178
void get_mi_min_max(float mode)
Definition util.qc:674
string maplist_reply
Definition util.qh:161
string rankings_reply
Definition util.qh:161
vector mi_min
Definition util.qh:127
string records_reply[10]
Definition util.qh:162
string lsmaps_reply
Definition util.qh:161
string ladder_reply
Definition util.qh:161
vector mi_max
Definition util.qh:128
string monsterlist_reply
Definition util.qh:161
#define RACE_RECORD
Definition util.qh:97
const int INITPRIO_GAMETYPE_FALLBACK
Definition constants.qh:95
const float PL_CORPSE_SCALE
Definition constants.qh:60
const int SERVERFLAG_FORBID_PICKUPTIMER
Definition constants.qh:20
const int SERVERFLAG_ALLOW_FULLBRIGHT
Definition constants.qh:16
const int INITPRIO_DROPTOFLOOR
Definition constants.qh:97
const int INITPRIO_FINDTARGET
Definition constants.qh:96
const int FL_ITEM
Definition constants.qh:77
string classname
float Q3SURFACEFLAG_SKY
float flags
const float MOVE_NOMONSTERS
float trace_dphitcontents
float maxclients
entity trace_ent
float frametime
string mapname
const float MOVE_NORMAL
vector mins
string trace_dphittexturename
const float FILE_READ
float time
vector trace_endpos
float checkpvs(vector viewpos, entity viewee)
float trace_startsolid
vector maxs
float nextthink
float trace_dphitq3surfaceflags
vector origin
float trace_fraction
float trace_allsolid
const float FILE_APPEND
ERASEABLE bool cvar_value_issafe(string s)
Definition cvar.qh:11
void defer(entity this, float fdelay, void(entity) func)
Execute func() after time + fdelay.
Definition defer.qh: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:129
const int MOVETYPE_FAKEPUSH
Definition movetypes.qh:153
const int MOVETYPE_PUSH
Definition movetypes.qh:136
entity groundentity
Definition movetypes.qh:93
const int MOVETYPE_PHYSICS
Definition movetypes.qh:142
const int MOVETYPE_QCENTITY
Definition movetypes.qh:151
float move_suspendedinair
Definition movetypes.qh:94
void target_music_kill()
Definition music.qc:48
void TargetMusic_RestoreGame()
Definition music.qc:85
var void func_null()
string string_null
Definition nil.qh:9
s1 s2 s1 s2 FLAG s1 s2 FLAG spree_cen s1 CPID_Null
Definition all.inc:622
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
void Send_Notification(NOTIF broadcast, entity client, MSG net_type, Notification net_name,...count)
Definition all.qc: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:2150
void PutObserverInServer(entity this, bool is_forced, bool use_spawnpoint)
putting a client as observer in the server
Definition client.qc:261
void ClientInit_Spawn()
Definition client.qc:946
float autocvar_g_player_alpha
Definition client.qh:19
float jointime
Definition client.qh:66
int autocvar_g_maxplayers
Definition client.qh:44
float orig_slowmo
Definition common.qh:58
const float TIMEOUT_ACTIVE
Definition common.qh:49
float sys_frametime
Definition common.qh:57
void VoteThink()
Definition vote.qc:336
void Nagger_Init()
Definition vote.qc:97
void ReadyRestart(bool forceWarmupEnd)
Definition vote.qc:526
void VoteReset(bool verbose)
Definition vote.qc:129
IntrusiveList g_items
Definition items.qh:119
void remove_safely(entity e)
Definition main.qc:283
void Pause_TryPause_Dedicated(entity this)
Definition main.qc:198
void remove_unsafely(entity e)
Definition main.qc:276
string GetField_fullspawndata(entity e, string fieldname, bool vfspath)
Retrieves the value of a map entity field from fullspawndata.
Definition main.qc:451
void remove_except_protected(entity e)
Definition main.qc:269
bool autocvar_sv_autopause
Definition main.qh:19
void MapVote_Think()
Definition mapvoting.qc:758
void MapVote_Start()
Definition mapvoting.qc:743
void race_StartCompleting()
Definition race.qc:1214
int g_race_qualifying
Definition race.qh: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
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:488
bool TeamBalance_IsTeamAllowed(entity balance, int index)
Returns whether the team change to the specified team is allowed.
Definition teamplay.qc:807
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:352
bool MoveToTeam(entity client, int team_index, int type)
Moves player to the specified team.
Definition teamplay.qc:328
@ 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:1863
void RunThink(entity this, float dt)
Definition world.qc:2433
void RandomSeed_Think(entity this)
Definition world.qc:601
void InitiateOvertime()
Definition world.qc:1499
void DumpStats(float final)
Definition world.qc:1264
#define BADCVAR(p)
int InitiateSuddenDeath()
Definition world.qc:1467
void SetDefaultAlpha()
Definition world.qc:105
void ClearWinners()
Definition world.qc:1554
void systems_update()
Definition main.qc:7
WepSet weapons_most()
Definition world.qc:1934
void CheckRules_World()
Definition world.qc:1725
void InitGameplayMode()
Definition world.qc:710
int fragsleft_last
Definition world.qc:1559
void MatchEnd_RestoreSpectatorStatus()
Definition world.qc:1395
WepSet weapons_all()
Definition world.qc:1914
float redirection_nextthink
Definition world.qc:2559
entity pingplreport
Definition world.qc:57
void NextLevel()
Definition world.qc:1404
void InitializeEntitiesRun()
Definition world.qc:2262
float RedirectionThink()
Definition world.qc:2560
void SetWinners(.float field, float value)
Definition world.qc:1539
WepSet weapons_start()
Weapons the player normally starts with outside weapon arena.
Definition world.qc:1903
void AddWinners(.float field, float value)
Definition world.qc:1545
float latency_time
Definition world.qc:56
float WinningCondition_Scores(float limit, float leadlimit)
Definition world.qc:1560
bool RandomSeed_Send(entity this, entity to, int sf)
Definition world.qc:595
void MatchEnd_RestoreSpectatorAndTeamStatus(.int prev_team_field)
Definition world.qc:1370
void weaponarena_available_all_update(entity this)
Definition world.qc:1944
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:1117
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:2427
float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
Definition world.qc:1251
void weaponarena_available_devall_update(entity this)
Definition world.qc:1957
void Physics_Frame()
Definition world.qc:2461
void EndFrame()
Definition world.qc:2503
float WinningCondition_RanOutOfSpawns()
Definition world.qc:1641
float GetWinningCode(float fraglimitreached, float equality)
Definition world.qc:1510
void GameplayMode_DelayedInit(entity this)
Definition world.qc:666
void Shutdown()
Definition world.qc:2618
void weaponarena_available_most_update(entity this)
Definition world.qc:1970
WepSet weapons_devall()
Definition world.qc:1924
#define BADVALUE(p, val)
entity randomseed
Definition world.qc:594
void readplayerstartcvars()
Definition world.qc:1983
bool autocvar_sv_freezenonclients
Definition world.qc:2460
const float SPAWNFLAG_NO_WAYPOINTS_FOR_ITEMS
Definition world.qc:103
void readlevelcvars()
Definition world.qc:2187
void PingPLReport_Spawn()
Definition world.qc:96
float redirection_timeout
Definition world.qc:2558
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:2229
bool autocvar_sv_gameplayfix_multiplethinksperframe
Definition world.qc:2432
void DropToFloor_QC(entity this)
Definition world.qc:2303
bool world_already_spawned
Definition world.qc:762
#define BADPRESUFFIXVALUE(p, s, val)
const float LATENCY_THINKRATE
Definition world.qc:53
void RestoreGame()
Definition world.qc:2601
#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