]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mario/invasion_types
authorMario <mario@smbclan.net>
Sat, 26 Aug 2017 15:19:52 +0000 (01:19 +1000)
committerMario <mario@smbclan.net>
Sat, 26 Aug 2017 15:19:52 +0000 (01:19 +1000)
# Conflicts:
# qcsrc/server/mutators/mutator/gamemode_invasion.qc

1  2 
gamemodes-server.cfg
qcsrc/server/g_world.qc
qcsrc/server/mutators/mutator/gamemode_invasion.qc

diff --combined gamemodes-server.cfg
index 0000000000000000000000000000000000000000,cfe8e319e52abeaf75cbe1f865d407dfa62589f8..6790a3b4fb9575ab91e070c57bc984ac97609ba2
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,523 +1,524 @@@
+ // ===================================
+ //  Master config for core game modes
+ // ===================================
+ // global gametype setting (0 = deathmatch)
+ set gamecfg 0
+ // =================
+ //  gamestart hooks
+ // =================
+ alias _sv_hook_gamestart "set _sv_hook_gametype $1; _sv_hook_gamestart_stage2"
+ alias _sv_hook_gamestart_stage2 "sv_hook_gamestart_all; sv_hook_gamestart_${_sv_hook_gametype}"
+ alias sv_hook_gamestart_all
+ alias sv_hook_gamestart_dm
+ alias sv_hook_gamestart_tdm
+ alias sv_hook_gamestart_dom
+ alias sv_hook_gamestart_ctf
+ alias sv_hook_gamestart_lms
+ alias sv_hook_gamestart_ca
+ alias sv_hook_gamestart_kh
+ alias sv_hook_gamestart_ons
+ alias sv_hook_gamestart_as
+ alias sv_hook_gamestart_rc
+ alias sv_hook_gamestart_nb
+ alias sv_hook_gamestart_cts
+ alias sv_hook_gamestart_ka
+ alias sv_hook_gamestart_ft
+ alias sv_hook_gamestart_inv
+ alias sv_hook_gamerestart
+ alias sv_hook_gameend
+ // =====================
+ //  gametype vote hooks
+ // =====================
+ // These are called when the mode is switched via gametype vote screen,
+ // earlier than gamestart hooks (useful for enabling per-gamemode mutators)
+ // The _all hook is called before the specific one
+ // here it sets g_maxplayers to undo what duel does
+ alias sv_vote_gametype_hook_all "set g_maxplayers 0"
+ alias sv_vote_gametype_hook_as
+ alias sv_vote_gametype_hook_ca
+ alias sv_vote_gametype_hook_ctf
+ alias sv_vote_gametype_hook_cts
+ alias sv_vote_gametype_hook_dm
+ alias sv_vote_gametype_hook_dom
+ alias sv_vote_gametype_hook_ft
+ alias sv_vote_gametype_hook_inv
+ alias sv_vote_gametype_hook_ka
+ alias sv_vote_gametype_hook_kh
+ alias sv_vote_gametype_hook_lms
+ alias sv_vote_gametype_hook_nb
+ alias sv_vote_gametype_hook_ons
+ alias sv_vote_gametype_hook_rc
+ alias sv_vote_gametype_hook_tdm
+ // Preset to allow duel to be used for the gametype voting screen
+ // sv_vote_gametype_*_type Must be set to the name of the gametype the option is based on
+ // sv_vote_gametype_*_name Contains a human-readable name of the gametype
+ // sv_vote_gametype_*_description Contains a longer description
+ set sv_vote_gametype_duel_type dm
+ set sv_vote_gametype_duel_name Duel
+ set sv_vote_gametype_duel_description "One vs One match"
+ alias sv_vote_gametype_hook_duel "set g_maxplayers 2"
+ // ===========
+ //  leadlimit
+ // ===========
+ // this means that timelimit can be overidden globally and fraglimit can be overidden for each game mode: DM/TDM, Domination, CTF, and Runematch.
+ set leadlimit 0
+ set leadlimit_and_fraglimit 0 "if set, leadlimit is ANDed with fraglimit (otherwise ORed)"
+ seta timelimit_override -1    "Time limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta fraglimit_override -1    "Frag limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta leadlimit_override -1    "Lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta capturelimit_override -1 "Capture limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta captureleadlimit_override -1     "Capture llead imit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta g_domination_point_limit -1      "Domination point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta g_domination_point_leadlimit -1  "Domination point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta g_keyhunt_point_limit -1 "Keyhunt point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta g_keyhunt_point_leadlimit -1     "Keyhunt point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta g_race_laps_limit -1     "Race laps limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta g_nexball_goallimit -1 "Nexball goal limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta g_nexball_goalleadlimit -1 "Nexball goal lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta g_invasion_point_limit -1 "Invasion point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ // =================================
+ //  respawn delay/waves/weapon_stay
+ // =================================
+ // when variables are set to anything other than 0, they take over the global setting. Negative values force an output value of zero.
+ set g_ctf_respawn_delay_small 1
+ set g_ctf_respawn_delay_small_count 1
+ set g_ctf_respawn_delay_large 5
+ set g_ctf_respawn_delay_large_count 5
+ set g_ctf_respawn_delay_max 0
+ set g_ctf_respawn_waves 0
+ set g_ctf_weapon_stay 0
+ set g_dm_respawn_delay_small 0
+ set g_dm_respawn_delay_small_count 0
+ set g_dm_respawn_delay_large 0
+ set g_dm_respawn_delay_large_count 0
+ set g_dm_respawn_delay_max 0
+ set g_dm_respawn_waves 0
+ set g_dm_weapon_stay 0
+ set g_dom_respawn_delay_small 0
+ set g_dom_respawn_delay_small_count 0
+ set g_dom_respawn_delay_large 0
+ set g_dom_respawn_delay_large_count 0
+ set g_dom_respawn_delay_max 0
+ set g_dom_respawn_waves 0
+ set g_dom_weapon_stay 0
+ set g_lms_respawn_delay_small 0
+ set g_lms_respawn_delay_small_count 0
+ set g_lms_respawn_delay_large 0
+ set g_lms_respawn_delay_large_count 0
+ set g_lms_respawn_delay_max 0
+ set g_lms_respawn_waves 0
+ set g_lms_weapon_stay 0
+ set g_tdm_respawn_delay_small 0
+ set g_tdm_respawn_delay_small_count 0
+ set g_tdm_respawn_delay_large 0
+ set g_tdm_respawn_delay_large_count 0
+ set g_tdm_respawn_delay_max 0
+ set g_tdm_respawn_waves 0
+ set g_tdm_weapon_stay 0
+ set g_ka_respawn_delay_small 0
+ set g_ka_respawn_delay_small_count 0
+ set g_ka_respawn_delay_large 0
+ set g_ka_respawn_delay_large_count 0
+ set g_ka_respawn_delay_max 0
+ set g_ka_respawn_waves 0
+ set g_ka_weapon_stay 0
+ set g_kh_respawn_delay_small 0
+ set g_kh_respawn_delay_small_count 0
+ set g_kh_respawn_delay_large 0
+ set g_kh_respawn_delay_large_count 0
+ set g_kh_respawn_delay_max 0
+ set g_kh_respawn_waves 0
+ set g_kh_weapon_stay 0
+ set g_ca_respawn_delay_small 0
+ set g_ca_respawn_delay_small_count 0
+ set g_ca_respawn_delay_large 0
+ set g_ca_respawn_delay_large_count 0
+ set g_ca_respawn_delay_max 0
+ set g_ca_respawn_waves 0
+ set g_ca_weapon_stay 0
+ set g_nb_respawn_delay_small 0
+ set g_nb_respawn_delay_small_count 0
+ set g_nb_respawn_delay_large 0
+ set g_nb_respawn_delay_large_count 0
+ set g_nb_respawn_delay_max 0
+ set g_nb_respawn_waves 0
+ set g_nb_weapon_stay 0
+ set g_as_respawn_delay_small 0
+ set g_as_respawn_delay_small_count 0
+ set g_as_respawn_delay_large 0
+ set g_as_respawn_delay_large_count 0
+ set g_as_respawn_delay_max 0
+ set g_as_respawn_waves 0
+ set g_as_weapon_stay 0
+ set g_ons_respawn_delay_small 0
+ set g_ons_respawn_delay_small_count 0
+ set g_ons_respawn_delay_large 0
+ set g_ons_respawn_delay_large_count 0
+ set g_ons_respawn_delay_max 0
+ set g_ons_respawn_waves 0
+ set g_ons_weapon_stay 0
+ set g_rc_respawn_delay_small 0
+ set g_rc_respawn_delay_small_count 0
+ set g_rc_respawn_delay_large 0
+ set g_rc_respawn_delay_large_count 0
+ set g_rc_respawn_delay_max 0
+ set g_rc_respawn_waves 0
+ set g_rc_weapon_stay 0
+ set g_cts_respawn_delay_small -1  // CTS shall have instant respawn.
+ set g_cts_respawn_delay_small_count 0
+ set g_cts_respawn_delay_large -1  // CTS shall have instant respawn.
+ set g_cts_respawn_delay_large_count 0
+ set g_cts_respawn_delay_max 0
+ set g_cts_respawn_waves 0
+ set g_cts_weapon_stay 2
+ set g_ft_respawn_delay_small 0
+ set g_ft_respawn_delay_small_count 0
+ set g_ft_respawn_delay_large 0
+ set g_ft_respawn_delay_large_count 0
+ set g_ft_respawn_delay_max 0
+ set g_ft_respawn_waves 0
+ set g_ft_weapon_stay 0
+ set g_inv_respawn_delay_small 0
+ set g_inv_respawn_delay_small_count 0
+ set g_inv_respawn_delay_large 0
+ set g_inv_respawn_delay_large_count 0
+ set g_inv_respawn_delay_max 0
+ set g_inv_respawn_waves 0
+ set g_inv_weapon_stay 0
+ // =========
+ //  assault
+ // =========
+ set g_assault 0 "Assault: attack the enemy base as fast as you can, then defend the base against the enemy for that time to win"
+ // ============
+ //  clan arena
+ // ============
+ set g_ca 0 "Clan Arena: Played in rounds, once you're dead you're out! The team with survivors wins the round"
+ seta g_ca_point_limit -1 "Clan Arena point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta g_ca_point_leadlimit -1 "Clan Arena point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ set g_ca_spectate_enemies 0 "Allow spectating enemy player by dead player during clan arena games"
+ set g_ca_warmup 10 "how long the players will have time to run around the map before the round starts"
+ set g_ca_damage2score_multiplier 0.01
+ set g_ca_round_timelimit 180 "round time limit in seconds"
+ seta g_ca_teams_override 0
+ set g_ca_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
+ set g_ca_teams 0
+ // ==================
+ //  capture the flag
+ // ==================
+ set g_ctf 0 "Capture The Flag: take the enemy flag and bring it to yours at your base to score"
+ set g_ctf_oneflag 0 "Allow oneflag CTF mode on maps that support it"
+ set g_ctf_oneflag_reverse 0 "apply reverse mode to oneflag CTF (take flag to enemy bases to cap), overrides g_ctf_reverse only in oneflag, g_ctf_reverse still affects oneflag"
+ set g_ctf_leaderboard 0 "show top capture times in the scoreboard"
+ set g_ctf_flag_return 1 "auto return the flag to base when touched by a teammate"
+ set g_ctf_flag_return_carrying 0 "(manual return mode) auto return the flag to base if touched by a flag carrier"
+ set g_ctf_flag_return_carried_radius 100 "allow flags to be returned by carrier if base is within this radius"
+ set g_ctf_flag_return_time 15 "automatically return the flag to base after this amount of time"
+ set g_ctf_flag_return_dropped 100 "automatically return the flag to base if dropped within this distance from base (in qu)"
+ set g_ctf_flag_return_damage 0 "allow the flag to be damaged, reducing time needed to automatically return to base"
+ set g_ctf_flag_return_damage_delay 0 "how much time the flag takes to automatically return to base if it falls into lava/slime/trigger hurt"
+ set g_ctf_flag_return_when_unreachable 1 "automatically return the flag if it falls into lava/slime/trigger hurt"
+ set g_ctf_flagcarrier_auto_helpme_damage 100 "automatically place a helpme notification on flag carrier waypointsprite if they get hit and their health dips below this value"
+ set g_ctf_flagcarrier_auto_helpme_time 2 "antispam time for the helpme notification"
+ set g_ctf_flagcarrier_selfdamagefactor 1
+ set g_ctf_flagcarrier_selfforcefactor 1
+ set g_ctf_flagcarrier_damagefactor 1
+ set g_ctf_flagcarrier_forcefactor 1
+ set g_ctf_stalemate 1 "show the enemy flagcarrier location after both teams have held the flags a certain amount of time"
+ set g_ctf_stalemate_endcondition 1 "condition for stalemate mode to be finished: 1 = If ONE flag is no longer stale, 2 = If BOTH flags are no longer stale"
+ set g_ctf_stalemate_time 60 "time for each flag until stalemate mode is activated"
+ set g_ctf_flagcarrier_waypointforenemy_spotting 1 "show the enemy flagcarrier location if a team mate presses +use to spot them"
+ set g_ctf_dropped_capture_delay 1 "dropped capture delay"
+ set g_ctf_dropped_capture_radius 100 "allow dropped flags to be automatically captured by base flags if the dropped flag is within this radius of it"
+ set g_ctf_flag_damageforcescale 2
+ set g_ctf_portalteleport 0 "allow flag carriers to go through portals made in portal gun without dropping the flag"
+ set g_ctf_reverse 0 "if enabled, you score by bringing your own flag to an enemy's flag in their base"
+ set g_ctf_flag_collect_delay 1
+ set g_ctf_flag_health 0
+ set g_ctf_flag_dropped_waypoint 2 "show dropped flag waypointsprite when a flag is lost. 1 = team only, 2 = for all players"
+ set g_ctf_flag_dropped_floatinwater 200 "move upwards while in water at this velocity"
+ set g_ctf_throw 1 "throwing allows circumventing carrierkill score, so enable this with care!"
+ set g_ctf_throw_angle_max 90 "maximum upwards angle you can throw the flag"
+ set g_ctf_throw_angle_min -90 "minimum downwards angle you can throw the flag"
+ set g_ctf_throw_punish_count 3
+ set g_ctf_throw_punish_delay 30
+ set g_ctf_throw_punish_time 10
+ set g_ctf_throw_strengthmultiplier 2 "multiplier for velocity when you have the strength... essentially, throw the flag REALLY hard when you have the strength :D"
+ set g_ctf_throw_velocity_forward 500 "how fast or far a player can throw the flag"
+ set g_ctf_throw_velocity_up 200 "upwards velocity added upon initial throw"
+ set g_ctf_drop_velocity_up 200 "upwards velocity when a flag is dropped (i.e. when a flag carrier dies)"
+ set g_ctf_drop_velocity_side 100 "randomized sideways velocity when a flag is dropped"
+ set g_ctf_pass 1 "allow passing of flags to nearby team mates"
+ set g_ctf_pass_arc 20 "upwards arcing of the flag path to look more like a throw"
+ set g_ctf_pass_arc_max 200 "maximum height for upwards arcing of the flag path to look more like a throw"
+ set g_ctf_pass_directional_max 200 "maximum radius from crosshair for line of sight selection when passing"
+ set g_ctf_pass_directional_min 50 "minimum radius from crosshair for line of sight selection when passing"
+ set g_ctf_pass_radius 500 "maximum radius that you can pass to a team mate in"
+ set g_ctf_pass_wait 2 "delay in seconds between how often players can pass the flag (antispam, essentially)"
+ set g_ctf_pass_request 1 "allow players to request the flag carrier to pass the flag to them"
+ set g_ctf_pass_turnrate 50 "how well the flag follows the best direction to its target while passing"
+ set g_ctf_pass_timelimit 2 "how long a flag can stay trying to pass before it gives up and just becomes dropped"
+ set g_ctf_pass_velocity 750 "how fast or far a player can pass the flag"
+ set g_ctf_allow_vehicle_touch 0 "allow flags to be picked up/captured/returned without even leaving the vehicle"
+ set g_ctf_allow_vehicle_carry 1 "allow players to hold flags inside a vehicle"
+ set g_ctf_allow_monster_touch 0 "allow flags to be returned by monsters"
+ set g_ctf_score_ignore_fields 0 "force regular score settings to override per entity specified scores"
+ set g_ctf_shield_max_ratio 0  "shield at most this percentage of a team from the enemy flag (try: 0.4 for 40%)"
+ set g_ctf_shield_min_negscore 20      "shield the player from the flag if he's got this negative amount of points or less"
+ set g_ctf_shield_force 100    "push force of the shield"
+ set g_ctf_flag_red_model "models/ctf/flags.md3"
+ set g_ctf_flag_red_skin 0
+ set g_ctf_flag_blue_model "models/ctf/flags.md3"
+ set g_ctf_flag_blue_skin 1
+ set g_ctf_flag_yellow_model "models/ctf/flags.md3"
+ set g_ctf_flag_yellow_skin 2
+ set g_ctf_flag_pink_model "models/ctf/flags.md3"
+ set g_ctf_flag_pink_skin 3
+ set g_ctf_flag_neutral_model "models/ctf/flags.md3"
+ set g_ctf_flag_neutral_skin 4
+ set g_ctf_flag_glowtrails 1
+ set g_ctf_fullbrightflags 0
+ set g_ctf_dynamiclights 0
+ seta g_ctf_ignore_frags 0     "1: regular frags give no points"
+ exec ctfscoring-samual.cfg
+ // ====================
+ //  complete the stage
+ // ====================
+ set g_cts 0 "CTS: complete the stage"
+ set g_cts_selfdamage 1 "0 = disable all selfdamage and falldamage in cts"
+ set g_cts_finish_kill_delay 10 "prevent cheating by running back to the start line, and starting out with more speed than otherwise possible"
+ // ==========================
+ //  deathmatch (ffa or team)
+ // ==========================
+ set g_dm 1 "Deathmatch: killing any other player is one frag, player with most frags wins"
+ set g_tdm_teams 2 "how many teams are in team deathmatch (set by mapinfo)"
+ set g_tdm_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
+ seta g_tdm_teams_override 0   "how many teams are in team deathmatch"
+ set g_tdm_point_limit -1 "TDM point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ set g_tdm_point_leadlimit -1 "TDM point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ // ============
+ //  domination
+ // ============
+ set g_domination                      0 "Domination: capture and hold control points to gain points"
+ set g_domination_default_teams                2 "default number of teams for maps that aren't domination-specific"
+ seta g_domination_teams_override              0 "use a specific number of teams in domination games (minimum 2), disables dom_team entities"
+ set g_domination_disable_frags                0 "players can't get frags normally, only get points from kills"
+ set g_domination_point_amt            0 "override: how many points to get per ping"
+ set g_domination_point_fullbright     0 "domination point fullbright"
+ set g_domination_point_rate           0 "override: how often to give those points"
+ //set g_domination_point_capturetime  0.1 "how long it takes to capture a point (given no interference)"
+ set g_domination_point_glow           0 "domination point glow (warning, slow)"
+ set g_domination_roundbased 0 "enable round-based domination (capture all control points to win the round)"
+ set g_domination_roundbased_point_limit 5 "capture limit in round-based domination mode"
+ set g_domination_round_timelimit 120
+ set g_domination_warmup 5
+ //set g_domination_balance_team_points        1 "# of points received is based on team sizes"
+ // ===========
+ //  freezetag
+ // ===========
+ set g_freezetag 0 "Freeze Tag: Freeze the opposing team(s) to win, unfreeze teammates by standing next to them"
+ set g_freezetag_warmup 5 "Time players get to run around before the round starts"
+ seta g_freezetag_point_limit -1       "Freeze Tag point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta g_freezetag_point_leadlimit -1   "Freeze Tag point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ set g_freezetag_revive_speed 0.4 "Speed for reviving a frozen teammate"
+ set g_freezetag_revive_clearspeed 1.6 "Speed at which reviving progress gets lost when out of range"
+ set g_freezetag_revive_extra_size 100 "Distance in qu that you can stand from a frozen teammate to keep reviving him"
+ set g_freezetag_revive_nade 1 "Enable reviving from own nade explosion"
+ set g_freezetag_revive_nade_health 40 "Amount of health player has if they revived from their own nade explosion"
+ set g_freezetag_round_timelimit 180 "round time limit in seconds"
+ set g_freezetag_frozen_maxtime 60 "frozen players will be automatically unfrozen after this time in seconds"
+ seta g_freezetag_teams_override 0
+ set g_freezetag_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
+ set g_freezetag_teams 0
+ // ==========
+ //  keepaway
+ // ==========
+ set g_keepaway 0 "game mode which focuses around a ball"
+ set g_keepaway_score_bckill 1 "enable scoring points (y/n) for ball carrier kills (value is how many points to award)"
+ set g_keepaway_score_killac 1 "amount of points to give when you kill someone while you have the ball"
+ set g_keepaway_score_timeinterval 1 "amount of time it takes between intervals for timepoints to be added to the score"
+ set g_keepaway_score_timepoints 0 "points to add to score per timeinterval, 0 for no points"
+ set g_keepaway_ballcarrier_effects 8 "Add together the numbers you want: EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)"
+ set g_keepaway_ballcarrier_highspeed 1 "speed multiplier done to the person holding the ball (recommended when used with some mutators)"
+ set g_keepaway_ballcarrier_damage     1       "damage multiplier while holding the ball"
+ set g_keepaway_ballcarrier_force      1       "force multiplier while holding the ball"
+ set g_keepaway_ballcarrier_selfdamage 1       "self damage multiplier while holding the ball"
+ set g_keepaway_ballcarrier_selfforce  1       "self force multiplier while holding the ball"
+ set g_keepaway_noncarrier_warn        1       "warn players when they kill without holding the ball"
+ set g_keepaway_noncarrier_damage      1       "damage done to other players if both you and they don't have the ball"
+ set g_keepaway_noncarrier_force       1       "force done to other players if both you and they don't have the ball"
+ set g_keepaway_noncarrier_selfdamage  1       "self damage if you don't have the ball"
+ set g_keepaway_noncarrier_selfforce   1       "self force if you don't have the ball"
+ set g_keepawayball_effects 0 "Add together the numbers you want: EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)"
+ set g_keepawayball_trail_color        254     "particle trail color from player/ball"
+ set g_keepawayball_damageforcescale   3 "Scale of force which is applied to the ball by weapons/explosions/etc"
+ set g_keepawayball_respawntime        10      "if no one picks up the ball, how long to wait until the ball respawns"
+ // ==========
+ //  key hunt
+ // ==========
+ set g_keyhunt 0 "Key Hunt: collect all keys from the enemies and bring them together to score"
+ set g_balance_keyhunt_return_when_unreachable 1 "automatically destroy a key if it falls into lava/slime/trigger hurt"
+ set g_balance_keyhunt_delay_damage_return 5 "time a key takes to automatically destroy itself if it falls into lava/slime/trigger hurt"
+ set g_balance_keyhunt_delay_return 60 "time a key takes to destroy itself if dropped"
+ set g_balance_keyhunt_delay_round 5
+ set g_balance_keyhunt_delay_tracking 10
+ set g_balance_keyhunt_delay_fadeout 2
+ set g_balance_keyhunt_delay_collect 1.5
+ set g_balance_keyhunt_maxdist 150
+ set g_balance_keyhunt_score_collect 3
+ set g_balance_keyhunt_score_carrierfrag 2
+ set g_balance_keyhunt_score_capture 100
+ set g_balance_keyhunt_score_push 60
+ set g_balance_keyhunt_score_destroyed 50
+ set g_balance_keyhunt_score_destroyed_ownfactor 1
+ set g_balance_keyhunt_dropvelocity 300
+ set g_balance_keyhunt_throwvelocity 400
+ set g_balance_keyhunt_protecttime 0.8
+ set g_balance_keyhunt_damageforcescale 1
+ seta g_keyhunt_teams_override 0
+ set g_keyhunt_teams 0
+ set g_keyhunt_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
+ // ===================
+ //  last man standing
+ // ===================
+ set g_lms 0 "Last Man Standing: everyone starts with a certain amount of lives, and the survivor wins"
+ set g_lms_lives_override -1
+ set g_lms_extra_lives 0
+ set g_lms_regenerate 0
+ set g_lms_last_join 3 "if g_lms_join_anytime is false, new players can only join if the worst active player has more than (fraglimit - g_lms_last_join) lives"
+ set g_lms_join_anytime 1      "if true, new players can join, but get same amount of lives as the worst player"
+ // =========
+ //  nexball
+ // =========
+ set g_nexball 0 "Nexball: Basketball and Soccer go Xonotic"
+ set g_nexball_basketball_effects_default     8    "default: dim light. The original version used 1024 (fire) but it gives bad performance"
+ set g_balance_nexball_primary_speed       1000    "launching speed"
+ set g_balance_nexball_primary_refire         0.7  "launching refire"
+ set g_balance_nexball_primary_animtime       0.3  "launching animtime"
+ set g_balance_nexball_secondary_animtime     0.3  "launching animtime"
+ set g_balance_nexball_secondary_speed     3000    "stealing projectile speed"
+ set g_balance_nexball_secondary_lifetime     0.15 "stealing projectile lifetime"
+ set g_balance_nexball_secondary_force      500    "stealing projectile force"
+ set g_balance_nexball_secondary_refire       0.6  "stealing projectile refire"
+ set g_balance_nexball_secondary_animtime     0.3  "stealing projectile animtime"
+ set g_nexball_football_physics  2  "0: Revenant's original movement, 1: 0 but half independant of aiming height, 2: 1 fully independant, -1: first recode try"
+ set g_nexball_basketball_jumppad 1    "whether basketballs should be pushable by jumppads"
+ set g_nexball_basketball_bouncefactor 0.6    "velocity loss when the ball bounces"
+ set g_nexball_basketball_bouncestop   0.075  "speed at which the ball stops when it hits the ground (multiplied by sv_gravity)"
+ set g_nexball_football_jumppad 1    "whether footballs should be pushable by jumppads"
+ set g_nexball_football_bouncefactor   0.6    "velocity loss when the ball bounces"
+ set g_nexball_football_bouncestop     0.075  "speed at which the ball stops when it hits the ground (multiplied by sv_gravity)"
+ set g_nexball_football_boost_forward      100   "forward velocity boost when the ball is touched"
+ set g_nexball_football_boost_up           200   "vertical velocity boost when the ball is touched"
+ set g_nexball_basketball_delay_hold           20    "time before a player who caught the ball loses it (anti-ballcamp)"
+ set g_nexball_basketball_delay_hold_forteam   60    "time before a ball reset when a team holds the ball for too long"
+ set g_nexball_basketball_teamsteal             1    "1 to allow players to steal from teammates, 0 to disallow"
+ set g_nexball_basketball_carrier_highspeed         0.8  "speed multiplier for the ballcarrier"
+ set g_nexball_meter_period                  1    "time to make a full cycle on the power meter"
+ set g_nexball_basketball_meter              1    "use the power meter for basketball"
+ set g_nexball_basketball_meter_minpower   0.5    "minimal multiplier to the launching speed when using the power meter"
+ set g_nexball_basketball_meter_maxpower   1.2    "maximal multiplier to the launching speed when using the power meter"
+ set g_nexball_delay_goal     3    "delay between a goal and a ball reset"
+ set g_nexball_delay_idle     10   "maximal idle time before a reset"
+ set g_nexball_delay_start    3    "time the ball stands on its spawn before being released"
+ set g_nexball_delay_collect  0.5  "time before the same player can catch the ball he launched"
+ set g_nexball_sound_bounce   1    "bouncing sound (0: off)"
+ set g_nexball_basketball_trail  1  "1 to leave a trail"
+ set g_nexball_football_trail    0  "1 to leave a trail"
+ set g_nexball_trail_color     254  "1-256 for different colors (Quake palette, 254 is white)"
+ set g_nexball_playerclip_collisions 1 "make the ball bounce off clips"
+ set g_nexball_radar_showallplayers 1  "1: show every player and the ball on the radar  0: only show teammates and the ball on the radar"
+ seta g_nexball_safepass_maxdist 5000 "Max distance to allow save fassping (0 to turn off safe passing)"
+ seta g_nexball_safepass_turnrate 0.1 "How fast the safe-pass ball can habge direction"
+ seta g_nexball_safepass_holdtime 0.75 "How long to remeber last teammate you pointed at"
+ seta g_nexball_viewmodel_scale 0.25 "How large the ball for the carrier"
+ seta g_nexball_viewmodel_offset "8 8 0" "Where the ball is located on carrier forward right up"
+ seta g_nexball_tackling 1 "Allow ball theft?"
+ // ===========
+ //  onslaught
+ // ===========
+ set g_onslaught 0 "Onslaught: take control points towards the enemy generator and then destroy it"
+ set g_onslaught_point_limit 1 "Onslaught point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ set g_onslaught_warmup 5
+ set g_onslaught_round_timelimit 280
+ set g_onslaught_teleport_radius 200 "Allows teleporting from a control point to another"
+ set g_onslaught_teleport_wait 5 "Time before player can teleport again"
+ set g_onslaught_spawn_choose 1 "Allow players to choose the control point to be spawned at"
+ set g_onslaught_click_radius 500 "When choosing from the map, this level of precision is required"
+ set g_onslaught_gen_health 2500
+ set g_onslaught_allow_vehicle_touch 0
+ set g_onslaught_cp_health 1000
+ set g_onslaught_cp_buildhealth 100
+ set g_onslaught_cp_buildtime 5
+ set g_onslaught_cp_regen 20
+ set g_onslaught_cp_proxydecap 0 "de-capture controlpoints by standing close to them"
+ set g_onslaught_cp_proxydecap_distance 512
+ set g_onslaught_cp_proxydecap_dps 100
+ set g_onslaught_shield_force 100
+ set g_onslaught_spawn_at_controlpoints 0
+ set g_onslaught_spawn_at_controlpoints_chance 0.5
+ set g_onslaught_spawn_at_controlpoints_random 0
+ set g_onslaught_spawn_at_generator 0
+ set g_onslaught_spawn_at_generator_chance 0
+ set g_onslaught_spawn_at_generator_random 0
+ // ======
+ //  race
+ // ======
+ set g_race 0 "Race: be faster than your opponents"
+ set g_race_qualifying_timelimit 0
+ set g_race_qualifying_timelimit_override -1 "qualifying session time limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ set g_race_teams 0    "when 2, 3, or 4, the race is played as a team game (the team members can add up their laps)"
+ // ==========
+ //  invasion
+ // ==========
+ set g_invasion 0 "Invasion: survive against waves of monsters"
+ set g_invasion_round_timelimit 120 "maximum time to kill all monsters"
+ set g_invasion_warmup 10 "time between waves to prepare for battle"
+ set g_invasion_monster_count 10 "number of monsters on first wave (increments)"
+ set g_invasion_zombies_only 0 "only spawn zombies"
+ set g_invasion_spawn_delay 0.25
+ set g_invasion_spawnpoint_spawn_delay 0.5
+ set g_invasion_teams 0 "number of teams in invasion (note: use mapinfo to set this)"
+ set g_invasion_team_spawns 1 "use team spawns in teamplay invasion mode"
++set g_invasion_type 0 "type of invasion mode - 0: round-based, 1: hunting, 2: complete the stage (note: use mapinfo to set this)"
diff --combined qcsrc/server/g_world.qc
index ff149e0641f26a012dc7758a70c9969d6ce8f045,2812da09d08cd5b6e26a3c9407bc43d17bdf3a3b..4a851b18d08f76b25f2b73b00c5f449185f6352c
@@@ -53,21 -53,21 +53,21 @@@ void PingPLReport_Think(entity this
        this.nextthink = time + delta;
  
        e = edict_num(this.cnt + 1);
-       if(IS_REAL_CLIENT(e))
+       if(IS_CLIENT(e) && IS_REAL_CLIENT(e))
        {
                WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT);
                WriteByte(MSG_BROADCAST, this.cnt);
-               WriteShort(MSG_BROADCAST, bound(1, e.ping, 65535));
-               WriteByte(MSG_BROADCAST, min(ceil(e.ping_packetloss * 255), 255));
-               WriteByte(MSG_BROADCAST, min(ceil(e.ping_movementloss * 255), 255));
+               WriteShort(MSG_BROADCAST, bound(1, CS(e).ping, 65535));
+               WriteByte(MSG_BROADCAST, min(ceil(CS(e).ping_packetloss * 255), 255));
+               WriteByte(MSG_BROADCAST, min(ceil(CS(e).ping_movementloss * 255), 255));
  
                // record latency times for clients throughout the match so we can report it to playerstats
-               if(time > (e.latency_time + LATENCY_THINKRATE))
+               if(time > (CS(e).latency_time + LATENCY_THINKRATE))
                {
-                       e.latency_sum += e.ping;
-                       e.latency_cnt += 1;
-                       e.latency_time = time;
-                       //print("sum: ", ftos(e.latency_sum), ", cnt: ", ftos(e.latency_cnt), ", avg: ", ftos(e.latency_sum / e.latency_cnt), ".\n");
+                       CS(e).latency_sum += CS(e).ping;
+                       CS(e).latency_cnt += 1;
+                       CS(e).latency_time = time;
+                       //print("sum: ", ftos(CS(e).latency_sum), ", cnt: ", ftos(CS(e).latency_cnt), ", avg: ", ftos(CS(e).latency_sum / CS(e).latency_cnt), ".\n");
                }
        }
        else
@@@ -176,7 -176,6 +176,6 @@@ void cvar_changes_init(
                BADPREFIX("cvar_check_");
                BADCVAR("gamecfg");
                BADCVAR("g_configversion");
-               BADCVAR("g_maplist_index");
                BADCVAR("halflifebsp");
                BADCVAR("sv_mapformat_is_quake2");
                BADCVAR("sv_mapformat_is_quake3");
                BADCVAR("mastervolume");
                BADCVAR("volume");
                BADCVAR("bgmvolume");
+               BADCVAR("in_pitch_min");
+               BADCVAR("in_pitch_max");
  
                // private
                BADCVAR("developer");
  
                // these can contain player IDs, so better hide
                BADPREFIX("g_forced_team_");
+               BADCVAR("sv_muteban_list");
+               BADCVAR("sv_allow_customplayermodels_idlist");
+               BADCVAR("sv_allow_customplayermodels_speciallist");
  
                // mapinfo
                BADCVAR("fraglimit");
                BADCVAR("g_freezetag");
                BADCVAR("g_freezetag_teams");
                BADCVAR("g_invasion_teams");
 +              BADCVAR("g_invasion_type");
                BADCVAR("g_jailbreak");
                BADCVAR("g_jailbreak_teams");
                BADCVAR("g_keepaway");
                BADCVAR("g_race_laps_limit");
                BADCVAR("g_race_qualifying_timelimit");
                BADCVAR("g_race_qualifying_timelimit_override");
+               BADCVAR("g_runematch");
                BADCVAR("g_snafu");
                BADCVAR("g_tdm");
                BADCVAR("g_tdm_teams");
  
                // does nothing gameplay relevant
                BADCVAR("captureleadlimit_override");
+               BADCVAR("condump_stripcolors");
                BADCVAR("gameversion");
                BADCVAR("g_allow_oldvortexbeam");
                BADCVAR("g_balance_kill_delay");
                BADCVAR("g_invasion_point_limit");
                BADCVAR("g_jump_grunt");
                BADCVAR("g_keyhunt_point_leadlimit");
-               BADCVAR("g_maplist_selectrandom");
                BADCVAR("g_nexball_goalleadlimit");
                BADCVAR("g_new_toys_use_pickupsound");
                BADCVAR("g_physics_predictall");
                BADCVAR("leadlimit_override");
                BADCVAR("pausable");
                BADCVAR("sv_checkforpacketsduringsleep");
+               BADCVAR("sv_damagetext");
                BADCVAR("sv_db_saveasdump");
                BADCVAR("sv_intermission_cdtrack");
                BADCVAR("sv_minigames");
                BADPREFIX("gameversion_");
                BADPREFIX("g_chat_");
                BADPREFIX("g_ctf_captimerecord_");
-               BADPREFIX("g_maplist_votable_");
+               BADPREFIX("g_maplist_");
                BADPREFIX("g_mod_");
                BADPREFIX("g_respawn_");
                BADPREFIX("net_");
+               BADPREFIX("notification_");
                BADPREFIX("prvm_");
                BADPREFIX("skill_");
                BADPREFIX("sv_allow_");
                BADCVAR("g_buffs");
                BADCVAR("g_ca_teams_override");
                BADCVAR("g_ctf_ignore_frags");
+               BADCVAR("g_ctf_leaderboard");
                BADCVAR("g_domination_point_limit");
                BADCVAR("g_domination_teams_override");
                BADCVAR("g_freezetag_teams_override");
                BADCVAR("g_keyhunt_teams_override");
                BADCVAR("g_lms_lives_override");
                BADCVAR("g_maplist");
-               BADCVAR("g_maplist_check_waypoints");
-               BADCVAR("g_maplist_mostrecent_count");
-               BADCVAR("g_maplist_shuffle");
-               BADCVAR("g_maplist_votable");
-               BADCVAR("g_maplist_votable_abstain");
-               BADCVAR("g_maplist_votable_nodetail");
-               BADCVAR("g_maplist_votable_suggestions");
                BADCVAR("g_maxplayers");
                BADCVAR("g_mirrordamage");
                BADCVAR("g_nexball_goallimit");
                BADCVAR("teamplay_mode");
                BADCVAR("timelimit_override");
                BADPREFIX("g_warmup_");
+               BADPREFIX("sv_info_");
                BADPREFIX("sv_ready_restart_");
  
                // mutators that announce themselves properly to the server browser
@@@ -1329,8 -1330,8 +1331,8 @@@ void IntermissionThink(entity this
  {
        FixIntermissionClient(this);
  
-       float server_screenshot = (autocvar_sv_autoscreenshot && this.cvar_cl_autoscreenshot);
-       float client_screenshot = (this.cvar_cl_autoscreenshot == 2);
+       float server_screenshot = (autocvar_sv_autoscreenshot && CS(this).cvar_cl_autoscreenshot);
+       float client_screenshot = (CS(this).cvar_cl_autoscreenshot == 2);
  
        if( (server_screenshot || client_screenshot)
                && ((this.autoscreenshot > 0) && (time > this.autoscreenshot)) )
@@@ -1458,9 -1459,9 +1460,9 @@@ void DumpStats(float final
        if(to_file)
                fputs(file, strcat(s, "\n"));
  
-       FOREACH_CLIENT(IS_REAL_CLIENT(it) || (IS_BOT_CLIENT(it) && autocvar_sv_logscores_bots), LAMBDA(
+       FOREACH_CLIENT(IS_REAL_CLIENT(it) || (IS_BOT_CLIENT(it) && autocvar_sv_logscores_bots), {
                s = strcat(":player:see-labels:", GetPlayerScoreString(it, 0), ":");
-               s = strcat(s, ftos(rint(time - it.jointime)), ":");
+               s = strcat(s, ftos(rint(time - CS(it).jointime)), ":");
                if(IS_PLAYER(it) || MUTATOR_CALLHOOK(GetPlayerStatus, it))
                        s = strcat(s, ftos(it.team), ":");
                else
                        GameLogEcho(strcat(s, ftos(it.playerid), ":", playername(it, false)));
                if(to_file)
                        fputs(file, strcat(s, playername(it, false), "\n"));
-       ));
+       });
  
        if(teamplay)
        {
@@@ -1529,9 -1530,9 +1531,9 @@@ void FixIntermissionClient(entity e
                {
                        stuffcmd(e, "\nscr_printspeed 1000000\n");
                        RandomSelection_Init();
-                       FOREACH_WORD(autocvar_sv_intermission_cdtrack, true, LAMBDA(
+                       FOREACH_WORD(autocvar_sv_intermission_cdtrack, true, {
                                RandomSelection_AddString(it, 1, 1);
-                       ));
+                       });
                        if (RandomSelection_chosen_string != "")
                        {
                                stuffcmd(e, sprintf("\ncd loop %s\n", RandomSelection_chosen_string));
@@@ -1581,11 -1582,11 +1583,11 @@@ void NextLevel(
  
        GameLogClose();
  
-       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
+       FOREACH_CLIENT(IS_PLAYER(it), {
                FixIntermissionClient(it);
                if(it.winning)
                        bprint(playername(it, false), " ^7wins.\n");
-       ));
+       });
  
        target_music_kill();
  
        localcmd("\nsv_hook_gameend\n");
  }
  
- /*
- ============
- CheckRules_Player
- Exit deathmatch games upon conditions
- ============
- */
- void CheckRules_Player(entity this)
- {
-       if (game_stopped) // someone else quit the game already
-               return;
-       if(!IS_DEAD(this))
-               this.play_time += frametime;
-       // fixme: don't check players; instead check spawnfunc_dom_team and spawnfunc_ctf_team entities
-       //   (div0: and that in CheckRules_World please)
- }
  
  float InitiateSuddenDeath()
  {
@@@ -1678,22 -1660,22 +1661,22 @@@ float GetWinningCode(float fraglimitrea
  // set the .winning flag for exactly those players with a given field value
  void SetWinners(.float field, float value)
  {
-       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(it.winning = (it.(field) == value)));
+       FOREACH_CLIENT(IS_PLAYER(it), { it.winning = (it.(field) == value); });
  }
  
  // set the .winning flag for those players with a given field value
  void AddWinners(.float field, float value)
  {
-       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
+       FOREACH_CLIENT(IS_PLAYER(it), {
                if(it.(field) == value)
                        it.winning = 1;
-       ));
+       });
  }
  
  // clear the .winning flags
  void ClearWinners()
  {
-       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(it.winning = 0));
+       FOREACH_CLIENT(IS_PLAYER(it), { it.winning = 0; });
  }
  
  void ShuffleMaplist()
@@@ -1786,7 -1768,7 +1769,7 @@@ float WinningCondition_RanOutOfSpawns(
  
        team1_score = team2_score = team3_score = team4_score = 0;
  
-       FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), LAMBDA(
+       FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), {
                switch(it.team)
                {
                        case NUM_TEAM_1: team1_score = 1; break;
                        case NUM_TEAM_3: team3_score = 1; break;
                        case NUM_TEAM_4: team4_score = 1; break;
                }
-       ));
+       });
  
        IL_EACH(g_spawnpoints, true,
        {
@@@ -1920,13 -1902,13 +1903,13 @@@ void CheckRules_World(
                                float playerswithlaps;
                                float readyplayers;
                                totalplayers = playerswithlaps = readyplayers = 0;
-                               FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
+                               FOREACH_CLIENT(IS_PLAYER(it), {
                                        ++totalplayers;
                                        if(PlayerScore_Add(it, SP_RACE_FASTEST, 0))
                                                ++playerswithlaps;
                                        if(it.ready)
                                                ++readyplayers;
-                               ));
+                               });
  
                                // at least 2 of the players have completed a lap: start the RACE
                                // otherwise, the players should end the qualifying on their own
@@@ -2146,7 -2128,7 +2129,7 @@@ float RedirectionThink(
        redirection_nextthink = time + 1;
  
        clients_found = 0;
-       FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA(
+       FOREACH_CLIENT(IS_REAL_CLIENT(it), {
                // TODO add timer
                LOG_INFO("Redirecting: sending connect command to ", it.netname, "\n");
                if(redirection_target == "self")
                else
                        stuffcmd(it, strcat("\ndisconnect; defer ", ftos(autocvar_quit_and_redirect_timer), " \"connect ", redirection_target, "\"\n"));
                ++clients_found;
-       ));
+       });
  
        LOG_INFO("Redirecting: ", ftos(clients_found), " clients left.\n");
  
index 51e8459d82476b4f8e7d4ef7bb0bc995a04593cd,fabcc26f62cb26bef62edda59104324330421a82..b7aa99498557c6035d0b3ed0be46e79d5d1448e5
@@@ -5,9 -5,6 +5,9 @@@
  
  #include <server/teamplay.qh>
  
 +IntrusiveList g_invasion_roundends;
 +STATIC_INIT(g_invasion_roundends) { g_invasion_roundends = IL_NEW(); }
 +
  IntrusiveList g_invasion_waves;
  STATIC_INIT(g_invasion_waves) { g_invasion_waves = IL_NEW(); }
  
@@@ -21,44 -18,10 +21,46 @@@ int autocvar_g_invasion_monster_count
  bool autocvar_g_invasion_zombies_only;
  float autocvar_g_invasion_spawn_delay;
  
 +bool victent_present;
 +.bool inv_endreached;
 +
+ bool inv_warning_shown; // spammy
  .string spawnmob;
  
 +void target_invasion_roundend_use(entity this, entity actor, entity trigger)
 +{
 +      if(!IS_PLAYER(actor)) { return; }
 +
 +      actor.inv_endreached = true;
 +
 +      int plnum = 0;
 +      int realplnum = 0;
 +      // let's not count bots
 +      FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), {
 +              ++realplnum;
 +              if(it.inv_endreached)
 +                      ++plnum;
 +      });
 +      if(plnum < ceil(realplnum * min(1, this.count))) // 70% of players
 +              return;
 +
 +      this.winning = true;
 +}
 +
 +spawnfunc(target_invasion_roundend)
 +{
 +      if(!g_invasion) { delete(this); return; }
 +
 +      victent_present = true; // a victory entity is present, we don't need to rely on monster count TODO: merge this with the intrusive list (can check empty)
 +
 +      if(!this.count) { this.count = 0.7; } // require at least 70% of the players to reach the end before triggering victory
 +
 +      this.use = target_invasion_roundend_use;
 +
 +      IL_PUSH(g_invasion_roundends, this);
 +}
 +
  spawnfunc(invasion_wave)
  {
        if(!g_invasion) { delete(this); return; }
@@@ -74,58 -37,6 +76,58 @@@ spawnfunc(invasion_spawnpoint
        IL_PUSH(g_invasion_spawns, this);
  }
  
 +// Invasion stage mode winning condition: If the attackers triggered a round end (by fulfilling all objectives)
 +// they win.
 +int WinningCondition_Invasion()
 +{
 +      WinningConditionHelper(NULL); // set worldstatus
 +
 +      int status = WINNING_NO;
 +
 +      if(autocvar_g_invasion_type == INV_TYPE_STAGE)
 +      {
 +              SetWinners(inv_endreached, true);
 +
 +              int found = 0;
 +              IL_EACH(g_invasion_roundends, true,
 +              {
 +                      ++found;
 +                      if(it.winning)
 +                      {
 +                              bprint("Invasion: round completed.\n");
 +                              // winners already set (TODO: teamplay support)
 +
 +                              status = WINNING_YES;
 +                              break;
 +                      }
 +              });
 +
 +              if(!found)
 +                      status = WINNING_YES; // just end it? TODO: should warn mapper!
 +      }
 +      else if(autocvar_g_invasion_type == INV_TYPE_HUNT)
 +      {
 +              ClearWinners();
 +
 +              int found = 0; // NOTE: this ends the round if no monsters are placed
 +              IL_EACH(g_monsters, !(it.spawnflags & MONSTERFLAG_RESPAWNED),
 +              {
 +                      ++found;
 +              });
 +
 +              if(found <= 0)
 +              {
 +                      FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
 +                      {
 +                              it.winning = true;
 +                      });
 +                      status = WINNING_YES;
 +              }
 +      }
 +
 +      return status;
 +}
 +
  Monster invasion_PickMonster(int supermonster_count)
  {
        RandomSelection_Init();
@@@ -194,9 -105,13 +196,13 @@@ void invasion_SpawnChosenMonster(Monste
  
        if(spawn_point == NULL)
        {
-               LOG_TRACE("Warning: couldn't find any invasion_spawnpoint spawnpoints, attempting to spawn monsters in random locations");
+               if(!inv_warning_shown)
+               {
+                       inv_warning_shown = true;
+                       LOG_TRACE("Warning: couldn't find any invasion_spawnpoint spawnpoints, attempting to spawn monsters in random locations");
+               }
                entity e = spawn();
-               setsize(e, mon.mins, mon.maxs);
+               setsize(e, mon.m_mins, mon.m_maxs);
  
                if(MoveToRandomMapLocation(e, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256))
                        monster = spawnmonster(e, tospawn, mon.monsterid, NULL, NULL, e.origin, false, false, 2);
                }
        }
  
+       if(monster.monster_attack)
+               IL_REMOVE(g_monster_targets, monster);
        monster.monster_attack = false; // it's the player's job to kill all the monsters
  
        if(inv_roundcnt >= inv_maxrounds)
@@@ -335,14 -252,14 +343,14 @@@ bool Invasion_CheckWinner(
        }
        else
        {
-               FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
+               FOREACH_CLIENT(IS_PLAYER(it), {
                        float cs = PlayerScore_Add(it, SP_KILLS, 0);
                        if(cs > winning_score)
                        {
                                winning_score = cs;
                                winner = it;
                        }
-               ));
+               });
        }
  
        IL_EACH(g_monsters, true,
@@@ -378,10 -295,10 +386,10 @@@ bool Invasion_CheckPlayers(
  void Invasion_RoundStart()
  {
        int numplayers = 0;
-       FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
+       FOREACH_CLIENT(IS_PLAYER(it), {
                it.player_blocked = false;
                ++numplayers;
-       ));
+       });
  
        if(inv_roundcnt < inv_maxrounds)
                inv_roundcnt += 1; // a limiter to stop crazy counts
@@@ -411,11 -328,8 +419,11 @@@ MUTATOR_HOOKFUNCTION(inv, MonsterDies
  
        if(!(frag_target.spawnflags & MONSTERFLAG_RESPAWNED))
        {
 -              inv_numkilled += 1;
 -              inv_maxcurrent -= 1;
 +              if(autocvar_g_invasion_type == INV_TYPE_ROUND)
 +              {
 +                      inv_numkilled += 1;
 +                      inv_maxcurrent -= 1;
 +              }
                if(teamplay) { inv_monsters_perteam[frag_target.team] -= 1; }
  
                if(IS_PLAYER(frag_attacker))
  MUTATOR_HOOKFUNCTION(inv, MonsterSpawn)
  {
        entity mon = M_ARGV(0, entity);
 +      mon.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
 +
 +      if(autocvar_g_invasion_type == INV_TYPE_HUNT)
 +              return false; // allowed
  
        if(!(mon.spawnflags & MONSTERFLAG_SPAWNED))
                return true;
  
        if((get_monsterinfo(mon.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER)
                Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_INVASION_SUPERMONSTER, mon.monster_name);
 -
 -      mon.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
 -}
 -
 -MUTATOR_HOOKFUNCTION(inv, OnEntityPreSpawn)
 -{
 -      entity ent = M_ARGV(0, entity);
 -
 -      // TODO: allow these as "rogues" or something
 -      if(startsWith(ent.classname, "monster_"))
 -      if(!(ent.spawnflags & MONSTERFLAG_SPAWNED))
 -              return true;
  }
  
  MUTATOR_HOOKFUNCTION(inv, SV_StartFrame)
  {
 +      if(autocvar_g_invasion_type != INV_TYPE_ROUND)
 +              return; // uses map spawned monsters
 +
        monsters_total = inv_maxspawned; // TODO: make sure numspawned never exceeds maxspawned
        monsters_killed = inv_numkilled;
  }
  
  MUTATOR_HOOKFUNCTION(inv, PlayerRegen)
  {
 -      // no regeneration in invasion
 +      // no regeneration in invasion, regardless of the game type
        return true;
  }
  
@@@ -494,6 -413,28 +502,6 @@@ MUTATOR_HOOKFUNCTION(inv, Damage_Calcul
        }
  }
  
 -MUTATOR_HOOKFUNCTION(inv, SV_ParseClientCommand)
 -{
 -      if(MUTATOR_RETURNVALUE) // command was already handled?
 -              return;
 -
 -      entity player = M_ARGV(0, entity);
 -      string cmd_name = M_ARGV(1, string);
 -
 -      if(cmd_name == "debuginvasion")
 -      {
 -              sprint(player, strcat("inv_maxspawned = ", ftos(inv_maxspawned), "\n"));
 -              sprint(player, strcat("inv_numspawned = ", ftos(inv_numspawned), "\n"));
 -              sprint(player, strcat("inv_numkilled = ", ftos(inv_numkilled), "\n"));
 -              sprint(player, strcat("inv_roundcnt = ", ftos(inv_roundcnt), "\n"));
 -              sprint(player, strcat("monsters_total = ", ftos(monsters_total), "\n"));
 -              sprint(player, strcat("monsters_killed = ", ftos(monsters_killed), "\n"));
 -              sprint(player, strcat("inv_monsterskill = ", ftos(inv_monsterskill), "\n"));
 -
 -              return true;
 -      }
 -}
 -
  MUTATOR_HOOKFUNCTION(inv, BotShouldAttack)
  {
        entity targ = M_ARGV(1, entity);
  
  MUTATOR_HOOKFUNCTION(inv, SetStartItems)
  {
 -      start_health = 200;
 -      start_armorvalue = 200;
 +      if(autocvar_g_invasion_type == INV_TYPE_ROUND)
 +      {
 +              start_health = 200;
 +              start_armorvalue = 200;
 +      }
  }
  
  MUTATOR_HOOKFUNCTION(inv, AccuracyTargetValid)
@@@ -527,15 -465,6 +535,15 @@@ MUTATOR_HOOKFUNCTION(inv, AllowMobSpawn
        return true;
  }
  
 +MUTATOR_HOOKFUNCTION(inv, CheckRules_World)
 +{
 +      if(autocvar_g_invasion_type == INV_TYPE_ROUND)
 +              return false;
 +
 +      M_ARGV(0, float) = WinningCondition_Invasion();
 +      return true;
 +}
 +
  MUTATOR_HOOKFUNCTION(inv, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
  {
        M_ARGV(0, float) = invasion_teams;
@@@ -558,9 -487,6 +566,9 @@@ void invasion_ScoreRules(int inv_teams
  
  void invasion_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up.
  {
 +      if(autocvar_g_invasion_type == INV_TYPE_HUNT || autocvar_g_invasion_type == INV_TYPE_STAGE)
 +              cvar_set("fraglimit", "0");
 +
        if(autocvar_g_invasion_teams)
        {
                invasion_teams = bound(2, autocvar_g_invasion_teams, 4);
  
        independent_players = 0;
  
 -      round_handler_Spawn(Invasion_CheckPlayers, Invasion_CheckWinner, Invasion_RoundStart);
 -      round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit);
 +      if(autocvar_g_invasion_type == INV_TYPE_ROUND)
 +      {
 +              round_handler_Spawn(Invasion_CheckPlayers, Invasion_CheckWinner, Invasion_RoundStart);
 +              round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit);
  
 -      inv_roundcnt = 0;
 -      inv_maxrounds = 15; // 15?
 +              inv_roundcnt = 0;
 +              inv_maxrounds = 15; // 15?
 +      }
  }
  
  void invasion_Initialize()