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