]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge remote-tracking branch 'origin/master' into pending-release
authorbones_was_here <bones_was_here@xonotic.au>
Sun, 28 May 2023 04:38:13 +0000 (14:38 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Sun, 28 May 2023 04:38:13 +0000 (14:38 +1000)
99 files changed:
.gitlab-ci.yml
gamemodes-client.cfg
gamemodes-server.cfg
gfx/hud/default/tka_taken_blue.tga [new file with mode: 0644]
gfx/hud/default/tka_taken_pink.tga [new file with mode: 0644]
gfx/hud/default/tka_taken_red.tga [new file with mode: 0644]
gfx/hud/default/tka_taken_yellow.tga [new file with mode: 0644]
gfx/hud/luma/tka_taken_blue.tga [new file with mode: 0644]
gfx/hud/luma/tka_taken_pink.tga [new file with mode: 0644]
gfx/hud/luma/tka_taken_red.tga [new file with mode: 0644]
gfx/hud/luma/tka_taken_yellow.tga [new file with mode: 0644]
gfx/menu/luma/gametype_surv.tga [new file with mode: 0644]
gfx/menu/luma/gametype_tka.tga [new file with mode: 0644]
gfx/menu/luminos/gametype_surv.tga [new file with mode: 0644]
gfx/menu/luminos/gametype_tka.tga [new file with mode: 0644]
gfx/menu/wickedx/gametype_surv.tga [new file with mode: 0644]
gfx/menu/wickedx/gametype_tka.tga [new file with mode: 0644]
gfx/menu/xaw/gametype_surv.tga [new file with mode: 0644]
gfx/menu/xaw/gametype_tka.tga [new file with mode: 0644]
models/ctf/banner.tga [new file with mode: 0644]
models/ctf/banner_blue.tga [deleted file]
models/ctf/banner_gloss.tga [new file with mode: 0644]
models/ctf/banner_neutral.tga [deleted file]
models/ctf/banner_neutral_gloss.tga [deleted file]
models/ctf/banner_pink.tga [deleted file]
models/ctf/banner_pink_gloss.tga [deleted file]
models/ctf/banner_red.tga [deleted file]
models/ctf/banner_red_gloss.tga [deleted file]
models/ctf/banner_shirt.tga [new file with mode: 0644]
models/ctf/banner_yellow.tga [deleted file]
models/ctf/banner_yellow_gloss.tga [deleted file]
models/ctf/flag.tga [new file with mode: 0644]
models/ctf/flag_blue.tga [deleted file]
models/ctf/flag_blue_gloss.tga [deleted file]
models/ctf/flag_blue_glow.tga [deleted file]
models/ctf/flag_blue_norm.tga [deleted file]
models/ctf/flag_gloss.tga [new file with mode: 0644]
models/ctf/flag_glow.tga [new file with mode: 0644]
models/ctf/flag_neutral.tga [deleted file]
models/ctf/flag_neutral_gloss.tga [deleted file]
models/ctf/flag_neutral_glow.tga [deleted file]
models/ctf/flag_neutral_norm.tga [deleted file]
models/ctf/flag_norm.tga [new file with mode: 0644]
models/ctf/flag_pink.tga [deleted file]
models/ctf/flag_pink_gloss.tga [deleted file]
models/ctf/flag_pink_glow.tga [deleted file]
models/ctf/flag_pink_norm.tga [deleted file]
models/ctf/flag_red.tga [deleted file]
models/ctf/flag_red_gloss.tga [deleted file]
models/ctf/flag_red_glow.tga [deleted file]
models/ctf/flag_red_norm.tga [deleted file]
models/ctf/flag_yellow.tga [deleted file]
models/ctf/flag_yellow_gloss.tga [deleted file]
models/ctf/flag_yellow_glow.tga [deleted file]
models/ctf/flag_yellow_norm.tga [deleted file]
models/ctf/flags.md3_0.skin
models/ctf/flags.md3_1.skin [deleted file]
models/ctf/flags.md3_2.skin [deleted file]
models/ctf/flags.md3_3.skin [deleted file]
models/ctf/flags.md3_4.skin [deleted file]
models/ctf/glow.tga [new file with mode: 0644]
models/ctf/glow_blue.tga [deleted file]
models/ctf/glow_blue_glow.tga [deleted file]
models/ctf/glow_glow.tga [new file with mode: 0644]
models/ctf/glow_neutral.tga [deleted file]
models/ctf/glow_neutral_glow.tga [deleted file]
models/ctf/glow_pink.tga [deleted file]
models/ctf/glow_pink_glow.tga [deleted file]
models/ctf/glow_red.tga [deleted file]
models/ctf/glow_red_glow.tga [deleted file]
models/ctf/glow_yellow.tga [deleted file]
models/ctf/glow_yellow_glow.tga [deleted file]
qcsrc/common/ent_cs.qc
qcsrc/common/gamemodes/gamemode/_mod.inc
qcsrc/common/gamemodes/gamemode/_mod.qh
qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc
qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qc
qcsrc/common/gamemodes/gamemode/survival/_mod.inc [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/survival/_mod.qh [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/survival/cl_survival.qc [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/survival/cl_survival.qh [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/survival/survival.qc [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/survival/survival.qh [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/survival/sv_survival.qc [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/survival/sv_survival.qh [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/tka/_mod.inc [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/tka/_mod.qh [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/tka/cl_tka.qc [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/tka/cl_tka.qh [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/tka/sv_tka.qc [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/tka/sv_tka.qh [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/tka/tka.qc [new file with mode: 0644]
qcsrc/common/gamemodes/gamemode/tka/tka.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/waypoints/all.inc
qcsrc/common/scores.qh
qcsrc/common/stats.qh
qcsrc/menu/xonotic/util.qc
qcsrc/server/world.qc
scripts/ctf.shader

index 1feae253cbcd4231b69ae551e0466917e3ee043e..58d1e45823ba3579e85304b567163ae4a00c6a47 100644 (file)
@@ -75,7 +75,7 @@ test_sv_game:
     - wget -nv -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints\r
     - wget -nv -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache\r
 \r
-    - EXPECT=20c9c4ea0364fbb611656cc0b876e02b\r
+    - EXPECT=b4bfc34b5a52ba950de9814c2c52c1c3\r
     - HASH=$(${ENGINE} +exec serverbench.cfg\r
       | tee /dev/stderr\r
       | grep '^:'\r
index 259c2c31dd3194e93cf125d29c4159938bad9eb5..c63bba5a76efb86a6ca15ae8ba4405dd4598828d 100644 (file)
@@ -34,6 +34,8 @@ alias cl_hook_gamestart_inv
 alias cl_hook_gamestart_duel
 alias cl_hook_gamestart_mayhem
 alias cl_hook_gamestart_tmayhem
+alias cl_hook_gamestart_tka
+alias cl_hook_gamestart_surv
 alias cl_hook_gameend
 alias cl_hook_shutdown
 alias cl_hook_activeweapon
index bccfc9e34a3a4e618f14e348d2d3a9833bdd3bef..01d297e084a27827809150fc6aa2cfd705b9bc7b 100644 (file)
@@ -31,6 +31,8 @@ alias sv_hook_gamestart_inv
 alias sv_hook_gamestart_duel
 alias sv_hook_gamestart_mayhem
 alias sv_hook_gamestart_tmayhem
+alias sv_hook_gamestart_tka
+alias sv_hook_gamestart_surv
 // there is currently no hook for when the match is restarted
 // see sv_hook_readyrestart for previous uses of this hook
 //alias sv_hook_gamerestart
@@ -62,6 +64,8 @@ alias sv_vote_gametype_hook_tdm
 alias sv_vote_gametype_hook_duel
 alias sv_vote_gametype_hook_mayhem
 alias sv_vote_gametype_hook_tmayhem
+alias sv_vote_gametype_hook_tka
+alias sv_vote_gametype_hook_surv
 
 // Example preset to allow 1v1ctf to be used for the gametype voting screen.
 // Aliases can have max 31 chars so the gametype can have max 9 chars.
@@ -226,6 +230,20 @@ set g_tmayhem_respawn_delay_large_count 0
 set g_tmayhem_respawn_delay_max 0
 set g_tmayhem_respawn_waves 0
 set g_tmayhem_weapon_stay 0
+set g_tka_respawn_delay_small 0
+set g_tka_respawn_delay_small_count 0
+set g_tka_respawn_delay_large 0
+set g_tka_respawn_delay_large_count 0
+set g_tka_respawn_delay_max 0
+set g_tka_respawn_waves 0
+set g_tka_weapon_stay 0
+set g_surv_respawn_delay_small 0
+set g_surv_respawn_delay_small_count 0
+set g_surv_respawn_delay_large 0
+set g_surv_respawn_delay_large_count 0
+set g_surv_respawn_delay_max 0
+set g_surv_respawn_waves 0
+set g_surv_weapon_stay 0
 
 
 // =========
@@ -636,3 +654,46 @@ set g_tmayhem_rot 0 "health and/or armor rotting, according to g_balance_health_
 set g_tmayhem_teams 2 "how many teams are in team mayhem (set by mapinfo)"
 set g_tmayhem_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
 set g_tmayhem_teams_override 0 "how many teams are in team mayhem"
+
+// ===============
+//  team keepaway
+// ===============
+set g_tka 0 "another game mode which focuses around a ball"
+set g_tka_on_ka_maps 1 "when this is set, all KA maps automatically support TKA"
+set g_tka_on_tdm_maps 0 "when this is set, all TDM maps automatically support TKA"
+set g_tka_teams 2 "how many teams are in team keepaway (set by mapinfo)"
+set g_tka_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
+set g_tka_teams_override 0     "how many teams are in team keepaway"
+set g_tka_point_limit -1 "TKA point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+set g_tka_point_leadlimit -1 "TKA point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+set g_tka_score_team 1 "allow points to be awarded to teammates for any kill when the ball is in your team's possession"
+set g_tka_score_bckill 1 "points for killing the ball barrier (Ball Carrier Kill)"
+set g_tka_score_killac 1 "points for kills while holding the ball (Kill As Carrier)"
+set g_tka_score_timeinterval 1 "amount of time it takes between intervals for timepoints to be added to the score"
+set g_tka_score_timepoints 0 "points to add to score per timeinterval, 0 for no points"
+set g_tka_ballcarrier_effects 8 "Add together the numbers you want: EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)"
+set g_tka_ballcarrier_highspeed 1 "speed multiplier done to the person holding the ball (recommended when used with some mutators)"
+set g_tka_ballcarrier_damage   1       "damage multiplier while holding the ball"
+set g_tka_ballcarrier_force    1       "force multiplier while holding the ball"
+set g_tka_ballcarrier_selfdamage       1       "self damage multiplier while holding the ball"
+set g_tka_ballcarrier_selfforce        1       "self force multiplier while holding the ball"
+set g_tka_noncarrier_warn      1       "warn players when they kill without holding the ball"
+set g_tka_noncarrier_damage    1       "damage done to other players if both you and they don't have the ball"
+set g_tka_noncarrier_force     1       "force done to other players if both you and they don't have the ball"
+set g_tka_noncarrier_selfdamage        1       "self damage if you don't have the ball"
+set g_tka_noncarrier_selfforce 1       "self force if you don't have the ball"
+set g_tkaball_effects 8 "Add together the numbers you want: EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)"
+set g_tkaball_trail_color      254     "particle trail color from player/ball"
+set g_tkaball_damageforcescale 2 "Scale of force which is applied to the ball by weapons/explosions/etc"
+set g_tkaball_respawntime      10      "if no one picks up the ball, how long to wait until the ball respawns"
+
+// ==========
+//  survival
+// ==========
+set g_survival 0 "Survival: identify and eliminate all the hunters before all your allies are gone"
+set g_survival_not_lms_maps 0 "when this is set, LMS maps will NOT be listed in survival"
+set g_survival_hunter_count 0.25 "number of players who will become hunters, set between 0 and 0.9 to use a multiplier of the current players, or 1 and above to specify an exact number of players"
+set g_survival_punish_teamkill 1 "kill the player when they kill an ally"
+set g_survival_reward_survival 1 "give a point to all surviving players if the round timelimit is reached, in addition to the points given for kills"
+set g_survival_warmup 10 "how long the players will have time to run around the map before the round starts"
+set g_survival_round_timelimit 120 "round time limit in seconds"
diff --git a/gfx/hud/default/tka_taken_blue.tga b/gfx/hud/default/tka_taken_blue.tga
new file mode 100644 (file)
index 0000000..67ed3f5
Binary files /dev/null and b/gfx/hud/default/tka_taken_blue.tga differ
diff --git a/gfx/hud/default/tka_taken_pink.tga b/gfx/hud/default/tka_taken_pink.tga
new file mode 100644 (file)
index 0000000..1263eac
Binary files /dev/null and b/gfx/hud/default/tka_taken_pink.tga differ
diff --git a/gfx/hud/default/tka_taken_red.tga b/gfx/hud/default/tka_taken_red.tga
new file mode 100644 (file)
index 0000000..eb7fc58
Binary files /dev/null and b/gfx/hud/default/tka_taken_red.tga differ
diff --git a/gfx/hud/default/tka_taken_yellow.tga b/gfx/hud/default/tka_taken_yellow.tga
new file mode 100644 (file)
index 0000000..06f5b5a
Binary files /dev/null and b/gfx/hud/default/tka_taken_yellow.tga differ
diff --git a/gfx/hud/luma/tka_taken_blue.tga b/gfx/hud/luma/tka_taken_blue.tga
new file mode 100644 (file)
index 0000000..0d517b0
Binary files /dev/null and b/gfx/hud/luma/tka_taken_blue.tga differ
diff --git a/gfx/hud/luma/tka_taken_pink.tga b/gfx/hud/luma/tka_taken_pink.tga
new file mode 100644 (file)
index 0000000..954f3bd
Binary files /dev/null and b/gfx/hud/luma/tka_taken_pink.tga differ
diff --git a/gfx/hud/luma/tka_taken_red.tga b/gfx/hud/luma/tka_taken_red.tga
new file mode 100644 (file)
index 0000000..d540cc5
Binary files /dev/null and b/gfx/hud/luma/tka_taken_red.tga differ
diff --git a/gfx/hud/luma/tka_taken_yellow.tga b/gfx/hud/luma/tka_taken_yellow.tga
new file mode 100644 (file)
index 0000000..7f0da09
Binary files /dev/null and b/gfx/hud/luma/tka_taken_yellow.tga differ
diff --git a/gfx/menu/luma/gametype_surv.tga b/gfx/menu/luma/gametype_surv.tga
new file mode 100644 (file)
index 0000000..050fb7b
Binary files /dev/null and b/gfx/menu/luma/gametype_surv.tga differ
diff --git a/gfx/menu/luma/gametype_tka.tga b/gfx/menu/luma/gametype_tka.tga
new file mode 100644 (file)
index 0000000..b61fc08
Binary files /dev/null and b/gfx/menu/luma/gametype_tka.tga differ
diff --git a/gfx/menu/luminos/gametype_surv.tga b/gfx/menu/luminos/gametype_surv.tga
new file mode 100644 (file)
index 0000000..c548665
Binary files /dev/null and b/gfx/menu/luminos/gametype_surv.tga differ
diff --git a/gfx/menu/luminos/gametype_tka.tga b/gfx/menu/luminos/gametype_tka.tga
new file mode 100644 (file)
index 0000000..b4adb18
Binary files /dev/null and b/gfx/menu/luminos/gametype_tka.tga differ
diff --git a/gfx/menu/wickedx/gametype_surv.tga b/gfx/menu/wickedx/gametype_surv.tga
new file mode 100644 (file)
index 0000000..c548665
Binary files /dev/null and b/gfx/menu/wickedx/gametype_surv.tga differ
diff --git a/gfx/menu/wickedx/gametype_tka.tga b/gfx/menu/wickedx/gametype_tka.tga
new file mode 100644 (file)
index 0000000..74d422d
Binary files /dev/null and b/gfx/menu/wickedx/gametype_tka.tga differ
diff --git a/gfx/menu/xaw/gametype_surv.tga b/gfx/menu/xaw/gametype_surv.tga
new file mode 100644 (file)
index 0000000..debbce0
Binary files /dev/null and b/gfx/menu/xaw/gametype_surv.tga differ
diff --git a/gfx/menu/xaw/gametype_tka.tga b/gfx/menu/xaw/gametype_tka.tga
new file mode 100644 (file)
index 0000000..d8e1ba5
Binary files /dev/null and b/gfx/menu/xaw/gametype_tka.tga differ
diff --git a/models/ctf/banner.tga b/models/ctf/banner.tga
new file mode 100644 (file)
index 0000000..718d40b
Binary files /dev/null and b/models/ctf/banner.tga differ
diff --git a/models/ctf/banner_blue.tga b/models/ctf/banner_blue.tga
deleted file mode 100644 (file)
index 8b95589..0000000
Binary files a/models/ctf/banner_blue.tga and /dev/null differ
diff --git a/models/ctf/banner_gloss.tga b/models/ctf/banner_gloss.tga
new file mode 100644 (file)
index 0000000..77168a8
Binary files /dev/null and b/models/ctf/banner_gloss.tga differ
diff --git a/models/ctf/banner_neutral.tga b/models/ctf/banner_neutral.tga
deleted file mode 100644 (file)
index 5c3de94..0000000
Binary files a/models/ctf/banner_neutral.tga and /dev/null differ
diff --git a/models/ctf/banner_neutral_gloss.tga b/models/ctf/banner_neutral_gloss.tga
deleted file mode 100644 (file)
index 77168a8..0000000
Binary files a/models/ctf/banner_neutral_gloss.tga and /dev/null differ
diff --git a/models/ctf/banner_pink.tga b/models/ctf/banner_pink.tga
deleted file mode 100644 (file)
index 68e65b6..0000000
Binary files a/models/ctf/banner_pink.tga and /dev/null differ
diff --git a/models/ctf/banner_pink_gloss.tga b/models/ctf/banner_pink_gloss.tga
deleted file mode 100644 (file)
index 77168a8..0000000
Binary files a/models/ctf/banner_pink_gloss.tga and /dev/null differ
diff --git a/models/ctf/banner_red.tga b/models/ctf/banner_red.tga
deleted file mode 100644 (file)
index a64aa36..0000000
Binary files a/models/ctf/banner_red.tga and /dev/null differ
diff --git a/models/ctf/banner_red_gloss.tga b/models/ctf/banner_red_gloss.tga
deleted file mode 100644 (file)
index 77168a8..0000000
Binary files a/models/ctf/banner_red_gloss.tga and /dev/null differ
diff --git a/models/ctf/banner_shirt.tga b/models/ctf/banner_shirt.tga
new file mode 100644 (file)
index 0000000..8c0b709
Binary files /dev/null and b/models/ctf/banner_shirt.tga differ
diff --git a/models/ctf/banner_yellow.tga b/models/ctf/banner_yellow.tga
deleted file mode 100644 (file)
index b398e88..0000000
Binary files a/models/ctf/banner_yellow.tga and /dev/null differ
diff --git a/models/ctf/banner_yellow_gloss.tga b/models/ctf/banner_yellow_gloss.tga
deleted file mode 100644 (file)
index 77168a8..0000000
Binary files a/models/ctf/banner_yellow_gloss.tga and /dev/null differ
diff --git a/models/ctf/flag.tga b/models/ctf/flag.tga
new file mode 100644 (file)
index 0000000..9e67747
Binary files /dev/null and b/models/ctf/flag.tga differ
diff --git a/models/ctf/flag_blue.tga b/models/ctf/flag_blue.tga
deleted file mode 100644 (file)
index 63feddd..0000000
Binary files a/models/ctf/flag_blue.tga and /dev/null differ
diff --git a/models/ctf/flag_blue_gloss.tga b/models/ctf/flag_blue_gloss.tga
deleted file mode 100644 (file)
index 3e92985..0000000
Binary files a/models/ctf/flag_blue_gloss.tga and /dev/null differ
diff --git a/models/ctf/flag_blue_glow.tga b/models/ctf/flag_blue_glow.tga
deleted file mode 100644 (file)
index 8605da1..0000000
Binary files a/models/ctf/flag_blue_glow.tga and /dev/null differ
diff --git a/models/ctf/flag_blue_norm.tga b/models/ctf/flag_blue_norm.tga
deleted file mode 100644 (file)
index 07d5e17..0000000
Binary files a/models/ctf/flag_blue_norm.tga and /dev/null differ
diff --git a/models/ctf/flag_gloss.tga b/models/ctf/flag_gloss.tga
new file mode 100644 (file)
index 0000000..3e92985
Binary files /dev/null and b/models/ctf/flag_gloss.tga differ
diff --git a/models/ctf/flag_glow.tga b/models/ctf/flag_glow.tga
new file mode 100644 (file)
index 0000000..bb5dfae
Binary files /dev/null and b/models/ctf/flag_glow.tga differ
diff --git a/models/ctf/flag_neutral.tga b/models/ctf/flag_neutral.tga
deleted file mode 100644 (file)
index 63feddd..0000000
Binary files a/models/ctf/flag_neutral.tga and /dev/null differ
diff --git a/models/ctf/flag_neutral_gloss.tga b/models/ctf/flag_neutral_gloss.tga
deleted file mode 100644 (file)
index 3e92985..0000000
Binary files a/models/ctf/flag_neutral_gloss.tga and /dev/null differ
diff --git a/models/ctf/flag_neutral_glow.tga b/models/ctf/flag_neutral_glow.tga
deleted file mode 100644 (file)
index 77191ab..0000000
Binary files a/models/ctf/flag_neutral_glow.tga and /dev/null differ
diff --git a/models/ctf/flag_neutral_norm.tga b/models/ctf/flag_neutral_norm.tga
deleted file mode 100644 (file)
index 07d5e17..0000000
Binary files a/models/ctf/flag_neutral_norm.tga and /dev/null differ
diff --git a/models/ctf/flag_norm.tga b/models/ctf/flag_norm.tga
new file mode 100644 (file)
index 0000000..07d5e17
Binary files /dev/null and b/models/ctf/flag_norm.tga differ
diff --git a/models/ctf/flag_pink.tga b/models/ctf/flag_pink.tga
deleted file mode 100644 (file)
index 63feddd..0000000
Binary files a/models/ctf/flag_pink.tga and /dev/null differ
diff --git a/models/ctf/flag_pink_gloss.tga b/models/ctf/flag_pink_gloss.tga
deleted file mode 100644 (file)
index 3e92985..0000000
Binary files a/models/ctf/flag_pink_gloss.tga and /dev/null differ
diff --git a/models/ctf/flag_pink_glow.tga b/models/ctf/flag_pink_glow.tga
deleted file mode 100644 (file)
index ba70c08..0000000
Binary files a/models/ctf/flag_pink_glow.tga and /dev/null differ
diff --git a/models/ctf/flag_pink_norm.tga b/models/ctf/flag_pink_norm.tga
deleted file mode 100644 (file)
index 07d5e17..0000000
Binary files a/models/ctf/flag_pink_norm.tga and /dev/null differ
diff --git a/models/ctf/flag_red.tga b/models/ctf/flag_red.tga
deleted file mode 100644 (file)
index 63feddd..0000000
Binary files a/models/ctf/flag_red.tga and /dev/null differ
diff --git a/models/ctf/flag_red_gloss.tga b/models/ctf/flag_red_gloss.tga
deleted file mode 100644 (file)
index 3e92985..0000000
Binary files a/models/ctf/flag_red_gloss.tga and /dev/null differ
diff --git a/models/ctf/flag_red_glow.tga b/models/ctf/flag_red_glow.tga
deleted file mode 100644 (file)
index dfe90a4..0000000
Binary files a/models/ctf/flag_red_glow.tga and /dev/null differ
diff --git a/models/ctf/flag_red_norm.tga b/models/ctf/flag_red_norm.tga
deleted file mode 100644 (file)
index 07d5e17..0000000
Binary files a/models/ctf/flag_red_norm.tga and /dev/null differ
diff --git a/models/ctf/flag_yellow.tga b/models/ctf/flag_yellow.tga
deleted file mode 100644 (file)
index 63feddd..0000000
Binary files a/models/ctf/flag_yellow.tga and /dev/null differ
diff --git a/models/ctf/flag_yellow_gloss.tga b/models/ctf/flag_yellow_gloss.tga
deleted file mode 100644 (file)
index 3e92985..0000000
Binary files a/models/ctf/flag_yellow_gloss.tga and /dev/null differ
diff --git a/models/ctf/flag_yellow_glow.tga b/models/ctf/flag_yellow_glow.tga
deleted file mode 100644 (file)
index 0af88a9..0000000
Binary files a/models/ctf/flag_yellow_glow.tga and /dev/null differ
diff --git a/models/ctf/flag_yellow_norm.tga b/models/ctf/flag_yellow_norm.tga
deleted file mode 100644 (file)
index 07d5e17..0000000
Binary files a/models/ctf/flag_yellow_norm.tga and /dev/null differ
index 489df22f419006fee5bb41eb28961d01467802d7..86a69eb307ca3c79b84acd45cd33c3b1d02c1411 100644 (file)
@@ -1,3 +1,3 @@
-mesh,models/ctf/flag_red.tga
-mesh2,models/ctf/banner_red.tga
-mesh3,models/ctf/glow_red.tga
\ No newline at end of file
+mesh,models/ctf/flag.tga
+mesh2,models/ctf/banner.tga
+mesh3,models/ctf/glow.tga
\ No newline at end of file
diff --git a/models/ctf/flags.md3_1.skin b/models/ctf/flags.md3_1.skin
deleted file mode 100644 (file)
index 47c5aeb..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-mesh,models/ctf/flag_blue.tga
-mesh2,models/ctf/banner_blue.tga
-mesh3,models/ctf/glow_blue.tga
\ No newline at end of file
diff --git a/models/ctf/flags.md3_2.skin b/models/ctf/flags.md3_2.skin
deleted file mode 100644 (file)
index 44fb67d..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-mesh,models/ctf/flag_yellow.tga
-mesh2,models/ctf/banner_yellow.tga
-mesh3,models/ctf/glow_yellow.tga
\ No newline at end of file
diff --git a/models/ctf/flags.md3_3.skin b/models/ctf/flags.md3_3.skin
deleted file mode 100644 (file)
index 22d77d2..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-mesh,models/ctf/flag_pink.tga
-mesh2,models/ctf/banner_pink.tga
-mesh3,models/ctf/glow_pink.tga
\ No newline at end of file
diff --git a/models/ctf/flags.md3_4.skin b/models/ctf/flags.md3_4.skin
deleted file mode 100644 (file)
index ba35e26..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-mesh,models/ctf/flag_neutral.tga
-mesh2,models/ctf/banner_neutral.tga
-mesh3,models/ctf/glow_neutral.tga
\ No newline at end of file
diff --git a/models/ctf/glow.tga b/models/ctf/glow.tga
new file mode 100644 (file)
index 0000000..7bb6358
Binary files /dev/null and b/models/ctf/glow.tga differ
diff --git a/models/ctf/glow_blue.tga b/models/ctf/glow_blue.tga
deleted file mode 100644 (file)
index 6968dc2..0000000
Binary files a/models/ctf/glow_blue.tga and /dev/null differ
diff --git a/models/ctf/glow_blue_glow.tga b/models/ctf/glow_blue_glow.tga
deleted file mode 100644 (file)
index 6968dc2..0000000
Binary files a/models/ctf/glow_blue_glow.tga and /dev/null differ
diff --git a/models/ctf/glow_glow.tga b/models/ctf/glow_glow.tga
new file mode 100644 (file)
index 0000000..5ffa098
Binary files /dev/null and b/models/ctf/glow_glow.tga differ
diff --git a/models/ctf/glow_neutral.tga b/models/ctf/glow_neutral.tga
deleted file mode 100644 (file)
index 5ffa098..0000000
Binary files a/models/ctf/glow_neutral.tga and /dev/null differ
diff --git a/models/ctf/glow_neutral_glow.tga b/models/ctf/glow_neutral_glow.tga
deleted file mode 100644 (file)
index 5ffa098..0000000
Binary files a/models/ctf/glow_neutral_glow.tga and /dev/null differ
diff --git a/models/ctf/glow_pink.tga b/models/ctf/glow_pink.tga
deleted file mode 100644 (file)
index 6a3bb8c..0000000
Binary files a/models/ctf/glow_pink.tga and /dev/null differ
diff --git a/models/ctf/glow_pink_glow.tga b/models/ctf/glow_pink_glow.tga
deleted file mode 100644 (file)
index 8da95a9..0000000
Binary files a/models/ctf/glow_pink_glow.tga and /dev/null differ
diff --git a/models/ctf/glow_red.tga b/models/ctf/glow_red.tga
deleted file mode 100644 (file)
index 9702b0b..0000000
Binary files a/models/ctf/glow_red.tga and /dev/null differ
diff --git a/models/ctf/glow_red_glow.tga b/models/ctf/glow_red_glow.tga
deleted file mode 100644 (file)
index 9702b0b..0000000
Binary files a/models/ctf/glow_red_glow.tga and /dev/null differ
diff --git a/models/ctf/glow_yellow.tga b/models/ctf/glow_yellow.tga
deleted file mode 100644 (file)
index 8562caa..0000000
Binary files a/models/ctf/glow_yellow.tga and /dev/null differ
diff --git a/models/ctf/glow_yellow_glow.tga b/models/ctf/glow_yellow_glow.tga
deleted file mode 100644 (file)
index 8562caa..0000000
Binary files a/models/ctf/glow_yellow_glow.tga and /dev/null differ
index 85119de08825c52ab9c6692d87d4d9a3eb9fd83b..b84a98ca6d3c9d1f723eec988d7cb89bb2de9918 100644 (file)
@@ -157,6 +157,11 @@ ENTCS_PROP(SOLID, true, sv_solid, solid, ENTCS_SET_NORMAL,
        { WriteByte(chan, ent.sv_solid); },
        { ent.sv_solid = ReadByte(); })
 
+// gamemode specific player survival status (independent of score and frags)
+ENTCS_PROP(SURVIVAL_STATUS, true, survival_status, survival_status, ENTCS_SET_NORMAL,
+       { WriteShort(chan, ent.survival_status); },
+       { ent.survival_status = ReadShort(); })
+
 #ifdef SVQC
 
        int ENTCS_PUBLICMASK = 0, ENTCS_PRIVATEMASK = 0;
index 1ca6b1d94a338381dea5a065aa19e0965d313096..75b1ea00185c748278c7ad1b5b3045bff4550d6f 100644 (file)
@@ -16,5 +16,7 @@
 #include <common/gamemodes/gamemode/nexball/_mod.inc>
 #include <common/gamemodes/gamemode/onslaught/_mod.inc>
 #include <common/gamemodes/gamemode/race/_mod.inc>
+#include <common/gamemodes/gamemode/survival/_mod.inc>
 #include <common/gamemodes/gamemode/tdm/_mod.inc>
+#include <common/gamemodes/gamemode/tka/_mod.inc>
 #include <common/gamemodes/gamemode/tmayhem/_mod.inc>
index ca2cffe8bd1cc0d1ed2f429b774ec86bef3ea9b8..776a88d259104e5c1d5693cf739f5a940219804e 100644 (file)
@@ -16,5 +16,7 @@
 #include <common/gamemodes/gamemode/nexball/_mod.qh>
 #include <common/gamemodes/gamemode/onslaught/_mod.qh>
 #include <common/gamemodes/gamemode/race/_mod.qh>
+#include <common/gamemodes/gamemode/survival/_mod.qh>
 #include <common/gamemodes/gamemode/tdm/_mod.qh>
+#include <common/gamemodes/gamemode/tka/_mod.qh>
 #include <common/gamemodes/gamemode/tmayhem/_mod.qh>
index d92dd0a3b99245f0d38553f429edbd3a248e0254..e16ed913829338802823dddce026e7be7f42d989 100644 (file)
@@ -1300,6 +1300,11 @@ void ctf_FlagSetup(int teamnum, entity flag) // called when spawning a flag enti
        flag.nextthink = time + FLAG_THINKRATE;
        flag.ctf_status = FLAG_BASE;
 
+       // set correct team colors
+       flag.glowmod = Team_ColorRGB(teamnum);
+       flag.colormap = (teamnum) ? (teamnum - 1) * 0x11 : 0x00;
+       flag.colormap |= BIT(10); // RENDER_COLORMAPPED
+
        // crudely force them all to 0
        if(autocvar_g_ctf_score_ignore_fields)
                flag.cnt = flag.score_assist = flag.score_team_capture = flag.score_capture = flag.score_drop = flag.score_pickup = flag.score_return = 0;
index 29025f7cb7d1da6b09183c0f438696c14438dfa0..c50d0d2fc1b964645738a6629f70c96684bd9f8a 100644 (file)
@@ -410,7 +410,7 @@ MUTATOR_HOOKFUNCTION(ka, PlayerUseKey)
        }
 }
 
-MUTATOR_HOOKFUNCTION(ka, Damage_Calculate) // for changing damage and force values that are applied to players in damage.qc
+MUTATOR_HOOKFUNCTION(ka, Damage_Calculate) // for changing damage and force values that are applied to players
 {
        entity frag_attacker = M_ARGV(1, entity);
        entity frag_target = M_ARGV(2, entity);
@@ -426,13 +426,13 @@ MUTATOR_HOOKFUNCTION(ka, Damage_Calculate) // for changing damage and force valu
                        M_ARGV(4, float) *= autocvar_g_keepaway_ballcarrier_selfdamage;
                        M_ARGV(6, vector) *= autocvar_g_keepaway_ballcarrier_selfforce;
                }
-               else // damage done to noncarriers
+               else // damage done to other ballcarriers
                {
                        M_ARGV(4, float) *= autocvar_g_keepaway_ballcarrier_damage;
                        M_ARGV(6, vector) *= autocvar_g_keepaway_ballcarrier_force;
                }
        }
-       else // if the target is a noncarrier
+       else // if the attacker is a noncarrier
        {
                if(frag_target == frag_attacker) // damage done to yourself
                {
diff --git a/qcsrc/common/gamemodes/gamemode/survival/_mod.inc b/qcsrc/common/gamemodes/gamemode/survival/_mod.inc
new file mode 100644 (file)
index 0000000..7121d39
--- /dev/null
@@ -0,0 +1,8 @@
+// generated file; do not modify
+#include <common/gamemodes/gamemode/survival/survival.qc>
+#ifdef CSQC
+    #include <common/gamemodes/gamemode/survival/cl_survival.qc>
+#endif
+#ifdef SVQC
+    #include <common/gamemodes/gamemode/survival/sv_survival.qc>
+#endif
diff --git a/qcsrc/common/gamemodes/gamemode/survival/_mod.qh b/qcsrc/common/gamemodes/gamemode/survival/_mod.qh
new file mode 100644 (file)
index 0000000..875e4d2
--- /dev/null
@@ -0,0 +1,8 @@
+// generated file; do not modify
+#include <common/gamemodes/gamemode/survival/survival.qh>
+#ifdef CSQC
+    #include <common/gamemodes/gamemode/survival/cl_survival.qh>
+#endif
+#ifdef SVQC
+    #include <common/gamemodes/gamemode/survival/sv_survival.qh>
+#endif
diff --git a/qcsrc/common/gamemodes/gamemode/survival/cl_survival.qc b/qcsrc/common/gamemodes/gamemode/survival/cl_survival.qc
new file mode 100644 (file)
index 0000000..02d0907
--- /dev/null
@@ -0,0 +1,59 @@
+#include "cl_survival.qh"
+
+#include <client/draw.qh>
+#include <client/hud/panel/modicons.qh>
+
+void HUD_Mod_Survival(vector pos, vector mySize)
+{
+       mod_active = 1; // survival should always show the mod HUD
+
+       int mystatus = entcs_receiver(player_localnum).survival_status;
+       string player_text = "";
+       vector player_color = '1 1 1';
+       //string player_icon = "";
+       if(mystatus == SURV_STATUS_HUNTER)
+       {
+               player_text = _("Hunter");
+               player_color = '1 0 0';
+               //player_icon = "player_red";
+       }
+       else if(mystatus == SURV_STATUS_PREY)
+       {
+               player_text = _("Survivor");
+               player_color = '0 1 0';
+               //player_icon = "player_neutral";
+       }
+       else
+       {
+               // if the player has no valid status, don't draw anything
+               return;
+       }
+
+       drawstring_aspect(pos, player_text, vec2(mySize.x, mySize.y), player_color, panel_fg_alpha, DRAWFLAG_NORMAL);
+}
+
+REGISTER_MUTATOR(cl_surv, true);
+
+MUTATOR_HOOKFUNCTION(cl_surv, ForcePlayercolors_Skip, CBC_ORDER_LAST)
+{
+       if(!ISGAMETYPE(SURVIVAL))
+               return false;
+
+       entity player = M_ARGV(0, entity);
+       entity e = entcs_receiver(player.entnum - 1);
+       int surv_status = ((e) ? e.survival_status : 0);
+       int mystatus = entcs_receiver(player_localnum).survival_status;
+
+       int plcolor = SURV_COLOR_PREY; // default to survivor
+       if((mystatus == SURV_STATUS_HUNTER || intermission || STAT(GAME_STOPPED)) && surv_status == SURV_STATUS_HUNTER)
+               plcolor = SURV_COLOR_HUNTER;
+
+       player.colormap = 1024 + plcolor;
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(cl_surv, DrawScoreboard_Force)
+{
+       // show the scoreboard when the round ends, so players can see who the hunter was
+       return STAT(GAME_STOPPED);
+}
diff --git a/qcsrc/common/gamemodes/gamemode/survival/cl_survival.qh b/qcsrc/common/gamemodes/gamemode/survival/cl_survival.qh
new file mode 100644 (file)
index 0000000..057120a
--- /dev/null
@@ -0,0 +1,3 @@
+#pragma once
+
+void HUD_Mod_Survival(vector pos, vector mySize);
diff --git a/qcsrc/common/gamemodes/gamemode/survival/survival.qc b/qcsrc/common/gamemodes/gamemode/survival/survival.qc
new file mode 100644 (file)
index 0000000..1f2d144
--- /dev/null
@@ -0,0 +1 @@
+#include "survival.qh"
diff --git a/qcsrc/common/gamemodes/gamemode/survival/survival.qh b/qcsrc/common/gamemodes/gamemode/survival/survival.qh
new file mode 100644 (file)
index 0000000..baddfb5
--- /dev/null
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <common/gamemodes/gamemode/lms/lms.qh>
+#include <common/mapinfo.qh>
+
+#ifdef CSQC
+void HUD_Mod_Survival(vector pos, vector mySize);
+#endif
+CLASS(Survival, Gametype)
+    INIT(Survival)
+    {
+        this.gametype_init(this, _("Survival"), "surv", "g_survival", GAMETYPE_FLAG_USEPOINTS, "", "timelimit=20 pointlimit=12", _("Identify and eliminate all the hunters before all your allies are gone"));
+    }
+    METHOD(Survival, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
+    {
+        return true;
+    }
+    METHOD(Survival, m_isForcedSupported, bool(Gametype this))
+    {
+        if(!cvar("g_survival_not_lms_maps"))
+        {
+            // if this is unset, all LMS maps support Survival too
+            if(!(MapInfo_Map_supportedGametypes & this.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_LMS.m_flags))
+                return true; // TODO: references another gametype (alternatively, we could check which gamemodes are always enabled and append this if any are supported)
+        }
+        return false;
+    }
+#ifdef CSQC
+    ATTRIB(Survival, m_modicons, void(vector pos, vector mySize), HUD_Mod_Survival);
+#endif
+ENDCLASS(Survival)
+REGISTER_GAMETYPE(SURVIVAL, NEW(Survival));
+
+#ifdef GAMEQC
+// shared state signalling the player's survival status
+.int survival_status;
+const int SURV_STATUS_PREY = 1;
+const int SURV_STATUS_HUNTER = 2;
+
+// hardcoded player colors for survival
+const int SURV_COLOR_PREY = 51; // green
+const int SURV_COLOR_HUNTER = 68; // red
+#endif
diff --git a/qcsrc/common/gamemodes/gamemode/survival/sv_survival.qc b/qcsrc/common/gamemodes/gamemode/survival/sv_survival.qc
new file mode 100644 (file)
index 0000000..6414686
--- /dev/null
@@ -0,0 +1,431 @@
+#include "sv_survival.qh"
+
+float autocvar_g_survival_hunter_count = 0.25;
+float autocvar_g_survival_round_timelimit = 120;
+float autocvar_g_survival_warmup = 10;
+bool autocvar_g_survival_punish_teamkill = true;
+bool autocvar_g_survival_reward_survival = true;
+
+void nades_Clear(entity player);
+
+void Surv_UpdateScores(bool timed_out)
+{
+       // give players their hard-earned kills now that the round is over
+       FOREACH_CLIENT(true,
+       {
+               it.totalfrags += it.survival_validkills;
+               if(it.survival_validkills)
+                       GameRules_scoring_add(it, SCORE, it.survival_validkills);
+               it.survival_validkills = 0;
+               // player survived the round
+               if(IS_PLAYER(it) && !IS_DEAD(it))
+               {
+                       if(autocvar_g_survival_reward_survival && timed_out && it.survival_status == SURV_STATUS_PREY)
+                               GameRules_scoring_add(it, SCORE, 1); // reward survivors who make it to the end of the round time limit
+                       if(it.survival_status == SURV_STATUS_PREY)
+                               GameRules_scoring_add(it, SURV_SURVIVALS, 1);
+                       else if(it.survival_status == SURV_STATUS_HUNTER)
+                               GameRules_scoring_add(it, SURV_HUNTS, 1);
+               }
+       });
+}
+
+float Surv_CheckWinner()
+{
+       if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
+       {
+               // if the match times out, survivors win too!
+               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_SURVIVAL_SURVIVOR_WIN);
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SURVIVAL_SURVIVOR_WIN);
+               FOREACH_CLIENT(true,
+               {
+                       if(IS_PLAYER(it))
+                               nades_Clear(it);
+               });
+
+               Surv_UpdateScores(true);
+
+               allowed_to_spawn = false;
+               game_stopped = true;
+               round_handler_Init(5, autocvar_g_survival_warmup, autocvar_g_survival_round_timelimit);
+               return 1;
+       }
+
+       int survivor_count = 0, hunter_count = 0;
+       FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
+       {
+               if(it.survival_status == SURV_STATUS_PREY)
+                       survivor_count++;
+               else if(it.survival_status == SURV_STATUS_HUNTER)
+                       hunter_count++;
+       });
+       if(survivor_count > 0 && hunter_count > 0)
+       {
+               return 0;
+       }
+
+       if(hunter_count > 0) // hunters win
+       {
+               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_SURVIVAL_HUNTER_WIN);
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SURVIVAL_HUNTER_WIN);
+       }
+       else if(survivor_count > 0) // survivors win
+       {
+               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_SURVIVAL_SURVIVOR_WIN);
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SURVIVAL_SURVIVOR_WIN);
+       }
+       else
+       {
+               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED);
+               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED);
+       }
+
+       Surv_UpdateScores(false);
+
+       allowed_to_spawn = false;
+       game_stopped = true;
+       round_handler_Init(5, autocvar_g_survival_warmup, autocvar_g_survival_round_timelimit);
+
+       FOREACH_CLIENT(true,
+       {
+               if(IS_PLAYER(it))
+                       nades_Clear(it);
+       });
+
+       return 1;
+}
+
+void Surv_RoundStart()
+{
+       allowed_to_spawn = boolean(warmup_stage);
+       int playercount = 0;
+       FOREACH_CLIENT(true,
+       {
+               if(IS_PLAYER(it) && !IS_DEAD(it))
+               {
+                       ++playercount;
+                       it.survival_status = SURV_STATUS_PREY;
+               }
+               else
+                       it.survival_status = 0; // this is mostly a safety check; if a client manages to somehow maintain a survival status, clear it before the round starts!
+               it.survival_validkills = 0;
+       });
+       int hunter_count = bound(1, ((autocvar_g_survival_hunter_count >= 1) ? autocvar_g_survival_hunter_count : floor(playercount * autocvar_g_survival_hunter_count)), playercount - 1); // 20%, but ensure at least 1 and less than total
+       int total_hunters = 0;
+       FOREACH_CLIENT_RANDOM(IS_PLAYER(it) && !IS_DEAD(it),
+       {
+               if(total_hunters >= hunter_count)
+                       break;
+               total_hunters++;
+               it.survival_status = SURV_STATUS_HUNTER;
+       });
+
+       FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
+       {
+               if(it.survival_status == SURV_STATUS_PREY)
+                       Send_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CENTER_SURVIVAL_SURVIVOR);
+               else if(it.survival_status == SURV_STATUS_HUNTER)
+                       Send_Notification(NOTIF_ONE_ONLY, it, MSG_CENTER, CENTER_SURVIVAL_HUNTER);
+       });
+}
+
+bool Surv_CheckPlayers()
+{
+       static int prev_missing_players;
+       allowed_to_spawn = true;
+       int playercount = 0;
+       FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
+       {
+               ++playercount;
+       });
+       if (playercount >= 2)
+       {
+               if(prev_missing_players > 0)
+                       Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_PLAYERS);
+               prev_missing_players = -1;
+               return true;
+       }
+       if(playercount == 0)
+       {
+               if(prev_missing_players > 0)
+                       Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_PLAYERS);
+               prev_missing_players = -1;
+               return false;
+       }
+       // if we get here, only 1 player is missing
+       if(prev_missing_players != 1)
+       {
+               Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_PLAYERS, 1);
+               prev_missing_players = 1;
+       }
+       return false;
+}
+
+bool surv_isEliminated(entity e)
+{
+       if(INGAME_JOINED(e) && (IS_DEAD(e) || e.frags == FRAGS_PLAYER_OUT_OF_GAME))
+               return true;
+       if(INGAME_JOINING(e))
+               return true;
+       return false;
+}
+
+void surv_Initialize() // run at the start of a match, initiates game mode
+{
+       GameRules_scoring(0, SFL_SORT_PRIO_PRIMARY, 0, {
+               field(SP_SURV_SURVIVALS, "survivals", 0);
+               field(SP_SURV_HUNTS, "hunts", SFL_SORT_PRIO_SECONDARY);
+       });
+
+       allowed_to_spawn = true;
+       round_handler_Spawn(Surv_CheckPlayers, Surv_CheckWinner, Surv_RoundStart);
+       round_handler_Init(5, autocvar_g_survival_warmup, autocvar_g_survival_round_timelimit);
+       EliminatedPlayers_Init(surv_isEliminated);
+}
+
+
+// ==============
+// Hook Functions
+// ==============
+
+MUTATOR_HOOKFUNCTION(surv, ClientObituary)
+{
+       // in survival, announcing a frag would tell everyone who the hunter is
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+       if(IS_PLAYER(frag_attacker) && frag_attacker != frag_target)
+       {
+               float frag_deathtype = M_ARGV(3, float);
+               entity wep_ent = M_ARGV(4, entity);
+               // "team" kill, a point is awarded to the player by default so we must take it away plus an extra one
+               // unless the player is going to be punished for suicide, in which case just remove one
+               if(frag_attacker.survival_status == frag_target.survival_status)
+                       GiveFrags(frag_attacker, frag_target, ((autocvar_g_survival_punish_teamkill) ? -1 : -2), frag_deathtype, wep_ent.weaponentity_fld);
+       }
+
+       if(frag_attacker.survival_status == SURV_STATUS_HUNTER)
+               M_ARGV(5, bool) = true; // anonymous attacker
+}
+
+MUTATOR_HOOKFUNCTION(surv, PlayerPreThink)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(IS_PLAYER(player) || INGAME_JOINED(player))
+       {
+               // update the scoreboard colour display to out the real killer at the end of the round
+               // running this every frame to avoid cheats
+               int plcolor = SURV_COLOR_PREY;
+               if(player.survival_status == SURV_STATUS_HUNTER && game_stopped)
+                       plcolor = SURV_COLOR_HUNTER;
+               setcolor(player, plcolor);
+       }
+}
+
+MUTATOR_HOOKFUNCTION(surv, PlayerSpawn)
+{
+       entity player = M_ARGV(0, entity);
+
+       player.survival_status = 0;
+       player.survival_validkills = 0;
+       INGAME_STATUS_SET(player, INGAME_STATUS_JOINED);
+       if (!warmup_stage)
+               eliminatedPlayers.SendFlags |= 1;
+}
+
+MUTATOR_HOOKFUNCTION(surv, ForbidSpawn)
+{
+       entity player = M_ARGV(0, entity);
+
+       // spectators / observers that weren't playing can join; they are
+       // immediately forced to observe in the PutClientInServer hook
+       // this way they are put in a team and can play in the next round
+       if (!allowed_to_spawn && INGAME(player))
+               return true;
+       return false;
+}
+
+MUTATOR_HOOKFUNCTION(surv, PutClientInServer)
+{
+       entity player = M_ARGV(0, entity);
+
+       if (!allowed_to_spawn && IS_PLAYER(player)) // this is true even when player is trying to join
+       {
+               TRANSMUTE(Observer, player);
+               if (CS(player).jointime != time && !INGAME(player)) // not when connecting
+               {
+                       INGAME_STATUS_SET(player, INGAME_STATUS_JOINING);
+                       Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_CA_JOIN_LATE);
+               }
+       }
+}
+
+MUTATOR_HOOKFUNCTION(surv, reset_map_players)
+{
+       FOREACH_CLIENT(true, {
+               CS(it).killcount = 0;
+               it.survival_status = 0;
+               if (INGAME(it) || IS_BOT_CLIENT(it))
+               {
+                       TRANSMUTE(Player, it);
+                       INGAME_STATUS_SET(it, INGAME_STATUS_JOINED);
+                       PutClientInServer(it);
+               }
+       });
+       bot_relinkplayerlist();
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(surv, reset_map_global)
+{
+       allowed_to_spawn = true;
+       return true;
+}
+
+entity surv_LastPlayerForTeam(entity this)
+{
+       entity last_pl = NULL;
+       FOREACH_CLIENT(IS_PLAYER(it) && it != this, {
+               if (!IS_DEAD(it) && this.survival_status == it.survival_status)
+               {
+                       if (!last_pl)
+                               last_pl = it;
+                       else
+                               return NULL;
+               }
+       });
+       return last_pl;
+}
+
+void surv_LastPlayerForTeam_Notify(entity this)
+{
+       if (!warmup_stage && round_handler_IsActive() && round_handler_IsRoundStarted())
+       {
+               entity pl = surv_LastPlayerForTeam(this);
+               if (pl)
+                       Send_Notification(NOTIF_ONE_ONLY, pl, MSG_CENTER, CENTER_ALONE);
+       }
+}
+
+MUTATOR_HOOKFUNCTION(surv, PlayerDies)
+{
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+       float frag_deathtype = M_ARGV(3, float);
+
+       surv_LastPlayerForTeam_Notify(frag_target);
+       if (!allowed_to_spawn)
+       {
+               frag_target.respawn_flags = RESPAWN_SILENT;
+               // prevent unwanted sudden rejoin as spectator and movement of spectator camera
+               frag_target.respawn_time = time + 2;
+       }
+       frag_target.respawn_flags |= RESPAWN_FORCE;
+       if (!warmup_stage)
+       {
+               eliminatedPlayers.SendFlags |= 1;
+               if (IS_BOT_CLIENT(frag_target))
+                       bot_clearqueue(frag_target);
+       }
+
+       // killed an ally! punishment is death
+       if(autocvar_g_survival_punish_teamkill && frag_attacker != frag_target && IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target) && frag_attacker.survival_status == frag_target.survival_status && !ITEM_DAMAGE_NEEDKILL(frag_deathtype))
+       if(!warmup_stage && round_handler_IsActive() && round_handler_IsRoundStarted()) // don't autokill if the round hasn't
+               Damage(frag_attacker, frag_attacker, frag_attacker, 100000, DEATH_MIRRORDAMAGE.m_id, DMG_NOWEP, frag_attacker.origin, '0 0 0');
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(surv, ClientDisconnect)
+{
+       entity player = M_ARGV(0, entity);
+
+       if (IS_PLAYER(player) && !IS_DEAD(player))
+               surv_LastPlayerForTeam_Notify(player);
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(surv, MakePlayerObserver)
+{
+       entity player = M_ARGV(0, entity);
+       bool is_forced = M_ARGV(1, bool);
+       if (is_forced && INGAME(player))
+               INGAME_STATUS_CLEAR(player);
+
+       if (IS_PLAYER(player) && !IS_DEAD(player))
+               surv_LastPlayerForTeam_Notify(player);
+       if (player.killindicator_teamchange == -2) // player wants to spectate
+               INGAME_STATUS_CLEAR(player);
+       if (INGAME(player))
+               player.frags = FRAGS_PLAYER_OUT_OF_GAME;
+       if (!warmup_stage)
+               eliminatedPlayers.SendFlags |= 1;
+       if (!INGAME(player))
+       {
+               player.survival_validkills = 0;
+               player.survival_status = 0;
+               return false;  // allow team reset
+       }
+       return true;  // prevent team reset
+}
+
+MUTATOR_HOOKFUNCTION(surv, Scores_CountFragsRemaining)
+{
+       // announce remaining frags?
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(surv, GiveFragsForKill, CBC_ORDER_FIRST)
+{
+       entity frag_attacker = M_ARGV(0, entity);
+       if(!warmup_stage && round_handler_IsActive() && round_handler_IsRoundStarted())
+               frag_attacker.survival_validkills += M_ARGV(2, float);
+       M_ARGV(2, float) = 0; // score will be given to the winner when the round ends
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(surv, AddPlayerScore)
+{
+       entity scorefield = M_ARGV(0, entity);
+       if(scorefield == SP_KILLS || scorefield == SP_DEATHS || scorefield == SP_SUICIDES || scorefield == SP_DMG || scorefield == SP_DMGTAKEN)
+               M_ARGV(1, float) = 0; // don't report that the player has killed or been killed, that would out them as a hunter!
+}
+
+MUTATOR_HOOKFUNCTION(surv, CalculateRespawnTime)
+{
+       // no respawn calculations needed, player is forced to spectate anyway
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(surv, Bot_FixCount, CBC_ORDER_EXCLUSIVE)
+{
+       FOREACH_CLIENT(IS_REAL_CLIENT(it), {
+               if (IS_PLAYER(it) || INGAME_JOINED(it))
+                       ++M_ARGV(0, int);
+               ++M_ARGV(1, int);
+       });
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(surv, ClientCommand_Spectate)
+{
+       entity player = M_ARGV(0, entity);
+
+       if (INGAME(player))
+       {
+               // they're going to spec, we can do other checks
+               if (autocvar_sv_spectate && (IS_SPEC(player) || IS_OBSERVER(player)))
+                       Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_CA_LEAVE);
+               return MUT_SPECCMD_FORCE;
+       }
+
+       return MUT_SPECCMD_CONTINUE;
+}
+
+MUTATOR_HOOKFUNCTION(surv, BotShouldAttack)
+{
+       entity bot = M_ARGV(0, entity);
+       entity targ = M_ARGV(1, entity);
+
+       if(targ.survival_status == bot.survival_status)
+               return true;
+}
diff --git a/qcsrc/common/gamemodes/gamemode/survival/sv_survival.qh b/qcsrc/common/gamemodes/gamemode/survival/sv_survival.qh
new file mode 100644 (file)
index 0000000..129410a
--- /dev/null
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <common/mutators/base.qh>
+#include <common/scores.qh>
+void surv_Initialize();
+
+REGISTER_MUTATOR(surv, false)
+{
+    MUTATOR_STATIC();
+       MUTATOR_ONADD
+       {
+               surv_Initialize();
+       }
+       return false;
+}
+
+.int survival_validkills; // store the player's valid kills to be given at the end of the match (avoid exposing their score until then)
diff --git a/qcsrc/common/gamemodes/gamemode/tka/_mod.inc b/qcsrc/common/gamemodes/gamemode/tka/_mod.inc
new file mode 100644 (file)
index 0000000..6a33efd
--- /dev/null
@@ -0,0 +1,8 @@
+// generated file; do not modify
+#include <common/gamemodes/gamemode/tka/tka.qc>
+#ifdef CSQC
+    #include <common/gamemodes/gamemode/tka/cl_tka.qc>
+#endif
+#ifdef SVQC
+    #include <common/gamemodes/gamemode/tka/sv_tka.qc>
+#endif
diff --git a/qcsrc/common/gamemodes/gamemode/tka/_mod.qh b/qcsrc/common/gamemodes/gamemode/tka/_mod.qh
new file mode 100644 (file)
index 0000000..e35dee6
--- /dev/null
@@ -0,0 +1,8 @@
+// generated file; do not modify
+#include <common/gamemodes/gamemode/tka/tka.qh>
+#ifdef CSQC
+    #include <common/gamemodes/gamemode/tka/cl_tka.qh>
+#endif
+#ifdef SVQC
+    #include <common/gamemodes/gamemode/tka/sv_tka.qh>
+#endif
diff --git a/qcsrc/common/gamemodes/gamemode/tka/cl_tka.qc b/qcsrc/common/gamemodes/gamemode/tka/cl_tka.qc
new file mode 100644 (file)
index 0000000..e3be464
--- /dev/null
@@ -0,0 +1,54 @@
+#include "cl_tka.qh"
+
+#include <client/draw.qh>
+#include <client/hud/panel/modicons.qh>
+
+// Keepaway HUD mod icon
+int tkaball_prevstatus; // last remembered status
+float tkaball_statuschange_time; // time when the status changed
+
+// we don't need to reset for team keepaway since it immediately
+// autocorrects prevstatus as to if the player has the ball or not
+
+void HUD_Mod_TeamKeepaway(vector pos, vector mySize)
+{
+       mod_active = 1; // team keepaway should always show the mod HUD
+
+       float tkaball_alpha = blink(0.85, 0.15, 5);
+
+       int stat_items = STAT(TKA_BALLSTATUS);
+       int tkaball = (stat_items & TKA_BALL_CARRYING);
+
+       if(tkaball != tkaball_prevstatus)
+       {
+               tkaball_statuschange_time = time;
+               tkaball_prevstatus = tkaball;
+       }
+
+       vector tkaball_pos, tkaball_size;
+
+       if(mySize.x > mySize.y) {
+               tkaball_pos = pos + eX * 0.25 * mySize.x;
+               tkaball_size = vec2(0.5 * mySize.x, mySize.y);
+       } else {
+               tkaball_pos = pos + eY * 0.25 * mySize.y;
+               tkaball_size = vec2(mySize.x, 0.5 * mySize.y);
+       }
+
+       float tkaball_statuschange_elapsedtime = time - tkaball_statuschange_time;
+       float f = bound(0, tkaball_statuschange_elapsedtime*2, 1);
+
+       if(tkaball_prevstatus && f < 1)
+               drawpic_aspect_skin_expanding(tkaball_pos, "keepawayball_carrying", tkaball_size, '1 1 1', panel_fg_alpha * tkaball_alpha, DRAWFLAG_NORMAL, f);
+
+       if(stat_items & TKA_BALL_CARRYING) // TODO: unique team based icon while carrying
+               drawpic_aspect_skin(pos, "keepawayball_carrying", vec2(mySize.x, mySize.y), '1 1 1', panel_fg_alpha * tkaball_alpha * f, DRAWFLAG_NORMAL);
+       else if(stat_items & TKA_BALL_TAKEN_RED)
+               drawpic_aspect_skin(pos, "tka_taken_red", vec2(mySize.x, mySize.y), '1 1 1', panel_fg_alpha * tkaball_alpha * f, DRAWFLAG_NORMAL);
+       else if(stat_items & TKA_BALL_TAKEN_BLUE)
+               drawpic_aspect_skin(pos, "tka_taken_blue", vec2(mySize.x, mySize.y), '1 1 1', panel_fg_alpha * tkaball_alpha * f, DRAWFLAG_NORMAL);
+       else if(stat_items & TKA_BALL_TAKEN_YELLOW)
+               drawpic_aspect_skin(pos, "tka_taken_yellow", vec2(mySize.x, mySize.y), '1 1 1', panel_fg_alpha * tkaball_alpha * f, DRAWFLAG_NORMAL);
+       else if(stat_items & TKA_BALL_TAKEN_PINK)
+               drawpic_aspect_skin(pos, "tka_taken_pink", vec2(mySize.x, mySize.y), '1 1 1', panel_fg_alpha * tkaball_alpha * f, DRAWFLAG_NORMAL);
+}
diff --git a/qcsrc/common/gamemodes/gamemode/tka/cl_tka.qh b/qcsrc/common/gamemodes/gamemode/tka/cl_tka.qh
new file mode 100644 (file)
index 0000000..d062456
--- /dev/null
@@ -0,0 +1,3 @@
+#pragma once
+
+void HUD_Mod_TeamKeepaway(vector pos, vector mySize);
diff --git a/qcsrc/common/gamemodes/gamemode/tka/sv_tka.qc b/qcsrc/common/gamemodes/gamemode/tka/sv_tka.qc
new file mode 100644 (file)
index 0000000..0be1e46
--- /dev/null
@@ -0,0 +1,566 @@
+#include "sv_tka.qh"
+
+#include <common/effects/all.qh>
+
+.entity ballcarried;
+
+int autocvar_g_tka_ballcarrier_effects;
+float autocvar_g_tka_ballcarrier_damage;
+float autocvar_g_tka_ballcarrier_force;
+float autocvar_g_tka_ballcarrier_highspeed;
+float autocvar_g_tka_ballcarrier_selfdamage;
+float autocvar_g_tka_ballcarrier_selfforce;
+float autocvar_g_tka_noncarrier_damage;
+float autocvar_g_tka_noncarrier_force;
+float autocvar_g_tka_noncarrier_selfdamage;
+float autocvar_g_tka_noncarrier_selfforce;
+bool autocvar_g_tka_noncarrier_warn;
+int autocvar_g_tka_score_bckill;
+int autocvar_g_tka_score_killac;
+bool autocvar_g_tka_score_team;
+int autocvar_g_tka_score_timepoints;
+float autocvar_g_tka_score_timeinterval;
+float autocvar_g_tkaball_damageforcescale;
+int autocvar_g_tkaball_effects;
+float autocvar_g_tkaball_respawntime;
+int autocvar_g_tkaball_trail_color;
+
+bool tka_ballcarrier_waypointsprite_visible_for_player(entity this, entity player, entity view) // runs on waypoints which are attached to ballcarriers, updates once per frame
+{
+       if(view.ballcarried)
+               if(IS_SPEC(player))
+                       return false; // we don't want spectators of the ballcarrier to see the attached waypoint on the top of their screen
+
+       // TODO: Make the ballcarrier lack a waypointsprite whenever they have the invisibility powerup
+
+       return true;
+}
+
+void tka_EventLog(string mode, entity actor) // use an alias for easy changing and quick editing later
+{
+       if(autocvar_sv_eventlog)
+               GameLogEcho(strcat(":tka:", mode, ((actor != NULL) ? (strcat(":", ftos(actor.team), ":", ftos(actor.playerid))) : "")));
+}
+
+void tka_TouchEvent(entity this, entity toucher);
+void tka_RespawnBall(entity this) // runs whenever the ball needs to be relocated
+{
+       if(game_stopped) return;
+       vector oldballorigin = this.origin;
+
+       if(!MoveToRandomMapLocation(this, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256))
+               setorigin(this, SelectSpawnPoint(this, true).origin);
+
+       set_movetype(this, MOVETYPE_BOUNCE);
+       this.velocity = '0 0 200';
+       this.angles = '0 0 0';
+       this.effects = autocvar_g_tkaball_effects;
+       settouch(this, tka_TouchEvent);
+       setthink(this, tka_RespawnBall);
+       this.nextthink = time + autocvar_g_tkaball_respawntime;
+       navigation_dynamicgoal_set(this, NULL);
+
+       Send_Effect(EFFECT_ELECTRO_COMBO, oldballorigin, '0 0 0', 1);
+       Send_Effect(EFFECT_ELECTRO_COMBO, this.origin, '0 0 0', 1);
+
+       WaypointSprite_Spawn(WP_KaBall, 0, 0, this, '0 0 64', NULL, this.team, this, waypointsprite_attachedforcarrier, false, RADARICON_FLAGCARRIER);
+       WaypointSprite_Ping(this.waypointsprite_attachedforcarrier);
+
+       sound(this, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
+}
+
+void tka_TimeScoring(entity this)
+{
+       if(this.owner.ballcarried)
+       { // add points for holding the ball after a certain amount of time
+               if(autocvar_g_tka_score_timepoints)
+                       GameRules_scoring_add_team(this.owner, SCORE, autocvar_g_tka_score_timepoints);
+
+               GameRules_scoring_add(this.owner, TKA_BCTIME, (autocvar_g_tka_score_timeinterval / 1)); // interval is divided by 1 so that time always shows "seconds"
+               this.nextthink = time + autocvar_g_tka_score_timeinterval;
+       }
+}
+
+void tka_TouchEvent(entity this, entity toucher) // runs any time that the ball comes in contact with something
+{
+       if (!this || game_stopped)
+               return;
+
+       if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
+       { // The ball fell off the map, respawn it since players can't get to it
+               tka_RespawnBall(this);
+               return;
+       }
+       if(toucher.ballcarried) { return; }
+       if(IS_DEAD(toucher)) { return; }
+       if(STAT(FROZEN, toucher)) { return; }
+       if (!IS_PLAYER(toucher))
+       {  // The ball just touched an object, most likely the world
+               Send_Effect(EFFECT_BALL_SPARKS, this.origin, '0 0 0', 1);
+               sound(this, CH_TRIGGER, SND_KA_TOUCH, VOL_BASE, ATTEN_NORM);
+               return;
+       }
+       else if(this.wait > time) { return; }
+
+       // attach the ball to the player
+       this.owner = toucher;
+       toucher.ballcarried = this;
+       GameRules_scoring_vip(toucher, true);
+       setattachment(this, toucher, "");
+       setorigin(this, '0 0 0');
+
+       // make the ball invisible/unable to do anything/set up time scoring
+       this.velocity = '0 0 0';
+       set_movetype(this, MOVETYPE_NONE);
+       this.effects |= EF_NODRAW;
+       settouch(this, func_null);
+       setthink(this, tka_TimeScoring);
+       this.nextthink = time + autocvar_g_tka_score_timeinterval;
+       this.takedamage = DAMAGE_NO;
+       navigation_dynamicgoal_unset(this);
+
+       // apply effects to player
+       toucher.glow_color = autocvar_g_tkaball_trail_color;
+       toucher.glow_trail = true;
+       toucher.effects |= autocvar_g_tka_ballcarrier_effects;
+
+       // messages and sounds
+       tka_EventLog("pickup", toucher);
+       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_KEEPAWAY_PICKUP, toucher.netname);
+       Send_Notification(NOTIF_ALL_EXCEPT, toucher, MSG_CENTER, CENTER_KEEPAWAY_PICKUP, toucher.netname);
+       Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_KEEPAWAY_PICKUP_SELF);
+       sound(this.owner, CH_TRIGGER, SND_KA_PICKEDUP, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
+
+       // scoring
+       GameRules_scoring_add(toucher, TKA_PICKUPS, 1);
+
+       // waypoints
+       WaypointSprite_AttachCarrier(WP_Null, toucher, RADARICON_FLAGCARRIER);
+       toucher.waypointsprite_attachedforcarrier.colormod = colormapPaletteColor(toucher.team - 1, 0);
+       toucher.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = tka_ballcarrier_waypointsprite_visible_for_player;
+       WaypointSprite_UpdateRule(toucher.waypointsprite_attachedforcarrier, toucher.team, SPRITERULE_TEAMPLAY);
+       if(toucher.team == NUM_TEAM_1)
+               WaypointSprite_UpdateSprites(toucher.waypointsprite_attachedforcarrier, WP_TkaBallCarrierRed, WP_KaBallCarrier, WP_TkaBallCarrierRed);
+       else if(toucher.team == NUM_TEAM_2)
+               WaypointSprite_UpdateSprites(toucher.waypointsprite_attachedforcarrier, WP_TkaBallCarrierBlue, WP_KaBallCarrier, WP_TkaBallCarrierBlue);
+       else if(toucher.team == NUM_TEAM_3)
+               WaypointSprite_UpdateSprites(toucher.waypointsprite_attachedforcarrier, WP_TkaBallCarrierYellow, WP_KaBallCarrier, WP_TkaBallCarrierYellow);
+       else if(toucher.team == NUM_TEAM_4)
+               WaypointSprite_UpdateSprites(toucher.waypointsprite_attachedforcarrier, WP_TkaBallCarrierPink, WP_KaBallCarrier, WP_TkaBallCarrierPink);
+       WaypointSprite_Ping(toucher.waypointsprite_attachedforcarrier);
+       WaypointSprite_Kill(this.waypointsprite_attachedforcarrier);
+}
+
+void tka_PlayerReset(entity player)
+{
+       player.ballcarried = NULL;
+       GameRules_scoring_vip(player, false);
+       WaypointSprite_Kill(player.waypointsprite_attachedforcarrier);
+
+       // reset the player effects
+       player.glow_trail = false;
+       player.effects &= ~autocvar_g_tka_ballcarrier_effects;
+}
+
+void tka_DropEvent(entity player) // runs any time that a player is supposed to lose the ball
+{
+       entity ball;
+       ball = player.ballcarried;
+
+       if(!ball) { return; }
+
+       // reset the ball
+       setattachment(ball, NULL, "");
+       set_movetype(ball, MOVETYPE_BOUNCE);
+       ball.wait = time + 1;
+       settouch(ball, tka_TouchEvent);
+       setthink(ball, tka_RespawnBall);
+       ball.nextthink = time + autocvar_g_tkaball_respawntime;
+       ball.takedamage = DAMAGE_YES;
+       ball.effects &= ~EF_NODRAW;
+       setorigin(ball, player.origin + '0 0 10');
+       ball.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
+       ball.owner = NULL;
+       navigation_dynamicgoal_set(ball, player);
+
+       // messages and sounds
+       tka_EventLog("dropped", player);
+       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_KEEPAWAY_DROPPED, player.netname);
+       Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_KEEPAWAY_DROPPED, player.netname);
+       sound(NULL, CH_TRIGGER, SND_KA_DROPPED, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
+
+       // waypoints
+       WaypointSprite_Spawn(WP_KaBall, 0, 0, ball, '0 0 64', NULL, ball.team, ball, waypointsprite_attachedforcarrier, false, RADARICON_FLAGCARRIER);
+       WaypointSprite_UpdateRule(ball.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
+       WaypointSprite_Ping(ball.waypointsprite_attachedforcarrier);
+
+       tka_PlayerReset(player);
+}
+
+.bool pushable;
+
+MODEL(TKA_BALL, "models/orbs/orbblue.md3");
+
+void tka_RemoveBalls()
+{
+       IL_EACH(g_tkaballs, true,
+       {
+               if (it.owner) // it was attached
+                       tka_PlayerReset(it.owner);
+               else
+                       WaypointSprite_DetachCarrier(it);
+               delete(it);
+       });
+}
+
+void tka_SpawnBalls()
+{
+       int i = 0;
+       do // never allow less than 1 ball to spawn
+       {
+               entity e = new(keepawayball);
+               setmodel(e, MDL_TKA_BALL);
+               setsize(e, '-16 -16 -20', '16 16 20'); // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off
+               e.damageforcescale = autocvar_g_tkaball_damageforcescale;
+               e.takedamage = DAMAGE_YES;
+               e.solid = SOLID_TRIGGER;
+               set_movetype(e, MOVETYPE_BOUNCE);
+               e.glow_color = autocvar_g_tkaball_trail_color;
+               e.glow_trail = true;
+               e.flags = FL_ITEM;
+               IL_PUSH(g_items, e);
+               e.pushable = true;
+               settouch(e, tka_TouchEvent);
+               e.owner = NULL;
+               IL_PUSH(g_tkaballs, e);
+               navigation_dynamicgoal_init(e, false);
+
+               tka_RespawnBall(e);
+
+               ++i;
+       }
+       while (i < TKA_BALL_COUNT);
+}
+
+void tka_Handler_CheckBall(entity this)
+{
+       if(time < game_starttime)
+       {
+               if (!IL_EMPTY(g_tkaballs))
+                       tka_RemoveBalls();
+       }
+       else
+       {
+               if (IL_EMPTY(g_tkaballs))
+                       tka_SpawnBalls();
+       }
+
+       this.nextthink = time;
+}
+
+
+// ================
+// Bot player logic
+// ================
+
+void havocbot_goalrating_tkaball(entity this, float ratingscale, vector org)
+{
+       entity ball = NULL, ball_carried = NULL;
+
+       // stops at last ball, prefers ball without carrier
+       IL_EACH(g_tkaballs, it.owner != this && DIFF_TEAM(ball.owner, this),
+       {
+               if(it.owner)
+                       ball_carried = it.owner;
+               else
+                       ball = it;
+       });
+
+       if (ball)
+               navigation_routerating(this, ball, ratingscale, 2000);
+       else if(ball_carried)
+               navigation_routerating(this, ball_carried, ratingscale, 2000);
+}
+
+void havocbot_role_tka_carrier(entity this)
+{
+       if (IS_DEAD(this))
+               return;
+
+       if (navigation_goalrating_timeout(this))
+       {
+               navigation_goalrating_start(this);
+               havocbot_goalrating_items(this, 10000, this.origin, 10000);
+               havocbot_goalrating_enemyplayers(this, 10000, this.origin, 10000);
+               havocbot_goalrating_waypoints(this, 1, this.origin, 3000);
+               navigation_goalrating_end(this);
+
+               navigation_goalrating_timeout_set(this);
+       }
+
+       if (!this.ballcarried)
+       {
+               this.havocbot_role = havocbot_role_tka_collector;
+               navigation_goalrating_timeout_expire(this, 2);
+       }
+}
+
+void havocbot_role_tka_collector(entity this)
+{
+       if (IS_DEAD(this))
+               return;
+
+       if (navigation_goalrating_timeout(this))
+       {
+               navigation_goalrating_start(this);
+               havocbot_goalrating_items(this, 10000, this.origin, 10000);
+               havocbot_goalrating_enemyplayers(this, 500, this.origin, 10000);
+               havocbot_goalrating_tkaball(this, 8000, this.origin);
+               navigation_goalrating_end(this);
+
+               navigation_goalrating_timeout_set(this);
+       }
+
+       if (this.ballcarried)
+       {
+               this.havocbot_role = havocbot_role_tka_carrier;
+               navigation_goalrating_timeout_expire(this, 2);
+       }
+}
+
+
+// ==============
+// Hook Functions
+// ==============
+
+MUTATOR_HOOKFUNCTION(tka, PlayerDies)
+{
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+
+       if(frag_attacker != frag_target && IS_PLAYER(frag_attacker) && DIFF_TEAM(frag_attacker, frag_target))
+       {
+               bool team_has_ball = false;
+               IL_EACH(g_tkaballs, it.owner != frag_attacker && SAME_TEAM(it.owner, frag_attacker),
+               {
+                       team_has_ball = true;
+                       break;
+               });
+               if(frag_target.ballcarried) { // add to amount of times killing carrier
+                       GameRules_scoring_add(frag_attacker, TKA_CARRIERKILLS, 1);
+                       if(autocvar_g_tka_score_bckill) // add bckills to the score
+                               GameRules_scoring_add_team(frag_attacker, SCORE, autocvar_g_tka_score_bckill);
+               }
+               else if(!frag_attacker.ballcarried && !(autocvar_g_tka_score_team && team_has_ball))
+               {
+                       if(autocvar_g_tka_noncarrier_warn)
+                               Send_Notification(NOTIF_ONE_ONLY, frag_attacker, MSG_CENTER, CENTER_KEEPAWAY_WARN);
+               }
+
+               if(frag_attacker.ballcarried || (autocvar_g_tka_score_team && team_has_ball)) // add to amount of kills while ballcarrier (or if team scoring is enabled)
+                       GameRules_scoring_add_team(frag_attacker, SCORE, autocvar_g_tka_score_killac);
+       }
+
+       if(frag_target.ballcarried) { tka_DropEvent(frag_target); } // a player with the ball has died, drop it
+}
+
+MUTATOR_HOOKFUNCTION(tka, GiveFragsForKill)
+{
+       M_ARGV(2, float) = 0; // no frags counted in keepaway
+       return true; // you deceptive little bugger ;3 This needs to be true in order for this function to even count.
+}
+
+MUTATOR_HOOKFUNCTION(tka, Scores_CountFragsRemaining)
+{
+       // announce remaining frags, but only when timed scoring is off
+       return !autocvar_g_tka_score_timepoints;
+}
+
+MUTATOR_HOOKFUNCTION(tka, PlayerPreThink)
+{
+       entity player = M_ARGV(0, entity);
+
+       // clear the item used for the ball in keepaway
+       STAT(TKA_BALLSTATUS, player) = 0;
+
+       // if the player has the ball, make sure they have the item for it (Used for HUD primarily)
+       if(player.ballcarried)
+               STAT(TKA_BALLSTATUS, player) |= TKA_BALL_CARRYING;
+
+       IL_EACH(g_tkaballs, true,
+       {
+               if(!it.owner)
+                       STAT(TKA_BALLSTATUS, player) |= TKA_BALL_DROPPED;
+               else
+               {
+                       // TODO: teamless carrier?
+                       switch(it.owner.team)
+                       {
+                               case NUM_TEAM_1: STAT(TKA_BALLSTATUS, player) |= TKA_BALL_TAKEN_RED; break;
+                               case NUM_TEAM_2: STAT(TKA_BALLSTATUS, player) |= TKA_BALL_TAKEN_BLUE; break;
+                               case NUM_TEAM_3: STAT(TKA_BALLSTATUS, player) |= TKA_BALL_TAKEN_YELLOW; break;
+                               case NUM_TEAM_4: STAT(TKA_BALLSTATUS, player) |= TKA_BALL_TAKEN_PINK; break;
+                       }
+               }
+       });
+}
+
+MUTATOR_HOOKFUNCTION(tka, PlayerUseKey)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(MUTATOR_RETURNVALUE == 0)
+       if(player.ballcarried)
+       {
+               tka_DropEvent(player);
+               return true;
+       }
+}
+
+MUTATOR_HOOKFUNCTION(tka, Damage_Calculate) // for changing damage and force values that are applied to players
+{
+       entity frag_attacker = M_ARGV(1, entity);
+       entity frag_target = M_ARGV(2, entity);
+       float frag_damage = M_ARGV(4, float);
+       vector frag_force = M_ARGV(6, vector);
+
+       // as a gamemode rule, only apply scaling to player versus player combat
+       if(!IS_PLAYER(frag_attacker) || !IS_PLAYER(frag_target))
+               return;
+
+       if(frag_attacker.ballcarried) // if the attacker is a ballcarrier
+       {
+               if(frag_target == frag_attacker) // damage done to yourself
+               {
+                       frag_damage *= autocvar_g_tka_ballcarrier_selfdamage;
+                       frag_force *= autocvar_g_tka_ballcarrier_selfforce;
+               }
+               else // damage done to other ballcarriers
+               {
+                       frag_damage *= autocvar_g_tka_ballcarrier_damage;
+                       frag_force *= autocvar_g_tka_ballcarrier_force;
+               }
+       }
+       else // if the attacker is a noncarrier
+       {
+               if(frag_target == frag_attacker) // damage done to yourself
+               {
+                       frag_damage *= autocvar_g_tka_noncarrier_selfdamage;
+                       frag_force *= autocvar_g_tka_noncarrier_selfforce;
+               }
+               else // damage done to other noncarriers
+               {
+                       frag_damage *= autocvar_g_tka_noncarrier_damage;
+                       frag_force *= autocvar_g_tka_noncarrier_force;
+               }
+       }
+
+       M_ARGV(4, float) = frag_damage;
+       M_ARGV(6, vector) = frag_force;
+}
+
+MUTATOR_HOOKFUNCTION(tka, ClientDisconnect)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(player.ballcarried) { tka_DropEvent(player); } // a player with the ball has left the match, drop it
+}
+
+MUTATOR_HOOKFUNCTION(tka, MakePlayerObserver)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(player.ballcarried) { tka_DropEvent(player); } // a player with the ball has left the match, drop it
+}
+
+MUTATOR_HOOKFUNCTION(tka, PlayerPowerups)
+{
+       entity player = M_ARGV(0, entity);
+
+       // In the future this hook is supposed to allow me to do some extra stuff with waypointsprites and invisibility powerup
+       // So bare with me until I can fix a certain bug with tka_ballcarrier_waypointsprite_visible_for_player()
+
+       player.effects &= ~autocvar_g_tka_ballcarrier_effects;
+
+       if(player.ballcarried)
+               player.effects |= autocvar_g_tka_ballcarrier_effects;
+}
+
+
+MUTATOR_HOOKFUNCTION(tka, PlayerPhysics_UpdateStats)
+{
+       entity player = M_ARGV(0, entity);
+       // these automatically reset, no need to worry
+
+       if(player.ballcarried)
+               STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_tka_ballcarrier_highspeed;
+}
+
+MUTATOR_HOOKFUNCTION(tka, BotShouldAttack)
+{
+       entity bot = M_ARGV(0, entity);
+       entity targ = M_ARGV(1, entity);
+
+       // if neither player has ball then don't attack unless the ball is on the ground
+       bool have_held_ball = false, team_has_ball = false;
+       IL_EACH(g_tkaballs, it.owner,
+       {
+               have_held_ball = true;
+               if(SAME_TEAM(bot, it.owner))
+                       team_has_ball = true;
+       });
+
+       if(!targ.ballcarried && !bot.ballcarried && have_held_ball && !(autocvar_g_tka_score_team && team_has_ball))
+               return true;
+}
+
+MUTATOR_HOOKFUNCTION(tka, HavocBot_ChooseRole)
+{
+       entity bot = M_ARGV(0, entity);
+
+       if (bot.ballcarried)
+               bot.havocbot_role = havocbot_role_tka_carrier;
+       else
+               bot.havocbot_role = havocbot_role_tka_collector;
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(tka, DropSpecialItems)
+{
+       entity frag_target = M_ARGV(0, entity);
+
+       if(frag_target.ballcarried)
+               tka_DropEvent(frag_target);
+}
+
+MUTATOR_HOOKFUNCTION(tka, SpectateCopy)
+{
+       entity spectatee = M_ARGV(0, entity);
+       entity client = M_ARGV(1, entity);
+
+       STAT(TKA_BALLSTATUS, client) = STAT(TKA_BALLSTATUS, spectatee);
+}
+
+MUTATOR_HOOKFUNCTION(tka, TeamBalance_CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
+{
+       M_ARGV(0, float) = tka_teams;
+       return true;
+}
+
+void tka_Initialize()
+{
+       tka_teams = autocvar_g_tka_teams_override;
+       if(tka_teams < 2)
+               tka_teams = cvar("g_tka_teams"); // read the cvar directly as it gets written earlier in the same frame
+       tka_teams = BITS(bound(2, tka_teams, 4));
+       GameRules_scoring(tka_teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, {
+               field(SP_TKA_PICKUPS, "pickups", 0);
+               field(SP_TKA_CARRIERKILLS, "bckills", 0);
+               field(SP_TKA_BCTIME, "bctime", SFL_SORT_PRIO_SECONDARY);
+       });
+
+       g_tkaballs = IL_NEW();
+       entity tka_Handler = new_pure(tka_Handler);
+       setthink(tka_Handler, tka_Handler_CheckBall);
+       InitializeEntity(tka_Handler, tka_Handler_CheckBall, INITPRIO_GAMETYPE);
+}
diff --git a/qcsrc/common/gamemodes/gamemode/tka/sv_tka.qh b/qcsrc/common/gamemodes/gamemode/tka/sv_tka.qh
new file mode 100644 (file)
index 0000000..20f92ee
--- /dev/null
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <common/mutators/base.qh>
+int autocvar_g_tka_point_limit;
+int autocvar_g_tka_point_leadlimit;
+bool autocvar_g_tka_team_spawns;
+void tka_Initialize();
+
+int tka_teams;
+//int autocvar_g_tka_teams;
+int autocvar_g_tka_teams_override;
+
+IntrusiveList g_tkaballs;
+REGISTER_MUTATOR(tka, false)
+{
+       MUTATOR_STATIC();
+       MUTATOR_ONADD
+       {
+               GameRules_teams(true);
+               GameRules_spawning_teams(autocvar_g_tka_team_spawns);
+               GameRules_limit_score(autocvar_g_tka_point_limit);
+               GameRules_limit_lead(autocvar_g_tka_point_leadlimit);
+
+               tka_Initialize();
+       }
+       return false;
+}
+
+const int TKA_BALL_COUNT = 1;
+
+void(entity this) havocbot_role_tka_carrier;
+void(entity this) havocbot_role_tka_collector;
+
+void tka_DropEvent(entity player);
diff --git a/qcsrc/common/gamemodes/gamemode/tka/tka.qc b/qcsrc/common/gamemodes/gamemode/tka/tka.qc
new file mode 100644 (file)
index 0000000..e0e6033
--- /dev/null
@@ -0,0 +1 @@
+#include "tka.qh"
diff --git a/qcsrc/common/gamemodes/gamemode/tka/tka.qh b/qcsrc/common/gamemodes/gamemode/tka/tka.qh
new file mode 100644 (file)
index 0000000..c6de0ee
--- /dev/null
@@ -0,0 +1,65 @@
+#pragma once
+
+#include <common/gamemodes/gamemode/deathmatch/deathmatch.qh>
+#include <common/gamemodes/gamemode/keepaway/keepaway.qh>
+#include <common/mapinfo.qh>
+#if defined(CSQC)
+       #include <common/gamemodes/gamemode/tka/cl_tka.qh>
+#endif
+
+CLASS(TeamKeepaway, Gametype)
+    INIT(TeamKeepaway)
+    {
+        this.gametype_init(this, _("Team Keepaway"),"tka","g_tka",GAMETYPE_FLAG_TEAMPLAY | GAMETYPE_FLAG_USEPOINTS,"","timelimit=15 pointlimit=50 teams=2 leadlimit=0",_("Keep the ball in your team's possession to get points for kills"));
+    }
+    METHOD(TeamKeepaway, m_parse_mapinfo, bool(string k, string v))
+    {
+        if (!k) {
+            cvar_set("g_tka_teams", cvar_defstring("g_tka_teams"));
+            return true;
+        }
+        switch (k) {
+            case "teams":
+                cvar_set("g_tka_teams", v);
+                return true;
+        }
+        return false;
+    }
+    METHOD(TeamKeepaway, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
+    {
+        if(spawnpoints >= 8 && diameter > 4096)
+            return true;
+        return false;
+    }
+    METHOD(TeamKeepaway, m_isForcedSupported, bool(Gametype this))
+    {
+        if(cvar("g_tka_on_ka_maps"))
+        {
+            // if this is set, all KA maps support TKA too
+            if(!(MapInfo_Map_supportedGametypes & this.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_KEEPAWAY.m_flags))
+                return true; // TODO: references another gametype (alternatively, we could check which gamemodes are always enabled and append this if any are supported)
+        }
+        if(cvar("g_tka_on_tdm_maps"))
+        {
+            // if this is set, all TDM maps support TKA too
+            if(!(MapInfo_Map_supportedGametypes & this.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH.m_flags))
+                return true; // TODO: references another gametype (alternatively, we could check which gamemodes are always enabled and append this if any are supported)
+        }
+        return false;
+    }
+    METHOD(TeamKeepaway, m_setTeams, void(string sa))
+    {
+        cvar_set("g_tka_teams", sa);
+    }
+#ifdef CSQC
+    ATTRIB(TeamKeepaway, m_modicons, void(vector pos, vector mySize), HUD_Mod_TeamKeepaway);
+#endif
+ENDCLASS(TeamKeepaway)
+REGISTER_GAMETYPE(TEAM_KEEPAWAY, NEW(TeamKeepaway));
+
+const int TKA_BALL_TAKEN_RED            = BIT(0);
+const int TKA_BALL_TAKEN_BLUE           = BIT(1);
+const int TKA_BALL_TAKEN_YELLOW         = BIT(2);
+const int TKA_BALL_TAKEN_PINK           = BIT(3);
+const int TKA_BALL_CARRYING             = BIT(4);
+const int TKA_BALL_DROPPED              = BIT(5);
index aa0ca250fdaede1d595fa6fd5f4113a022bba0d8..98b1f4d3ce8b5f78bee76adc87a0e057690ef0cd 100644 (file)
@@ -47,6 +47,10 @@ REGISTER_WAYPOINT(KeyCarrierPink, _("Key carrier"), "kh_pink_carrying", '0 1 1',
 
 REGISTER_WAYPOINT(KaBall, _("Ball"), "notify_ballpickedup", '0 1 1', 1);
 REGISTER_WAYPOINT(KaBallCarrier, _("Ball carrier"), "keepawayball_carrying", '1 0 0', 1);
+REGISTER_WAYPOINT(TkaBallCarrierRed, _("Ball carrier"), "tka_taken_red", '0 1 1', 1);
+REGISTER_WAYPOINT(TkaBallCarrierBlue, _("Ball carrier"), "tka_taken_blue", '0 1 1', 1);
+REGISTER_WAYPOINT(TkaBallCarrierYellow, _("Ball carrier"), "tka_taken_yellow", '0 1 1', 1);
+REGISTER_WAYPOINT(TkaBallCarrierPink, _("Ball carrier"), "tka_taken_pink", '0 1 1', 1);
 
 REGISTER_WAYPOINT(LmsLeader, _("Leader"), "", '0 1 1', 1);
 
index e13d3030c300bafe3832ada5c4aeb23f7c431cd8..8a01893f1019c423927029d57b004baa8f66b88c 100644 (file)
@@ -60,6 +60,13 @@ REGISTER_SP(NEXBALL_FAULTS);
 REGISTER_SP(ONS_CAPS);
 REGISTER_SP(ONS_TAKES);
 
+REGISTER_SP(TKA_PICKUPS);
+REGISTER_SP(TKA_BCTIME);
+REGISTER_SP(TKA_CARRIERKILLS);
+
+REGISTER_SP(SURV_SURVIVALS);
+REGISTER_SP(SURV_HUNTS);
+
 REGISTER_SP(SCORE);
 REGISTER_SP(KILLS);
 REGISTER_SP(DEATHS);
index c093b62826d020269e45b7792c642bf56bc65341..96a136cadc0dd3b11a6b5007345d6421afa80deb 100644 (file)
@@ -132,6 +132,7 @@ REGISTER_STAT(ROUNDLOST, int)
 REGISTER_STAT(CAPTURE_PROGRESS, float)
 REGISTER_STAT(ITEMSTIME, int, autocvar_sv_itemstime)
 REGISTER_STAT(KILL_TIME, float)
+REGISTER_STAT(TKA_BALLSTATUS, int)
 
 #ifdef SVQC
 float autocvar_sv_showfps = 0;
index 582d037f575e90f65e662ae2ed0dd95fa8745c1b..c038d52347e999cb61198efdfdf5d9fb92c523af 100644 (file)
@@ -665,12 +665,14 @@ float updateCompression()
        GAMETYPE(MAPINFO_TYPE_TEAM_MAYHEM) \
        GAMETYPE(MAPINFO_TYPE_MAYHEM) \
        GAMETYPE(MAPINFO_TYPE_KEEPAWAY) \
+       GAMETYPE(MAPINFO_TYPE_TEAM_KEEPAWAY) \
        GAMETYPE(MAPINFO_TYPE_KEYHUNT) \
        GAMETYPE(MAPINFO_TYPE_LMS) \
        GAMETYPE(MAPINFO_TYPE_DOMINATION) \
        GAMETYPE(MAPINFO_TYPE_NEXBALL) \
        GAMETYPE(MAPINFO_TYPE_ONSLAUGHT) \
        GAMETYPE(MAPINFO_TYPE_ASSAULT) \
+       GAMETYPE(MAPINFO_TYPE_SURVIVAL) \
        /* GAMETYPE(MAPINFO_TYPE_DUEL) */ \
        /**/
 
index 56d293f98076b0091ffdd50efb848f6a5142dec0..db614661cf2c3f80a4efc7d2b4467cd963ac67af 100644 (file)
@@ -305,6 +305,10 @@ void cvar_changes_init()
                BADCVAR("g_tdm");
                BADCVAR("g_tdm_on_dm_maps");
                BADCVAR("g_tdm_teams");
+               BADCVAR("g_tka");
+               BADCVAR("g_tka_on_ka_maps");
+               BADCVAR("g_tka_on_tdm_maps");
+               BADCVAR("g_tka_teams");
                BADCVAR("g_tmayhem");
                BADCVAR("g_tmayhem_teams");
                BADCVAR("g_vip");
index 827d7c5a701e7f765046638ce6e027150e891c38..8cb101f120d6a8b31d7dd71069bb3039b8624e0c 100644 (file)
@@ -1,43 +1,7 @@
-models/ctf/glow_red
+models/ctf/glow
 {
  {
-       map models/ctf/glow_red.tga
-       tcMod scale 0.03 0.03
-       tcMod scroll 0.1 -0.04
-       tcMod rotate 0.1
- }
-}
-models/ctf/glow_blue
-{
- {
-       map models/ctf/glow_blue.tga
-       tcMod scale 0.03 0.03
-       tcMod scroll 0.1 -0.04
-       tcMod rotate 0.1
- }
-}
-models/ctf/glow_yellow
-{
- {
-       map models/ctf/glow_yellow.tga
-       tcMod scale 0.03 0.03
-       tcMod scroll 0.1 -0.04
-       tcMod rotate 0.1
- }
-}
-models/ctf/glow_pink
-{
- {
-       map models/ctf/glow_pink.tga
-       tcMod scale 0.03 0.03
-       tcMod scroll 0.1 -0.04
-       tcMod rotate 0.1
- }
-}
-models/ctf/glow_neutral
-{
- {
-       map models/ctf/glow_neutral.tga
+       map models/ctf/glow.tga
        tcMod scale 0.03 0.03
        tcMod scroll 0.1 -0.04
        tcMod rotate 0.1