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