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