Merge branch 'master' of git://git.xonotic.org/xonotic/xonotic-data.pk3dir
authorRudolf Polzer <divVerent@xonotic.org>
Wed, 1 Dec 2010 08:03:27 +0000 (09:03 +0100)
committerRudolf Polzer <divVerent@xonotic.org>
Wed, 1 Dec 2010 08:03:27 +0000 (09:03 +0100)
119 files changed:
balanceXonotic.cfg
defaultXonotic.cfg
effectinfo.txt
gfx/hud/default/keepawayball_carrying.tga [new file with mode: 0644]
gfx/hud/default/notify_balldropped.tga [new file with mode: 0644]
gfx/hud/default/notify_ballpickedup.tga [new file with mode: 0644]
gfx/hud/luminos/keepawayball_carrying.tga [new file with mode: 0644]
gfx/hud/luminos/notify_balldropped.tga [new file with mode: 0644]
gfx/hud/luminos/notify_ballpickedup.tga [new file with mode: 0644]
models/ctf/banner_blue.tga [new file with mode: 0644]
models/ctf/banner_blue_gloss.tga [new file with mode: 0644]
models/ctf/banner_red.tga [new file with mode: 0644]
models/ctf/banner_red_gloss.tga [new file with mode: 0644]
models/ctf/flag_blue.tga [new file with mode: 0644]
models/ctf/flag_blue_gloss.tga [new file with mode: 0644]
models/ctf/flag_blue_glow.tga [new file with mode: 0644]
models/ctf/flag_blue_norm.tga [new file with mode: 0644]
models/ctf/flag_red.tga [new file with mode: 0644]
models/ctf/flag_red_gloss.tga [new file with mode: 0644]
models/ctf/flag_red_glow.tga [new file with mode: 0644]
models/ctf/flag_red_norm.tga [new file with mode: 0644]
models/ctf/flags.md3
models/ctf/flags.md3.framegroups [new file with mode: 0644]
models/ctf/flags.md3_0.skin
models/ctf/flags.md3_1.skin
models/ctf/glow_blue.tga [new file with mode: 0644]
models/ctf/glow_blue_glow.tga [new file with mode: 0644]
models/ctf/glow_red.tga [new file with mode: 0644]
models/ctf/glow_red_glow.tga [new file with mode: 0644]
models/elaser.mdl
models/hlac_bullet.md3
models/laser.mdl
models/orbs/orbblue.md3 [new file with mode: 0644]
models/orbs/orbblue.tga [new file with mode: 0644]
models/orbs/orbblue_glow.tga [new file with mode: 0644]
models/orbs/orbpink.md3 [new file with mode: 0644]
models/orbs/orbpink.tga [new file with mode: 0644]
models/orbs/orbpink_glow.tga [new file with mode: 0644]
models/orbs/orbred.md3 [new file with mode: 0644]
models/orbs/orbred.tga [new file with mode: 0644]
models/orbs/orbred_glow.tga [new file with mode: 0644]
models/orbs/orbyellow.md3 [new file with mode: 0644]
models/orbs/orbyellow.tga [new file with mode: 0644]
models/orbs/orbyellow_glow.tga [new file with mode: 0644]
models/plasmatrail.mdl
models/sprites/ka-ball.tga [new file with mode: 0644]
models/sprites/ka-ballcarrier.tga [new file with mode: 0644]
models/sprites/make-sprites.sh
models/weapons/g_minelayer.md3
models/weapons/v_minelayer.md3
qcsrc/client/View.qc
qcsrc/client/hud.qc
qcsrc/client/scoreboard.qc
qcsrc/common/constants.qh
qcsrc/common/mapinfo.qc
qcsrc/common/mapinfo.qh
qcsrc/common/util.qc
qcsrc/menu/xonotic/dialog_multiplayer_create.c
qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.c
qcsrc/server/arena.qc
qcsrc/server/bot/aim.qc
qcsrc/server/bot/havocbot/roles.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_physics.qc
qcsrc/server/cl_player.qc
qcsrc/server/cl_weapons.qc
qcsrc/server/clientcommands.qc
qcsrc/server/defs.qh
qcsrc/server/g_damage.qc
qcsrc/server/g_world.qc
qcsrc/server/mutators/base.qh
qcsrc/server/mutators/gamemode_freezetag.qc [new file with mode: 0644]
qcsrc/server/mutators/gamemode_keepaway.qc [new file with mode: 0644]
qcsrc/server/mutators/mutator_vampire.qc
qcsrc/server/mutators/mutators.qh
qcsrc/server/progs.src
qcsrc/server/scores_rules.qc
qcsrc/server/t_jumppads.qc
qcsrc/server/teamplay.qc
qcsrc/server/w_electro.qc
scripts/ctf.shader [new file with mode: 0644]
scripts/orbs.shader [new file with mode: 0644]
scripts/projectiles.shader [new file with mode: 0644]
scripts/weapons.shader
sound/keepaway/dropped.wav [new file with mode: 0644]
sound/keepaway/pickedup.wav [new file with mode: 0644]
sound/keepaway/respawn.wav [new file with mode: 0644]
sound/keepaway/touch.wav [new file with mode: 0644]
textures/flags/flag_blue.tga [deleted file]
textures/flags/flag_blue_cloth.tga [deleted file]
textures/flags/flag_blue_gloss.tga [deleted file]
textures/flags/flag_blue_glow.tga [deleted file]
textures/flags/flag_blue_laser.tga [deleted file]
textures/flags/flag_blue_norm.tga [deleted file]
textures/flags/flag_red.tga [deleted file]
textures/flags/flag_red_cloth.tga [deleted file]
textures/flags/flag_red_gloss.tga [deleted file]
textures/flags/flag_red_glow.tga [deleted file]
textures/flags/flag_red_laser.tga [deleted file]
textures/flags/flag_red_norm.tga [deleted file]
textures/minelayer.tga [new file with mode: 0644]
textures/minelayer_gloss.tga [new file with mode: 0644]
textures/minelayer_glow.tga [new file with mode: 0644]
textures/projectiles/crylink_projectile_core.tga [new file with mode: 0644]
textures/projectiles/crylink_projectile_core_glow.tga [new file with mode: 0644]
textures/projectiles/crylink_projectile_long.tga [new file with mode: 0644]
textures/projectiles/crylink_projectile_long_glow.tga [new file with mode: 0644]
textures/projectiles/electro_projectile_core.tga [new file with mode: 0644]
textures/projectiles/electro_projectile_core_glow.tga [new file with mode: 0644]
textures/projectiles/electro_projectile_long.tga [new file with mode: 0644]
textures/projectiles/electro_projectile_long_glow.tga [new file with mode: 0644]
textures/projectiles/hlac_projectile_core.tga [new file with mode: 0644]
textures/projectiles/hlac_projectile_core_glow.tga [new file with mode: 0644]
textures/projectiles/hlac_projectile_long.tga [new file with mode: 0644]
textures/projectiles/hlac_projectile_long_glow.tga [new file with mode: 0644]
textures/projectiles/laser_projectile_core.tga [new file with mode: 0644]
textures/projectiles/laser_projectile_core_glow.tga [new file with mode: 0644]
textures/projectiles/laser_projectile_long.tga [new file with mode: 0644]
textures/projectiles/laser_projectile_long_glow.tga [new file with mode: 0644]

index ae6fa02..6ff82fd 100644 (file)
@@ -288,7 +288,7 @@ set g_balance_uzi_sustained_ammo 1
 set g_balance_uzi_speed 18000
 set g_balance_uzi_bulletconstant 115 // 13.1qu
 // }}}
-// {{{ mortar // TODO
+// {{{ mortar
 set g_balance_grenadelauncher_primary_type 0
 set g_balance_grenadelauncher_primary_damage 50
 set g_balance_grenadelauncher_primary_edgedamage 32
@@ -328,7 +328,7 @@ set g_balance_grenadelauncher_secondary_remote_detonateprimary 0
 set g_balance_grenadelauncher_bouncefactor 0.5
 set g_balance_grenadelauncher_bouncestop 0.12
 // }}}
-// {{{ minelayer // TODO
+// {{{ minelayer
 set g_balance_minelayer_damage 35
 set g_balance_minelayer_edgedamage 30
 set g_balance_minelayer_force 250
@@ -350,7 +350,7 @@ set g_balance_minelayer_remote_edgedamage 40
 set g_balance_minelayer_remote_radius 200
 set g_balance_minelayer_remote_force 300
 // }}}
-// {{{ electro // TODO
+// {{{ electro
 set g_balance_electro_lightning 1
 set g_balance_electro_primary_damage 100
 set g_balance_electro_primary_edgedamage 0
@@ -514,7 +514,7 @@ set g_balance_hagar_secondary_lifetime_rand 0
 set g_balance_hagar_secondary_refire 0.12
 set g_balance_hagar_secondary_ammo 1
 // }}}
-// {{{ rocketlauncher // TODO
+// {{{ rocketlauncher
 set g_balance_rocketlauncher_damage 90
 set g_balance_rocketlauncher_edgedamage 33
 set g_balance_rocketlauncher_force 350
index bf1d943..8a241f7 100644 (file)
@@ -182,6 +182,7 @@ seta crosshair_fireball_size 1      "crosshair size when wielding the fireball"
 
 // ring around crosshair, used for various purposes (such as indicating bullets left in clip, nex charge)
 seta crosshair_ring_size 2     "bullet counter ring size for Rifle, velocity ring for Nex"
+seta crosshair_ring_alpha 0.2  "ring alpha"
 
 seta crosshair_ring_campingrifle_alpha 0.15
 
@@ -568,6 +569,14 @@ seta g_nexball_goalleadlimit -1 "Nexball goal lead limit overriding the mapinfo
 seta g_ctf_win_mode 0  "0: captures only, 1: captures, then points, 2: points only"
 seta g_ctf_ignore_frags 0      "1: regular frags give no points"
 
+set g_freezetag 0 "Freeze Tag: Freeze the opposing team(s) to win, unfreeze teammates by standing next to them"
+seta 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)"
+seta g_freezetag_revive_time 2.5 "Time it takes to revive a frozen teammate"
+seta g_freezetag_revive_extra_size 100 "Distance in qu that you can stand from a frozen teammate to keep reviving him"
+seta g_freezetag_frozen_force 0.6 "How much to multiply the force on a frozen player with"
+
 // 50% of the spawns shall be far away from any players
 set g_spawn_furthest 0.5
 // respawn delay
@@ -607,6 +616,10 @@ set g_cts_respawn_waves 0
 set g_cts_respawn_delay 0.25
 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"
+set g_freezetag_respawn_waves 0
+set g_freezetag_respawn_delay 0.25
+set g_ka_respawn_delay 0
+set g_ka_respawn_waves 0
 
 // overtime
 seta timelimit_overtime 2 "duration in minutes of one added overtime, added to the timelimit"
@@ -1244,6 +1257,28 @@ set g_balance_keyhunt_damageforcescale 1
 seta g_keyhunt_teams_override 0
 set g_keyhunt_teams 0
 
+// keepaway
+set g_keepaway 0 "game mode which focuses around a ball, look at g_keepaway_win_mode for further details"
+set g_keepaway_bckillscore 1 "enable scoring points (y/n) for ball carrier kills"
+set g_keepaway_pointlimit      -1      "total amount of points you can get, -1 for unlimited"
+set g_keepaway_pointleadlimit  -1      "mercy rule, -1 for unlimited"
+set g_keepaway_ballcarrier_alpha 0.6 "alpha when the player is the ballcarrier"
+set g_keepaway_ballcarrier_highspeed 1.5 "speed multiplier done to the person holding the ball"
+set g_keepaway_ballcarrier_damage      1.5     "damage multiplier while having powerup"
+set g_keepaway_ballcarrier_force       1.5     "force multiplier while having powerup"
+set g_keepaway_ballcarrier_selfdamage  1       "self damage multiplier while having powerup"
+set g_keepaway_ballcarrier_selfforce   1.5     "self force multiplier while having powerup"
+set g_keepaway_noncarrier_warn 0       "warn players when they kill without holding the ball"
+set g_keepaway_noncarrier_damage       0.5     "damage done to other players if both you and they don't have the ball"
+set g_keepaway_noncarrier_force        0.5     "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_trail_color 254     "particle trail color from player/ball"
+set g_keepawayball_damageforcescale    2 "Scale of force which is applied to the ball by weapons/explosions/etc"
+set g_keepawayball_respawntime 15      "if no one picks up the ball, how long to wait until the ball respawns"
+seta g_keepaway_teams_override 0
+set g_keepaway_teams 0
+
 // so it can be stuffcmd-ed still
 set cl_gravity 800     "but ignored anyway"
 
@@ -1373,6 +1408,8 @@ seta hud_panel_engineinfo_framecounter_exponentialmovingaverage_instantupdate_ch
 seta hud_showbinds 1   "the way to show the keys to press in HUD messages: 0 displays commands, 1 bound keys, 2 both"
 seta hud_showbinds_limit 2     "maximum number of bound keys to show for a command. 0 for unlimited"
 
+seta hud_colorflash_alpha 0.5 "starting alpha of the color flash"
+
 seta hud_damage 1 "an improved version of gl_polyblend, draw an image instead when hurt"
 seta hud_damage_gentle_alpha_multiplier 0.25 "how much to multiply alpha of flash when using the cl_gentle version, it's much more opaque than the non-gentle version"
 seta hud_damage_gentle_color "1 0.7 1" "color of flash for cl_gentle version"
@@ -1767,6 +1804,7 @@ alias cl_hook_gamestart_as
 alias cl_hook_gamestart_rc
 alias cl_hook_gamestart_nexball
 alias cl_hook_gamestart_cts
+alias cl_hook_gamestart_ka
 alias cl_hook_gameend
 alias cl_hook_activeweapon
 
@@ -1787,6 +1825,7 @@ alias sv_hook_gamestart_as
 alias sv_hook_gamestart_rc
 alias sv_hook_gamestart_nexball
 alias sv_hook_gamestart_cts
+alias sv_hook_gamestart_ka
 alias sv_hook_gamerestart
 alias sv_hook_gameend
 
index 6c7428d..3e69bd8 100644 (file)
@@ -5110,3 +5110,18 @@ size 16 16
 alpha 256 256 1024
 velocityjitter 256 256 256
 
+//sparks for keepaway ball touch
+// used nowhere in code
+effect kaball_sparks
+count 35
+type spark
+tex 40 40
+color 0xa9cacf 0x0054ff
+size 1 3
+alpha 0 256 556
+gravity 1
+bounce 1.5
+originjitter 1 1 1
+velocityjitter 300 300 300
+velocitymultiplier 0.5
+airfriction 3
diff --git a/gfx/hud/default/keepawayball_carrying.tga b/gfx/hud/default/keepawayball_carrying.tga
new file mode 100644 (file)
index 0000000..9a63c35
Binary files /dev/null and b/gfx/hud/default/keepawayball_carrying.tga differ
diff --git a/gfx/hud/default/notify_balldropped.tga b/gfx/hud/default/notify_balldropped.tga
new file mode 100644 (file)
index 0000000..7300a15
Binary files /dev/null and b/gfx/hud/default/notify_balldropped.tga differ
diff --git a/gfx/hud/default/notify_ballpickedup.tga b/gfx/hud/default/notify_ballpickedup.tga
new file mode 100644 (file)
index 0000000..9a63c35
Binary files /dev/null and b/gfx/hud/default/notify_ballpickedup.tga differ
diff --git a/gfx/hud/luminos/keepawayball_carrying.tga b/gfx/hud/luminos/keepawayball_carrying.tga
new file mode 100644 (file)
index 0000000..9a63c35
Binary files /dev/null and b/gfx/hud/luminos/keepawayball_carrying.tga differ
diff --git a/gfx/hud/luminos/notify_balldropped.tga b/gfx/hud/luminos/notify_balldropped.tga
new file mode 100644 (file)
index 0000000..7300a15
Binary files /dev/null and b/gfx/hud/luminos/notify_balldropped.tga differ
diff --git a/gfx/hud/luminos/notify_ballpickedup.tga b/gfx/hud/luminos/notify_ballpickedup.tga
new file mode 100644 (file)
index 0000000..9a63c35
Binary files /dev/null and b/gfx/hud/luminos/notify_ballpickedup.tga differ
diff --git a/models/ctf/banner_blue.tga b/models/ctf/banner_blue.tga
new file mode 100644 (file)
index 0000000..8b95589
Binary files /dev/null and b/models/ctf/banner_blue.tga differ
diff --git a/models/ctf/banner_blue_gloss.tga b/models/ctf/banner_blue_gloss.tga
new file mode 100644 (file)
index 0000000..77168a8
Binary files /dev/null and b/models/ctf/banner_blue_gloss.tga differ
diff --git a/models/ctf/banner_red.tga b/models/ctf/banner_red.tga
new file mode 100644 (file)
index 0000000..a64aa36
Binary files /dev/null and b/models/ctf/banner_red.tga differ
diff --git a/models/ctf/banner_red_gloss.tga b/models/ctf/banner_red_gloss.tga
new file mode 100644 (file)
index 0000000..77168a8
Binary files /dev/null and b/models/ctf/banner_red_gloss.tga differ
diff --git a/models/ctf/flag_blue.tga b/models/ctf/flag_blue.tga
new file mode 100644 (file)
index 0000000..63feddd
Binary files /dev/null and b/models/ctf/flag_blue.tga differ
diff --git a/models/ctf/flag_blue_gloss.tga b/models/ctf/flag_blue_gloss.tga
new file mode 100644 (file)
index 0000000..3e92985
Binary files /dev/null and b/models/ctf/flag_blue_gloss.tga differ
diff --git a/models/ctf/flag_blue_glow.tga b/models/ctf/flag_blue_glow.tga
new file mode 100644 (file)
index 0000000..8605da1
Binary files /dev/null and b/models/ctf/flag_blue_glow.tga differ
diff --git a/models/ctf/flag_blue_norm.tga b/models/ctf/flag_blue_norm.tga
new file mode 100644 (file)
index 0000000..07d5e17
Binary files /dev/null and b/models/ctf/flag_blue_norm.tga differ
diff --git a/models/ctf/flag_red.tga b/models/ctf/flag_red.tga
new file mode 100644 (file)
index 0000000..63feddd
Binary files /dev/null and b/models/ctf/flag_red.tga differ
diff --git a/models/ctf/flag_red_gloss.tga b/models/ctf/flag_red_gloss.tga
new file mode 100644 (file)
index 0000000..3e92985
Binary files /dev/null and b/models/ctf/flag_red_gloss.tga differ
diff --git a/models/ctf/flag_red_glow.tga b/models/ctf/flag_red_glow.tga
new file mode 100644 (file)
index 0000000..dfe90a4
Binary files /dev/null and b/models/ctf/flag_red_glow.tga differ
diff --git a/models/ctf/flag_red_norm.tga b/models/ctf/flag_red_norm.tga
new file mode 100644 (file)
index 0000000..07d5e17
Binary files /dev/null and b/models/ctf/flag_red_norm.tga differ
index 5d91c7a..86d232c 100644 (file)
Binary files a/models/ctf/flags.md3 and b/models/ctf/flags.md3 differ
diff --git a/models/ctf/flags.md3.framegroups b/models/ctf/flags.md3.framegroups
new file mode 100644 (file)
index 0000000..05a4991
--- /dev/null
@@ -0,0 +1 @@
+0 50 5 1
\ No newline at end of file
index 05a4fe7..489df22 100644 (file)
@@ -1,3 +1,3 @@
-flag,flags/flag_red
-flag_cloth,flags/flag_red_cloth
-flag_laser,flags/flag_red_laser
+mesh,models/ctf/flag_red.tga
+mesh2,models/ctf/banner_red.tga
+mesh3,models/ctf/glow_red.tga
\ No newline at end of file
index 02f217f..47c5aeb 100644 (file)
@@ -1,3 +1,3 @@
-flag,flags/flag_blue
-flag_cloth,flags/flag_blue_cloth
-flag_laser,flags/flag_blue_laser
+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/glow_blue.tga b/models/ctf/glow_blue.tga
new file mode 100644 (file)
index 0000000..6968dc2
Binary files /dev/null and b/models/ctf/glow_blue.tga differ
diff --git a/models/ctf/glow_blue_glow.tga b/models/ctf/glow_blue_glow.tga
new file mode 100644 (file)
index 0000000..6968dc2
Binary files /dev/null and b/models/ctf/glow_blue_glow.tga differ
diff --git a/models/ctf/glow_red.tga b/models/ctf/glow_red.tga
new file mode 100644 (file)
index 0000000..9702b0b
Binary files /dev/null and b/models/ctf/glow_red.tga differ
diff --git a/models/ctf/glow_red_glow.tga b/models/ctf/glow_red_glow.tga
new file mode 100644 (file)
index 0000000..9702b0b
Binary files /dev/null and b/models/ctf/glow_red_glow.tga differ
index 02fd59d..ce62d46 100644 (file)
Binary files a/models/elaser.mdl and b/models/elaser.mdl differ
index 8ae609a..a1be459 100644 (file)
Binary files a/models/hlac_bullet.md3 and b/models/hlac_bullet.md3 differ
index c3a5d99..8809d16 100644 (file)
Binary files a/models/laser.mdl and b/models/laser.mdl differ
diff --git a/models/orbs/orbblue.md3 b/models/orbs/orbblue.md3
new file mode 100644 (file)
index 0000000..ac412e5
Binary files /dev/null and b/models/orbs/orbblue.md3 differ
diff --git a/models/orbs/orbblue.tga b/models/orbs/orbblue.tga
new file mode 100644 (file)
index 0000000..325577f
Binary files /dev/null and b/models/orbs/orbblue.tga differ
diff --git a/models/orbs/orbblue_glow.tga b/models/orbs/orbblue_glow.tga
new file mode 100644 (file)
index 0000000..d06af31
Binary files /dev/null and b/models/orbs/orbblue_glow.tga differ
diff --git a/models/orbs/orbpink.md3 b/models/orbs/orbpink.md3
new file mode 100644 (file)
index 0000000..c6844d2
Binary files /dev/null and b/models/orbs/orbpink.md3 differ
diff --git a/models/orbs/orbpink.tga b/models/orbs/orbpink.tga
new file mode 100644 (file)
index 0000000..9dad41a
Binary files /dev/null and b/models/orbs/orbpink.tga differ
diff --git a/models/orbs/orbpink_glow.tga b/models/orbs/orbpink_glow.tga
new file mode 100644 (file)
index 0000000..5f90638
Binary files /dev/null and b/models/orbs/orbpink_glow.tga differ
diff --git a/models/orbs/orbred.md3 b/models/orbs/orbred.md3
new file mode 100644 (file)
index 0000000..041973a
Binary files /dev/null and b/models/orbs/orbred.md3 differ
diff --git a/models/orbs/orbred.tga b/models/orbs/orbred.tga
new file mode 100644 (file)
index 0000000..ffb352e
Binary files /dev/null and b/models/orbs/orbred.tga differ
diff --git a/models/orbs/orbred_glow.tga b/models/orbs/orbred_glow.tga
new file mode 100644 (file)
index 0000000..142092f
Binary files /dev/null and b/models/orbs/orbred_glow.tga differ
diff --git a/models/orbs/orbyellow.md3 b/models/orbs/orbyellow.md3
new file mode 100644 (file)
index 0000000..ca63a33
Binary files /dev/null and b/models/orbs/orbyellow.md3 differ
diff --git a/models/orbs/orbyellow.tga b/models/orbs/orbyellow.tga
new file mode 100644 (file)
index 0000000..64b61f7
Binary files /dev/null and b/models/orbs/orbyellow.tga differ
diff --git a/models/orbs/orbyellow_glow.tga b/models/orbs/orbyellow_glow.tga
new file mode 100644 (file)
index 0000000..9e0597b
Binary files /dev/null and b/models/orbs/orbyellow_glow.tga differ
index 59dcdb9..5669bc4 100644 (file)
Binary files a/models/plasmatrail.mdl and b/models/plasmatrail.mdl differ
diff --git a/models/sprites/ka-ball.tga b/models/sprites/ka-ball.tga
new file mode 100644 (file)
index 0000000..17b1e6e
Binary files /dev/null and b/models/sprites/ka-ball.tga differ
diff --git a/models/sprites/ka-ballcarrier.tga b/models/sprites/ka-ballcarrier.tga
new file mode 100644 (file)
index 0000000..ca0c12f
Binary files /dev/null and b/models/sprites/ka-ballcarrier.tga differ
index 59468b5..50131ee 100644 (file)
@@ -171,6 +171,8 @@ sprite race-checkpoint    "Checkpoint"    ff8000 000000 0.0
 sprite race-finish        "Finish"        ff8000 000000 0.0
 sprite race-start         "Start"         ff8000 000000 0.0
 sprite nb-ball            "Ball"          e8d8a0 000000 0.0
+sprite ka-ball            "Ball"          00ffff 000000 0.0
+sprite ka-ballcarrier     "Ball carrier"  ff0000 000000 0.0
 
 sprite wpn-laser          "Laser"         ff8080 000000 0.0 # bright red
 sprite wpn-shotgun        "Shotgun"       804000 000000 0.0 # brown
index 5fce1ef..60caa41 100644 (file)
Binary files a/models/weapons/g_minelayer.md3 and b/models/weapons/g_minelayer.md3 differ
index 4c7d449..648b0f1 100644 (file)
Binary files a/models/weapons/v_minelayer.md3 and b/models/weapons/v_minelayer.md3 differ
index 714b5d3..7cf5ee6 100644 (file)
@@ -764,6 +764,17 @@ void CSQC_UpdateView(float w, float h)
         CSQC_RAPTOR_HUD();
        else
        {
+               if(gametype == GAME_FREEZETAG)
+               {
+                       if(getstati(STAT_FROZEN))
+                               drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, '0.25 0.90 1', cvar_or("hud_colorflash_alpha", 0.5), DRAWFLAG_ADDITIVE);
+                       if(getstatf(STAT_REVIVE_PROGRESS))
+                       {
+                               DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_REVIVE_PROGRESS), '0.25 0.90 1', cvar("hud_colorflash_alpha"), DRAWFLAG_ADDITIVE);
+                               drawstring_aspect(eY * 0.64 * vid_conheight, "Revival progress", eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
+                       }
+               }
+
                if(cvar("r_letterbox") == 0)
                        if(cvar("viewsize") < 120)
                                CSQC_common_hud();
index 7ad148b..f66e417 100644 (file)
@@ -3023,6 +3023,17 @@ void HUD_KillNotify(string s1, string s2, string s3, float type, float msg) // s
                else if(type == RACE_FAIL) {
                        HUD_KillNotify_Push(s1, s2, 1, RACE_FAIL);
                }
+       } else if(msg == MSG_KA) {
+               if(type == KA_PICKUPBALL) {
+                       HUD_KillNotify_Push(s1, s2, 0, KA_PICKUPBALL);
+                       if(alsoprint)
+                               print (s1, "^7 has picked up the ball!\n");
+               }
+               else if(type == KA_DROPBALL) {
+                       HUD_KillNotify_Push(s1, s2, 0, KA_DROPBALL);
+                       if(alsoprint)
+                               print(s1, "^7 has dropped the ball!\n");
+               }
        }
 }
 
@@ -3302,6 +3313,15 @@ void HUD_Notify (void)
                                        s = "notify_blue_captured";
                                }
                        }
+                       else if(killnotify_deathtype[j] == KA_DROPBALL)
+                       {
+                               s = "notify_balldropped";
+                       }
+                       else if(killnotify_deathtype[j] == KA_PICKUPBALL)
+                       {
+                               s = "notify_ballpickedup";
+                       }
+                       
                        attacker = textShortenToWidth(killnotify_attackers[j], 0.48 * mySize_x - height, fontsize, stringwidth_colors);
                        pos_attacker = pos + eX * (0.52 * mySize_x + height) + eY * (0.5 * fontsize_y + i * height);
                        weap_pos = pos + eX * 0.5 * mySize_x - eX * height + eY * i * height;
@@ -4352,6 +4372,44 @@ void HUD_Mod_KH(vector pos, vector mySize)
        }
 }
 
+// Keepaway HUD mod icon
+float kaball_prevstatus; // last remembered status
+float kaball_statuschange_time; // time when the status changed
+
+// we don't need to reset for keepaway since it immediately 
+// autocorrects prevstatus as to if the player has the ball or not
+
+void HUD_Mod_Keepaway(vector pos, vector mySize)
+{
+       mod_active = 1; // keepaway should always show the mod HUD
+       
+       float BLINK_FACTOR = 0.15;
+       float BLINK_BASE = 0.85;
+       float BLINK_FREQ = 5; 
+       float kaball_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ);
+       
+       float stat_items = getstati(STAT_ITEMS);
+       float kaball = (stat_items/IT_KEY1) & 1;
+       
+       if(kaball != kaball_prevstatus)
+       {
+               kaball_statuschange_time = time;
+               kaball_prevstatus = kaball;
+       }
+       
+       // todo: Fix the sizing with the expanding image
+       
+       float kaball_statuschange_elapsedtime = time - kaball_statuschange_time;
+       float f = bound(0, kaball_statuschange_elapsedtime*2, 1);
+       
+       if(kaball_prevstatus && f < 1)
+               drawpic_aspect_skin_expanding(pos + eY * 0.25 * mySize_y, "keepawayball_carrying", eX * mySize_x + eY * mySize_y * 0.5, '1 1 1', panel_fg_alpha * kaball_alpha, DRAWFLAG_NORMAL, f);
+       
+       if(kaball)
+               drawpic_aspect_skin(pos, "keepawayball_carrying", eX * mySize_x + eY * mySize_y, '1 1 1', panel_fg_alpha * kaball_alpha * f, DRAWFLAG_NORMAL);
+}
+
+
 // Nexball HUD mod icon
 void HUD_Mod_NexBall(vector pos, vector mySize)
 {
@@ -4549,7 +4607,7 @@ void HUD_ModIcons(void)
        if(!autocvar_hud_panel_modicons && !autocvar__hud_configure)
                return;
 
-       if (gametype != GAME_KEYHUNT && gametype != GAME_CTF && gametype != GAME_NEXBALL && gametype != GAME_CTS && gametype != GAME_RACE && gametype != GAME_CA && !autocvar__hud_configure)
+       if (gametype != GAME_KEYHUNT && gametype != GAME_CTF && gametype != GAME_NEXBALL && gametype != GAME_CTS && gametype != GAME_RACE && gametype != GAME_CA && gametype != GAME_FREEZETAG && gametype != GAME_KEEPAWAY && !autocvar__hud_configure)
                return;
 
        active_panel = HUD_PANEL_MODICONS;
@@ -4586,8 +4644,10 @@ void HUD_ModIcons(void)
                HUD_Mod_NexBall(pos, mySize);
        else if(gametype == GAME_CTS || gametype == GAME_RACE)
                HUD_Mod_Race(pos, mySize);
-       else if(gametype == GAME_CA)
+       else if(gametype == GAME_CA || gametype == GAME_FREEZETAG)
                HUD_Mod_CA(pos, mySize);
+       else if(gametype == GAME_KEEPAWAY)
+               HUD_Mod_Keepaway(pos, mySize);
 }
 
 // Draw pressed keys (#11)
index 5f4c64e..0476428 100644 (file)
@@ -248,12 +248,12 @@ string HUD_DefaultColumnLayout()
 {
        return strcat( // fteqcc sucks
                "ping pl name | ",
-               "-teams,race,lms/kills -teams,lms/deaths -teams,lms,race/suicides -race,dm,tdm/frags ", // tdm already has this in "score"
+               "-teams,race,lms/kills -teams,lms/deaths -teams,lms,race,ka/suicides -race,dm,tdm,ka/frags ", // tdm already has this in "score"
                "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns ",
                "+lms/lives +lms/rank ",
                "+kh/caps +kh/pushes +kh/destroyed ",
                "?+race/laps ?+race/time ?+race/fastest ",
-               "+as/objectives +nexball/faults +nexball/goals ",
+               "+as/objectives +nexball/faults +nexball/goals +ka/drops +ka/pickups +ka/bckills ",
                "-lms,race,nexball/score");
 }
 
index 8cc2463..b3929c7 100644 (file)
@@ -39,6 +39,8 @@ const float GAME_RACE = 11;
 const float GAME_NEXBALL = 12;
 const float GAME_CTS = 13;
 const float GAME_CA            = 14;
+const float GAME_FREEZETAG             = 15;
+const float GAME_KEEPAWAY              = 16;
 
 const float AS_STRING          = 1;
 const float AS_INT             = 2;
@@ -342,6 +344,12 @@ const float STAT_VEHICLESTAT_RELOAD2 = 66;
 // mod stats (1xx)
 const float STAT_REDALIVE = 100;
 const float STAT_BLUEALIVE = 101;
+const float STAT_YELLOWALIVE = 102;
+const float STAT_PINKALIVE = 103;
+
+// freeze tag
+const float STAT_FROZEN = 104;
+const float STAT_REVIVE_PROGRESS = 105;
 
 //const float STAT_SPIDERBOT_AIM     53 // compressShotOrigin
 //const float STAT_SPIDERBOT_TARGET  54 // compressShotOrigin
@@ -566,7 +574,7 @@ float MSG_SPREE = 2;
 float MSG_KILL_ACTION = 3;
 float MSG_KILL_ACTION_SPREE = 4;
 float MSG_INFO = 5;
-
+float MSG_KA = 6;
 float MSG_RACE = 10;
 
 float KILL_TEAM_RED = 10301;
@@ -594,6 +602,9 @@ float INFO_LOSTFLAG = 10321;
 float INFO_RETURNFLAG = 10322;
 float INFO_CAPTUREFLAG = 10323;
 
+float KA_PICKUPBALL = 10350;
+float KA_DROPBALL = 10351;
+
 float RACE_SERVER_RECORD = 10400;
 float RACE_NEW_TIME = 10401;
 float RACE_NEW_RANK = 10402;
index 5192b75..35a827f 100644 (file)
@@ -347,9 +347,11 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp
                MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DEATHMATCH;      // DM always works
                MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RUNEMATCH;       // Rune always works
                MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_LMS;             // LMS always works
+               MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEEPAWAY;                // Keepaway always works
 
                if(spawnpoints >= 8  && diameter > 4096) {
                        MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH;
+                       MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_FREEZETAG;
                        MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CA;
                }
                if(                     diameter < 4096)
@@ -406,6 +408,8 @@ string _MapInfo_GetDefault(float t)
                case MAPINFO_TYPE_ONSLAUGHT:       return "20 0";
                case MAPINFO_TYPE_NEXBALL:         return "5 20 0";
                case MAPINFO_TYPE_CTS:             return "20 0 0";
+               case MAPINFO_TYPE_FREEZETAG:       return "10 20 0";
+               case MAPINFO_TYPE_KEEPAWAY:        return "30 20 0";
                default:                           return "";
        }
 }
@@ -462,6 +466,14 @@ void _MapInfo_Map_ApplyGametype(string s, float pWantedType, float pThisType, fl
                s = cdr(s);
        }
 
+       if(pWantedType == MAPINFO_TYPE_KEEPAWAY)
+       {
+               sa = car(s);
+               if(sa != "")
+                       cvar_set("fraglimit", sa);
+               s = cdr(s);
+       }
+
        // rc = timelimit timelimit_qualification laps laps_teamplay
        if(pWantedType == MAPINFO_TYPE_RACE)
        {
@@ -526,6 +538,8 @@ string _MapInfo_GetDefaultEx(float t)
                case MAPINFO_TYPE_ONSLAUGHT:       return "timelimit=20";
                case MAPINFO_TYPE_NEXBALL:         return "timelimit=20 pointlimit=5 leadlimit=0";
                case MAPINFO_TYPE_CTS:             return "timelimit=20 skill=-1";
+               case MAPINFO_TYPE_FREEZETAG:       return "timelimit=20 pointlimit=10 teams=2 leadlimit=0";
+               case MAPINFO_TYPE_KEEPAWAY:        return "timelimit=20 pointlimit=30";
                default:                           return "";
        }
 }
@@ -650,6 +664,8 @@ float MapInfo_Type_FromString(string t)
        else if(t == "rc")      return MAPINFO_TYPE_RACE;
        else if(t == "nexball") return MAPINFO_TYPE_NEXBALL;
        else if(t == "cts")     return MAPINFO_TYPE_CTS;
+       else if(t == "freezetag")       return MAPINFO_TYPE_FREEZETAG;
+       else if(t == "keepaway") return MAPINFO_TYPE_KEEPAWAY;
        else if(t == "all")     return MAPINFO_TYPE_ALL;
        else                    return 0;
 }
@@ -670,6 +686,8 @@ string MapInfo_Type_ToString(float t)
        else if(t == MAPINFO_TYPE_RACE)            return "rc";
        else if(t == MAPINFO_TYPE_NEXBALL)         return "nexball";
        else if(t == MAPINFO_TYPE_CTS)             return "cts";
+       else if(t == MAPINFO_TYPE_FREEZETAG)       return "freezetag";
+       else if(t == MAPINFO_TYPE_KEEPAWAY)        return "keepaway";
        else if(t == MAPINFO_TYPE_ALL)             return "all";
        else                                       return "";
 }
@@ -1134,6 +1152,10 @@ float MapInfo_CurrentGametype()
                return MAPINFO_TYPE_NEXBALL;
        else if(cvar("g_cts"))
                return MAPINFO_TYPE_CTS;
+       else if(cvar("g_freezetag"))
+               return MAPINFO_TYPE_FREEZETAG;
+       else if(cvar("g_keepaway"))
+               return MAPINFO_TYPE_KEEPAWAY;
        else
                return MAPINFO_TYPE_DEATHMATCH;
 }
@@ -1174,7 +1196,9 @@ string MapInfo_GetGameTypeCvar(float t)
                case MAPINFO_TYPE_ONSLAUGHT: return "g_onslaught";
                case MAPINFO_TYPE_RACE: return "g_race";
                case MAPINFO_TYPE_NEXBALL: return "g_nexball";
+               case MAPINFO_TYPE_FREEZETAG: return "g_freezetag";
                case MAPINFO_TYPE_CTS: return "g_cts";
+               case MAPINFO_TYPE_KEEPAWAY:             return "g_keepaway";
                default: return "";
        }
 }
@@ -1196,6 +1220,8 @@ void MapInfo_SwitchGameType(float t)
        cvar_set("g_race",       (t == MAPINFO_TYPE_RACE)            ? "1" : "0");
        cvar_set("g_nexball",    (t == MAPINFO_TYPE_NEXBALL)         ? "1" : "0");
        cvar_set("g_cts",        (t == MAPINFO_TYPE_CTS)             ? "1" : "0");
+       cvar_set("g_freezetag",  (t == MAPINFO_TYPE_FREEZETAG)       ? "1" : "0");
+       cvar_set("g_keepaway",   (t == MAPINFO_TYPE_KEEPAWAY)        ? "1" : "0");
 }
 
 void MapInfo_LoadMap(string s)
index 1fcc0dc..1202668 100644 (file)
@@ -12,7 +12,9 @@ float MAPINFO_TYPE_KEYHUNT            = 1024;
 float MAPINFO_TYPE_ASSAULT             = 2048;
 float MAPINFO_TYPE_ONSLAUGHT           = 4096;
 float MAPINFO_TYPE_NEXBALL             = 8192;
-float MAPINFO_TYPE_ALL                 = 16383; // this has to include all above bits
+float MAPINFO_TYPE_FREEZETAG           = 16384;
+float MAPINFO_TYPE_KEEPAWAY    = 32768;
+float MAPINFO_TYPE_ALL                 = 65535; // this has to include all above bits
 
 float MAPINFO_FEATURE_WEAPONS       = 1; // not defined for minstagib-only maps
 
index e4db850..44f8751 100644 (file)
@@ -399,6 +399,8 @@ string GametypeNameFromType(float g)
        else if (g == GAME_RACE) return "rc";
        else if (g == GAME_NEXBALL) return "nexball";
        else if (g == GAME_CTS) return "cts";
+       else if (g == GAME_FREEZETAG) return "freezetag";
+       else if (g == GAME_KEEPAWAY) return "ka";
        return "dm";
 }
 
index 4fb8ac0..416a854 100644 (file)
@@ -39,18 +39,22 @@ void XonoticServerCreateTab_fill(entity me)
                        if(e.checked) e0 = NULL;
                me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_arena", "Arena"));
                        if(e.checked) e0 = NULL;
+               me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_keepaway", "Keepaway"));
+                       if(e.checked) e0 = NULL;
                me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_race", "Race"));
                        if(e.checked) e0 = NULL;
                me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_cts", "Race CTS"));
                        if(e.checked) e0 = NULL;
        me.TR(me);
-               n = 8;
+               n = 9;
                me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_tdm", "TDM"));
                        if(e.checked) e0 = NULL;
                me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_ctf", "CTF"));
                        if(e.checked) e0 = NULL;
                me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_ca", "CA"));
                        if(e.checked) e0 = NULL;
+               me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_freezetag", "Freeze Tag"));
+                       if(e.checked) e0 = NULL;
                me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_domination", "Domination"));
                        if(e.checked) e0 = NULL;
                me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_keyhunt", "Key Hunt"));
index 4673578..f3f620d 100644 (file)
@@ -28,6 +28,8 @@ CLASS(XonoticMapInfoDialog) EXTENDS(XonoticDialog)
        ATTRIB(XonoticMapInfoDialog, typeRaceLabel, entity, NULL)
        ATTRIB(XonoticMapInfoDialog, typeCTSLabel, entity, NULL)
        ATTRIB(XonoticMapInfoDialog, typeNexballLabel, entity, NULL)
+       ATTRIB(XonoticMapInfoDialog, typeFreezetagLabel, entity, NULL)
+       ATTRIB(XonoticMapInfoDialog, typeKeepawayLabel, entity, NULL)
 
        ATTRIB(XonoticMapInfoDialog, currentMapIndex, float, 0)
        ATTRIB(XonoticMapInfoDialog, currentMapBSPName, string, string_null)
@@ -83,6 +85,8 @@ void XonoticMapInfoDialog_loadMapInfo(entity me, float i, entity mlb)
        me.typeRaceLabel.disabled = !(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RACE);
        me.typeCTSLabel.disabled = !(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_CTS);
        me.typeNexballLabel.disabled = !(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_NEXBALL);
+       me.typeFreezetagLabel.disabled = !(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_FREEZETAG);
+       me.typeKeepawayLabel.disabled = !(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_KEEPAWAY);
 
        MapInfo_ClearTemps();
 }
@@ -146,6 +150,10 @@ void XonoticMapInfoDialog_fill(entity me)
                        me.typeCTSLabel = e;
                me.TD(me, 1, wgt, e = makeXonoticTextLabel(0, "Nexball"));
                        me.typeNexballLabel = e;
+               me.TD(me, 1, wgt, e = makeXonoticTextLabel(0, "Freezetag"));
+                       me.typeFreezetagLabel = e;
+               me.TD(me, 1, wgt, e = makeXonoticTextLabel(0, "Keepaway"));
+                       me.typeKeepawayLabel = e;
 
        me.gotoRC(me, me.rows - 2, 0);
                me.TD(me, 1, me.columns, e = makeXonoticTextLabel(0.5, ""));
index 0467431..fbabbcf 100644 (file)
@@ -24,6 +24,14 @@ void func_breakable_reset();
 void assault_objective_reset();
 void target_assault_roundend_reset();
 
+float next_round;
+float stopalivecheck;
+float redalive, bluealive, yellowalive, pinkalive;
+float totalalive;
+.float redalive_stat, bluealive_stat, yellowalive_stat, pinkalive_stat;
+float redspawned, bluespawned, yellowspawned, pinkspawned;
+float totalspawned;
+
 /**
  * Resets the state of all clients, items, flags, runes, keys, weapons, waypoints, ... of the map.
  * Sets the 'warmup' global variable.
@@ -39,6 +47,10 @@ void reset_map(float dorespawn)
                warmup = time + cvar("g_ca_warmup");
                allowed_to_spawn = 1;
        }
+       else if(g_freezetag)
+       {
+               warmup = time + cvar("g_freezetag_warmup");
+       }
 
        lms_lowest_lives = 999;
        lms_next_place = player_count;
@@ -46,7 +58,7 @@ void reset_map(float dorespawn)
        race_ReadyRestart();
 
        for(self = world; (self = nextent(self)); )
-       if(clienttype(self) == CLIENTTYPE_NOTACLIENT)
+       if(clienttype(self) == CLIENTTYPE_NOTACLIENT && self.items != IT_STRENGTH && self.items != IT_INVINCIBLE) // don't respawn strength or shield, that will only lead to them spawning very early each match
        {
                if(self.reset)
                {
@@ -92,6 +104,11 @@ void reset_map(float dorespawn)
                                self.classname = "player";
                                PutClientInServer();
                        }
+                       else if(g_freezetag)
+                       {
+                               if(self.classname == "player")
+                                       PutClientInServer();
+                       }
                        else
                        {
                                /*
@@ -191,8 +208,9 @@ void Arena_Warmup()
 {
        float f;
        string msg;
+    entity e;
 
-       if((!g_arena && !g_ca) || (g_arena && !arena_roundbased) || (time < game_starttime))
+       if((!g_arena && !g_ca && !g_freezetag) || (g_arena && !arena_roundbased) || (time < game_starttime))
                return;
 
        f = ceil(warmup - time);
@@ -228,7 +246,8 @@ void Arena_Warmup()
                        else if(f == 1)
                                Announce("1");
 
-                       centerprint(self, msg);
+            FOR_EACH_PLAYER(e)
+                centerprint(e, msg);
                }
 
                if (g_arena) {
@@ -251,19 +270,104 @@ void Arena_Warmup()
                if(g_ca) {
                        ca_players = 0;
 
-                       FOR_EACH_PLAYER(self)
+            FOR_EACH_PLAYER(e)
                                ca_players += 1;
                }
+
+        if(self.classname == "player" && self.health > 0 && self.movetype == MOVETYPE_NONE)
+            self.movetype = MOVETYPE_WALK;
+       }
+}
+
+void count_spawned_players()
+{
+       // TODO fix "*spawned" name, it should rather be "*players" or so
+       // not doing this now to prevent merge hell with Tag
+       // fix after merging with Tag
+
+       // count amount of players in each team
+       totalspawned = redspawned = bluespawned = yellowspawned = pinkspawned = 0;
+       FOR_EACH_PLAYER(self) {
+               if (self.team == COLOR_TEAM1)
+               {
+                       redspawned += 1;
+                       totalspawned += 1;
+               }
+               else if (self.team == COLOR_TEAM2)
+               {
+                       bluespawned += 1;
+                       totalspawned += 1;
+               }
+               else if (self.team == COLOR_TEAM3)
+               {
+                       yellowspawned += 1;
+                       totalspawned += 1;
+               }
+               else if (self.team == COLOR_TEAM4)
+               {
+                       pinkspawned += 1;
+                       totalspawned += 1;
+               }
+       }
+}
+
+void count_alive_players()
+{
+       totalalive = redalive = bluealive = yellowalive = pinkalive = 0;
+       if(g_ca)
+       {
+               FOR_EACH_PLAYER(self) {
+                       if (self.team == COLOR_TEAM1 && self.health >= 1)
+                       {
+                               redalive += 1;
+                               totalalive += 1;
+                       }
+                       else if (self.team == COLOR_TEAM2 && self.health >= 1)
+                       {
+                               bluealive += 1;
+                               totalalive += 1;
+                       }
+               }
+               FOR_EACH_PLAYER(self) {
+                       self.redalive_stat = redalive;
+                       self.bluealive_stat = bluealive;
+               }
+       }
+       else if(g_freezetag)
+       {
+               // count amount of alive players in each team
+               FOR_EACH_PLAYER(self) {
+                       if (self.team == COLOR_TEAM1 && self.freezetag_frozen == 0 && self.health >= 1)
+                       {
+                               redalive += 1;
+                               totalalive += 1;
+                       }
+                       else if (self.team == COLOR_TEAM2 && self.freezetag_frozen == 0 && self.health >= 1)
+                       {
+                               bluealive += 1;
+                               totalalive += 1;
+                       }
+                       else if (self.team == COLOR_TEAM3 && self.freezetag_frozen == 0 && self.health >= 1)
+                       {
+                               yellowalive += 1;
+                               totalalive += 1;
+                       }
+                       else if (self.team == COLOR_TEAM4 && self.freezetag_frozen == 0 && self.health >= 1)
+                       {
+                               pinkalive += 1;
+                               totalalive += 1;
+                       }
+               }
+               FOR_EACH_PLAYER(self) {
+                       self.redalive_stat = redalive;
+                       self.bluealive_stat = bluealive;
+                       self.yellowalive_stat = yellowalive;
+                       self.pinkalive_stat = pinkalive;
+               }
        }
 
-       if(self.classname == "player" && self.health > 0)
-               self.movetype = MOVETYPE_WALK;
 }
 
-float next_round;
-float stopalivecheck;
-float redalive, bluealive;
-.float redalive_stat, bluealive_stat;
 /**
  * This function finds out whether an arena round is over 1 player is left.
  * It determines the last player who's still alive and saves it's entity reference
@@ -273,33 +377,15 @@ float redalive, bluealive;
  */
 void Spawnqueue_Check()
 {
-       if(g_ca) // we want to perform this before the return block below...
+       count_spawned_players();
+       if(g_ca || g_freezetag) // we want to perform this before the return block below (CA)...
        {
-               // this is STUPID to perform again, but has to be done so that we can give instant feedback when a round ends
-               // and so the code won't start searching for a champion using find() before all players are actually REMOVED
-               redalive = 0; bluealive = 0;
-               FOR_EACH_PLAYER(self) {
-                       if (self.team == COLOR_TEAM1 && self.health >= 1) redalive += 1;
-                       else if (self.team == COLOR_TEAM2 && self.health >= 1) bluealive += 1;
-               }
-               // as if the above stuff wasn't stupid enough, let's run it a third time! :D
-               // (so that we can send redalive/bluealive as a stat)
-               FOR_EACH_PLAYER(self) {
-                       self.redalive_stat = redalive;
-                       self.bluealive_stat = bluealive;
-               }
+               count_alive_players();
        }
-       if(time < warmup + 1 || inWarmupStage)
+       if(time < warmup + 1 || inWarmupStage || intermission_running)
                return;
 
        if(g_ca) {
-               // check the amount of spawned players in each team
-               float redspawned, bluespawned;
-               FOR_EACH_PLAYER(self) {
-                       if (self.team == COLOR_TEAM1) redspawned += 1;
-                       else if (self.team == COLOR_TEAM2) bluespawned += 1;
-               }
-
                required_ca_players = max(2, fabs(cvar("bot_vs_human") + 1));
 
                if(ca_players < required_ca_players && (redspawned && bluespawned)) {
@@ -348,6 +434,12 @@ void Spawnqueue_Check()
                        next_round = 0;
                        reset_map(TRUE);
                }
+       } else if(g_freezetag) {
+               if((next_round && next_round < time))
+               {
+                       next_round = 0;
+                       reset_map(TRUE);
+               }
        } else { // arena
                //extend next_round if it isn't set yet and only 1 player is spawned
                if(!next_round)
index d1c9a5a..e6950dd 100644 (file)
@@ -109,6 +109,10 @@ float bot_shouldattack(entity e)
                        return FALSE;
        }
 
+       if(g_freezetag)
+               if(e.freezetag_frozen)
+                       return FALSE;
+
        if(teams_matter)
        {
                if(e.team==0)
index d2f6b90..1e9231b 100644 (file)
@@ -274,6 +274,24 @@ void havocbot_role_race()
        }
 };
 
+// Keepaway
+// If you don't have the ball, get it; if you do, kill people.
+void havocbot_role_ka()
+{
+       if(self.deadflag != DEAD_NO)
+               return;
+
+       if (self.bot_strategytime < time)
+       {
+               self.bot_strategytime = time + cvar("bot_ai_strategyinterval");
+               navigation_goalrating_start();
+               havocbot_goalrating_items(10000, self.origin, 10000);
+               havocbot_goalrating_enemyplayers(20000, self.origin, 10000);
+               //havocbot_goalrating_waypoints(1, self.origin, 1000);
+               navigation_goalrating_end();
+       }
+}
+
 void havocbot_chooserole_dm()
 {
        self.havocbot_role = havocbot_role_dm;
@@ -289,6 +307,11 @@ void havocbot_chooserole_dom()
        self.havocbot_role = havocbot_role_dom;
 };
 
+void havocbot_chooserole_ka()
+{
+       self.havocbot_role = havocbot_role_ka;
+}
+
 void havocbot_chooserole()
 {
        dprint("choosing a role...\n");
@@ -304,6 +327,8 @@ void havocbot_chooserole()
                havocbot_chooserole_race();
        else if (g_onslaught)
                havocbot_chooserole_ons();
+       else if (g_keepaway)
+               havocbot_chooserole_ka();
        else // assume anything else is deathmatch
                havocbot_chooserole_dm();
 };
index 68f699f..4eb3d58 100644 (file)
@@ -612,7 +612,7 @@ void PutObserverInServer (void)
        if(self.flagcarried)
                DropFlag(self.flagcarried, world, world);
 
-       if(self.ballcarried)
+       if(self.ballcarried && g_nexball)
                DropBall(self.ballcarried, self.origin + self.ballcarried.origin, self.velocity);
 
        WaypointSprite_PlayerDead();
@@ -1347,6 +1347,10 @@ void ClientKill (void)
        {
                // do nothing
        }
+    else if(g_freezetag && self.freezetag_frozen == 1)
+    {
+        // do nothing
+    }
        else
                ClientKill_TeamChange(0);
 }
@@ -1766,7 +1770,7 @@ void ClientDisconnect (void)
 
        if(self.flagcarried)
                DropFlag(self.flagcarried, world, world);
-       if(self.ballcarried)
+       if(self.ballcarried && g_nexball)
                DropBall(self.ballcarried, self.origin + self.ballcarried.origin, self.velocity);
 
        // Here, everything has been done that requires this player to be a client.
@@ -2000,6 +2004,9 @@ string getTimeoutText(float addOneSecond) {
 
 void player_powerups (void)
 {
+       // add a way to see what the items were BEFORE all of these checks for the mutator hook
+       olditems = self.items;
+       
        if((self.items & IT_USING_JETPACK) && !self.deadflag)
        {
                SoundEntity_StartSound(self, CHAN_PLAYER, "misc/jetpack_fly.wav", VOL_BASE, cvar("g_jetpack_attenuation"));
@@ -2062,64 +2069,67 @@ void player_powerups (void)
                                sprint(self, "^3You are on speed\n");
                        }
                }
-               return;
        }
-
-       if (self.items & IT_STRENGTH)
+       else // if we're not in minstagib, continue. I added this else to replace the "return" which was here that broke the callhook for this function -- This code is nasty.
        {
-               play_countdown(self.strength_finished, "misc/poweroff.wav");
-               self.effects = self.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
-               if (time > self.strength_finished && cvar("g_balance_powerup_timer"))
+               if (self.items & IT_STRENGTH)
                {
-                       self.items = self.items - (self.items & IT_STRENGTH);
-                       sprint(self, "^3Strength has worn off\n");
+                       play_countdown(self.strength_finished, "misc/poweroff.wav");
+                       self.effects = self.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
+                       if (time > self.strength_finished && cvar("g_balance_powerup_timer"))
+                       {
+                               self.items = self.items - (self.items & IT_STRENGTH);
+                               sprint(self, "^3Strength has worn off\n");
+                       }
                }
-       }
-       else
-       {
-               if (time < self.strength_finished)
+               else
                {
-                       self.items = self.items | IT_STRENGTH;
-                       sprint(self, "^3Strength infuses your weapons with devastating power\n");
+                       if (time < self.strength_finished)
+                       {
+                               self.items = self.items | IT_STRENGTH;
+                               sprint(self, "^3Strength infuses your weapons with devastating power\n");
+                       }
                }
-       }
-       if (self.items & IT_INVINCIBLE)
-       {
-               play_countdown(self.invincible_finished, "misc/poweroff.wav");
-               self.effects = self.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
-               if (time > self.invincible_finished && cvar("g_balance_powerup_timer"))
+               if (self.items & IT_INVINCIBLE)
                {
-                       self.items = self.items - (self.items & IT_INVINCIBLE);
-                       sprint(self, "^3Shield has worn off\n");
+                       play_countdown(self.invincible_finished, "misc/poweroff.wav");
+                       self.effects = self.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
+                       if (time > self.invincible_finished && cvar("g_balance_powerup_timer"))
+                       {
+                               self.items = self.items - (self.items & IT_INVINCIBLE);
+                               sprint(self, "^3Shield has worn off\n");
+                       }
                }
-       }
-       else
-       {
-               if (time < self.invincible_finished)
+               else
                {
-                       self.items = self.items | IT_INVINCIBLE;
-                       sprint(self, "^3Shield surrounds you\n");
+                       if (time < self.invincible_finished)
+                       {
+                               self.items = self.items | IT_INVINCIBLE;
+                               sprint(self, "^3Shield surrounds you\n");
+                       }
                }
-       }
 
-       if(cvar("g_nodepthtestplayers"))
-               self.effects = self.effects | EF_NODEPTHTEST;
+               if(cvar("g_nodepthtestplayers"))
+                       self.effects = self.effects | EF_NODEPTHTEST;
 
-       if(cvar("g_fullbrightplayers"))
-               self.effects = self.effects | EF_FULLBRIGHT;
+               if(cvar("g_fullbrightplayers"))
+                       self.effects = self.effects | EF_FULLBRIGHT;
 
-       // midair gamemode: damage only while in the air
-       // if in midair mode, being on ground grants temporary invulnerability
-       // (this is so that multishot weapon don't clear the ground flag on the
-       // first damage in the frame, leaving the player vulnerable to the
-       // remaining hits in the same frame)
-       if (self.flags & FL_ONGROUND)
-       if (g_midair)
-               self.spawnshieldtime = max(self.spawnshieldtime, time + cvar("g_midair_shieldtime"));
+               // midair gamemode: damage only while in the air
+               // if in midair mode, being on ground grants temporary invulnerability
+               // (this is so that multishot weapon don't clear the ground flag on the
+               // first damage in the frame, leaving the player vulnerable to the
+               // remaining hits in the same frame)
+               if (self.flags & FL_ONGROUND)
+               if (g_midair)
+                       self.spawnshieldtime = max(self.spawnshieldtime, time + cvar("g_midair_shieldtime"));
 
-       if (time >= game_starttime)
-       if (time < self.spawnshieldtime)
-               self.effects = self.effects | (EF_ADDITIVE | EF_FULLBRIGHT);
+               if (time >= game_starttime)
+               if (time < self.spawnshieldtime)
+                       self.effects = self.effects | (EF_ADDITIVE | EF_FULLBRIGHT);
+       }
+       
+       MUTATOR_CALLHOOK(PlayerPowerups);
 }
 
 float CalcRegen(float current, float stable, float regenfactor, float regenframetime)
index e9d1932..b852e3f 100644 (file)
@@ -46,6 +46,9 @@ When you press the jump key
 */
 void PlayerJump (void)
 {
+       if(g_freezetag && self.freezetag_frozen)
+               return; // no jumping in freezetag when frozen
+
        float mjumpheight;
        float doublejump;
 
@@ -690,8 +693,12 @@ void SV_PlayerPhysics()
        maxspd_mod = 1;
        if(g_minstagib && (self.items & IT_INVINCIBLE))
                maxspd_mod *= cvar("g_minstagib_speed_highspeed");
-       if(g_nexball && self.ballcarried)
-               maxspd_mod *= cvar("g_nexball_basketball_carrier_highspeed");
+       if(self.ballcarried)
+               if(g_nexball)
+                       maxspd_mod *= cvar("g_nexball_basketball_carrier_highspeed");
+               else if(g_keepaway)
+                       maxspd_mod *= cvar("g_keepaway_ballcarrier_highspeed");
+
        if(g_runematch)
        {
                if(self.runes & RUNE_SPEED)
index cb20a91..f8acd05 100644 (file)
@@ -386,6 +386,8 @@ void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float
 }
 
 void ClientKill_Now_TeamChange();
+void freezetag_CheckWinner();
+void freezetag_Unfreeze();
 
 void PlayerDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 {
@@ -455,7 +457,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
        damage_take = take;
        damage_save = save;
        damage_force = force;
-       MUTATOR_CALLHOOK(PlayerDamage);
+       MUTATOR_CALLHOOK(PlayerDamage_SplitHealthArmor);
        take = bound(0, damage_take, self.health);
        save = bound(0, damage_save, self.armorvalue);
 
@@ -601,22 +603,37 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                        }
                }
 
-               // become fully visible
-               self.alpha = 1;
-               // clear selected player display
-               ClearSelectedPlayer();
-               // throw a weapon
-               SpawnThrownWeapon (self.origin + (self.mins + self.maxs) * 0.5, self.switchweapon);
+               if(!g_freezetag)
+               {
+                       // become fully visible
+                       self.alpha = 1;
+                       // clear selected player display
+                       ClearSelectedPlayer();
+                       // throw a weapon
+                       SpawnThrownWeapon (self.origin + (self.mins + self.maxs) * 0.5, self.switchweapon);
+               }
+
                // print an obituary message
                Obituary (attacker, inflictor, self, deathtype);
                race_PreDie();
                DropAllRunes(self);
 
+               if(deathtype == DEATH_HURTTRIGGER && g_freezetag)
+               {
+                       PutClientInServer();
+                       count_alive_players(); // re-count players
+                       freezetag_CheckWinner();
+                       return;
+               }
+
                frag_attacker = attacker;
                frag_inflictor = inflictor;
                frag_target = self;
                MUTATOR_CALLHOOK(PlayerDies);
 
+               if(g_freezetag)
+            return;
+
                if(self.flagcarried)
                {
                        if(attacker.classname != "player" && attacker.classname != "gib")
@@ -626,7 +643,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                        else
                                DropFlag(self.flagcarried, world, attacker);
                }
-               if(self.ballcarried)
+               if(self.ballcarried && g_nexball)
                        DropBall(self.ballcarried, self.origin, self.velocity);
                Portal_ClearAllLater(self);
                // clear waypoints
index 477b641..5b76539 100644 (file)
@@ -310,7 +310,10 @@ void W_WeaponFrame()
        if (frametime)
                self.weapon_frametime = frametime;
 
-       if(((arena_roundbased || g_ca) && time < warmup) || ((time < game_starttime) && !cvar("sv_ready_restart_after_countdown")))
+       if(((arena_roundbased || g_ca || g_freezetag) && time < warmup) || ((time < game_starttime) && !cvar("sv_ready_restart_after_countdown")))
+               return;
+
+       if(g_freezetag && self.freezetag_frozen == 1)
                return;
 
        if (!self.weaponentity || self.health < 1)
index e221911..1a46ed8 100644 (file)
@@ -213,7 +213,7 @@ void SV_ParseClientCommand(string s) {
                if(self.classname == "player" && cvar("sv_spectate") == 1) {
                        if(self.flagcarried)
                                DropFlag(self.flagcarried, world, world);
-                       if(self.ballcarried)
+                       if(self.ballcarried && g_nexball)
                                DropBall(self.ballcarried, self.origin, self.velocity);
                        WaypointSprite_PlayerDead();
                        self.classname = "observer";
index 6d967e6..381543f 100644 (file)
@@ -17,7 +17,7 @@ float require_spawnfunc_prefix; // if this float exists, only functions with spa
 
 float ctf_score_value(string parameter);
 
-float g_dm, g_domination, g_ctf, g_tdm, g_keyhunt, g_onslaught, g_assault, g_arena, g_ca, g_lms, g_runematch, g_race, g_nexball, g_cts;
+float g_dm, g_domination, g_ctf, g_tdm, g_keyhunt, g_onslaught, g_assault, g_arena, g_ca, g_lms, g_runematch, g_race, g_nexball, g_cts, g_freezetag, g_keepaway;
 float g_cloaked, g_footsteps, g_jump_grunt, g_grappling_hook, g_midair, g_minstagib, g_pinata, g_norecoil, g_minstagib_invis_alpha, g_bloodloss;
 float g_warmup_limit;
 float g_warmup_allguns;
@@ -555,8 +555,8 @@ void target_voicescript_clear(entity pl);
 .string target4;
 .float trigger_reverse;
 
-// Nexball
-.entity ballcarried;
+// Nexball 
+.entity ballcarried; // Also used for keepaway
 .float metertime;
 float g_nexball_meter_period;
 
@@ -654,3 +654,7 @@ float allowed_to_spawn; // boolean variable used by the clan arena code to deter
 float serverflags;
 
 .float team_forced; // can be a team number to force a team, or 0 for default action, or -1 for forced spectator
+
+.float freezetag_frozen;
+.float freezetag_beginrevive_time;
+.float freezetag_revive_progress;
index cde87fe..fd5b445 100644 (file)
@@ -625,7 +625,17 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float
                        force = force * g_weaponforcefactor;
                        mirrorforce *= g_weaponforcefactor;
                }
-
+               
+               // should this be changed at all? If so, in what way?
+               frag_attacker = attacker;
+               frag_target = targ;
+               frag_damage = damage;
+               frag_force = force;
+        frag_deathtype = deathtype;
+               MUTATOR_CALLHOOK(PlayerDamage_Calculate);
+               damage = frag_damage;
+               force = frag_force;
+               
                // apply strength multiplier
                if ((attacker.items & IT_STRENGTH) && !g_minstagib)
                {
index 29ebeb3..9a3c361 100644 (file)
@@ -329,6 +329,7 @@ void cvar_changes_init()
                BADCVAR("g_runematch");
                BADCVAR("g_tdm");
                BADCVAR("g_nexball");
+               BADCVAR("g_keepaway");
                BADCVAR("teamplay");
 
                // long
@@ -809,11 +810,19 @@ void spawnfunc_worldspawn (void)
        addstat(STAT_NEX_CHARGE, AS_FLOAT, nex_charge);
        addstat(STAT_NEX_CHARGEPOOL, AS_FLOAT, nex_charge_pool_ammo);
 
-       if(g_ca)
+       if(g_ca || g_freezetag)
        {
                addstat(STAT_REDALIVE, AS_INT, redalive_stat);
                addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat);
+               addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat);
+               addstat(STAT_PINKALIVE, AS_INT, pinkalive_stat);
        }
+       if(g_freezetag)
+       {
+               addstat(STAT_FROZEN, AS_INT, freezetag_frozen);
+               addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, freezetag_revive_progress);
+       }
+
        // g_movementspeed hack
        addstat(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW, AS_FLOAT, stat_sv_airspeedlimit_nonqw);
        addstat(STAT_MOVEVARS_MAXSPEED, AS_FLOAT, stat_sv_maxspeed);
index fdfbdfb..b7c523d 100644 (file)
@@ -119,13 +119,30 @@ MUTATOR_HOOKABLE(EditProjectile);
                entity self;
                entity other;
 
-MUTATOR_HOOKABLE(PlayerDamage);
+MUTATOR_HOOKABLE(PlayerDamage_SplitHealthArmor);
        // called when a player gets damaged to e.g. remove stuff he was carrying.
        // INPUT:
                entity frag_inflictor;
                entity frag_attacker;
                entity frag_target; // same as self
-               vector damage_force; // NOTE: this force already HAS been applied (create and use a Damage hook to change that one)
+               vector damage_force; // NOTE: this force already HAS been applied
        // INPUT, OUTPUT:
                float damage_take;
                float damage_save;
+               
+MUTATOR_HOOKABLE(PlayerDamage_Calculate);
+       // called to adjust damage and force values which are applied to the player, used for e.g. strength damage/force multiplier or runematch runes
+       // i'm not sure if I should change this around slightly (Naming of the entities, and also how they're done in g_damage).
+       // INPUT:
+               entity frag_attacker;
+               entity frag_target;
+               float frag_deathtype;
+       // INPUT, OUTPUT:
+               float frag_damage;
+               vector frag_force;
+
+MUTATOR_HOOKABLE(PlayerPowerups);
+       // called at the end of player_powerups() in cl_client.qc, used for manipulating the values which are set by powerup items.
+       // INPUT
+       entity self;
+       float olditems; // also technically output, but since it is at the end of the function it's useless for that :P 
diff --git a/qcsrc/server/mutators/gamemode_freezetag.qc b/qcsrc/server/mutators/gamemode_freezetag.qc
new file mode 100644 (file)
index 0000000..790c9d0
--- /dev/null
@@ -0,0 +1,299 @@
+void freezetag_Initialize()
+{
+       precache_model("models/ice/ice.md3");
+       warmup = time + cvar("g_start_delay") + cvar("g_freezetag_warmup");
+}
+
+void freezetag_CheckWinner()
+{
+       if(time <= game_starttime) // game didn't even start yet! nobody can win in that case.
+               return;
+
+       if(next_round || (time > warmup - cvar("g_freezetag_warmup") && time < warmup))
+               return; // already waiting for next round to start
+
+       if((redalive >= 1 && bluealive >= 1) // counted in arena.qc
+               || (redalive >= 1 && yellowalive >= 1)
+               || (redalive >= 1 && pinkalive >= 1)
+               || (bluealive >= 1 && yellowalive >= 1)
+               || (bluealive >= 1 && pinkalive >= 1)
+               || (yellowalive >= 1 && pinkalive >= 1))
+               return; // we still have active players on two or more teams, nobody won yet
+
+       entity e, winner;
+       string teamname;
+
+       FOR_EACH_PLAYER(e)
+       {
+               if(e.freezetag_frozen == 0 && e.classname == "player" && e.health >= 1) // here's one player from the winning team... good
+               {
+                       winner = e;
+                       break; // break, we found the winner
+               }
+       }
+
+       if(winner != world) // just in case a winner wasn't found
+       {
+               TeamScore_AddToTeam(winner.team, ST_SCORE, +1);
+               if(winner.team == COLOR_TEAM1)
+                       teamname = "^1Red Team";
+               else if(winner.team == COLOR_TEAM2)
+                       teamname = "^4Blue Team";
+               else if(winner.team == COLOR_TEAM3)
+                       teamname = "^3Yellow Team";
+               else
+                       teamname = "^6Pink Team";
+               FOR_EACH_PLAYER(e) {
+                       centerprint(e, strcat(teamname, "^5 wins the round, all other teams were frozen.\n"));
+               }
+               bprint(teamname, "^5 wins the round since all the other teams were frozen.\n");
+       }
+
+       next_round = time + 5;
+}
+
+void freezetag_Ice_Think()
+{
+       setorigin(self, self.owner.origin - '0 0 16');
+       self.nextthink = time;
+}
+
+void freezetag_Freeze()
+{
+       self.freezetag_frozen = 1;
+
+       entity ice;
+       ice = spawn();
+       ice.owner = self;
+       ice.classname = "freezetag_ice";
+       ice.think = freezetag_Ice_Think;
+       ice.nextthink = time;
+       ice.frame = floor(random() * 21); // ice model has 20 different looking frames
+       setmodel(ice, "models/ice/ice.md3");
+
+       self.movement = '0 0 0';
+
+       // add waypoint
+       WaypointSprite_Spawn("freezetag_frozen", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attached, TRUE);
+       if(self.waypointsprite_attached)
+       {
+               WaypointSprite_UpdateTeamRadar(self.waypointsprite_attached, RADARICON_WAYPOINT, '0.25 0.90 1');
+       }
+}
+
+void freezetag_Unfreeze()
+{
+       self.freezetag_frozen = 0;
+
+       // remove the ice block
+       entity ice;
+       for(ice = world; (ice = find(ice, classname, "freezetag_ice")); ) if(ice.owner == self)
+       {
+               remove(ice);
+               break;
+       }
+
+       // remove waypoint
+       if(self.waypointsprite_attached)
+               WaypointSprite_Kill(self.waypointsprite_attached);
+}
+
+MUTATOR_HOOKFUNCTION(freezetag_RemovePlayer)
+{
+       if(self.freezetag_frozen == 0)
+       {
+               if(self.team == COLOR_TEAM1)
+                       --redalive;
+               else if(self.team == COLOR_TEAM2)
+                       --bluealive;
+               else if(self.team == COLOR_TEAM3)
+                       --yellowalive;
+               else if(self.team == COLOR_TEAM4)
+                       --pinkalive;
+               --totalalive;
+       }
+
+       if(totalspawned > 2) // only check for winners if we had more than two players (one of them left, don't let the other player win just because of that)
+               freezetag_CheckWinner();
+
+       freezetag_Unfreeze();
+
+       return 1;
+}
+
+MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
+{
+       if(self.freezetag_frozen == 0)
+       {
+               if(self.team == COLOR_TEAM1)
+                       --redalive;
+               else if(self.team == COLOR_TEAM2)
+                       --bluealive;
+               else if(self.team == COLOR_TEAM3)
+                       --yellowalive;
+               else if(self.team == COLOR_TEAM4)
+                       --pinkalive;
+               --totalalive;
+
+        freezetag_Freeze();
+       }
+
+    if(frag_attacker.classname == STR_PLAYER)
+        centerprint(frag_attacker, strcat("^2You froze ^7", frag_target.netname, ".\n"));
+
+       if(frag_attacker == frag_target || frag_attacker == world)
+       {
+        if(frag_target.classname == STR_PLAYER)
+            centerprint(frag_target, "^1You froze yourself.\n");
+               bprint("^7", frag_target.netname, "^1 froze himself.\n");
+       }
+       else
+       {
+        if(frag_target.classname == STR_PLAYER)
+            centerprint(frag_target, strcat("^1You were frozen by ^7", frag_attacker.netname, ".\n"));
+               bprint("^7", frag_target.netname, "^1 was frozen by ^7", frag_attacker.netname, ".\n");
+       }
+
+       frag_target.health = cvar("g_balance_health_start"); // "respawn" the player :P
+
+       freezetag_CheckWinner();
+
+       return 1;
+}
+
+MUTATOR_HOOKFUNCTION(freezetag_PlayerSpawn)
+{
+    freezetag_Unfreeze(); // start by making sure that all ice blocks are removed
+
+       if(totalspawned == 1 && time > game_starttime) // only one player active on server, start a new match immediately
+       if(!next_round && warmup && (time < warmup - cvar("g_freezetag_warmup") || time > warmup)) // not awaiting next round
+       {
+               next_round = time;
+               return 1;
+       }
+       if(warmup && time > warmup) // spawn too late, freeze player
+       {
+               centerprint(self, "^1You spawned after the round started, you'll spawn as frozen.\n");
+               freezetag_Freeze();
+       }
+
+       return 1;
+}
+
+MUTATOR_HOOKFUNCTION(freezetag_GiveFragsForKill)
+{
+       frag_score = 0; // no frags counted in Freeze Tag
+       return 1;
+}
+
+MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink)
+{
+    vector revive_extra_size;
+    revive_extra_size = '1 1 1' * cvar("g_freezetag_revive_extra_size");
+
+    float teammate_nearby;
+    FOR_EACH_PLAYER(other) if(self != other)
+    {
+        if(other.freezetag_frozen == 0)
+        {
+            if(other.team == self.team)
+            {
+                teammate_nearby = boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax);
+                if(teammate_nearby)
+                    break;
+            }
+        }
+    }
+
+    if(teammate_nearby && self.freezetag_frozen == 1) // OK, there is at least one teammate reviving us
+    {
+        if(self.freezetag_beginrevive_time == -9999) // initialize values if this is the first frame of revival
+        {
+            self.freezetag_beginrevive_time = time;
+            self.freezetag_revive_progress = 0;
+        }
+        else
+        {
+            self.freezetag_revive_progress = (time - self.freezetag_beginrevive_time) / cvar("g_freezetag_revive_time");
+            if(time - self.freezetag_beginrevive_time >= cvar("g_freezetag_revive_time"))
+            {
+                freezetag_Unfreeze();
+
+                centerprint(self, strcat("^5You were revived by ^7", other.netname, ".\n"));
+                centerprint(other, strcat("^5You revived ^7", self.netname, ".\n"));
+                bprint("^7", other.netname, "^5 revived ^7", self.netname, ".\n");
+
+                self.freezetag_beginrevive_time = -9999;
+                self.freezetag_revive_progress = 0;
+            }
+        }
+        // now find EVERY teammate within reviving radius, set their revive_progress values correct
+        FOR_EACH_PLAYER(other) if(self != other)
+        {
+            if(other.freezetag_frozen == 0)
+            {
+                if(other.team == self.team)
+                {
+                    teammate_nearby = boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax);
+                    if(teammate_nearby)
+                        other.freezetag_revive_progress = self.freezetag_revive_progress;
+                }
+            }
+        }
+    }
+    else if(!teammate_nearby) // only if no teammate is nearby will we reset
+    {
+        self.freezetag_beginrevive_time = -9999;
+        self.freezetag_revive_progress = 0;
+    }
+
+       return 1;
+}
+
+MUTATOR_HOOKFUNCTION(freezetag_PlayerPhysics)
+{
+       if(self.freezetag_frozen)
+               self.movement = '0 0 0';
+       return 1;
+}
+
+MUTATOR_HOOKFUNCTION(freezetag_PlayerDamage_Calculate)
+{
+    if(g_freezetag)
+    {
+        if(frag_target.freezetag_frozen == 1 && frag_deathtype != DEATH_HURTTRIGGER)
+        {
+            frag_damage = 0;
+            frag_force = frag_force * cvar("g_freezetag_frozen_force");
+        }
+    }
+    return 1;
+}
+
+MUTATOR_DEFINITION(gamemode_freezetag)
+{
+       MUTATOR_HOOK(MakePlayerObserver, freezetag_RemovePlayer, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ClientDisconnect, freezetag_RemovePlayer, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerDies, freezetag_PlayerDies, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerSpawn, freezetag_PlayerSpawn, CBC_ORDER_ANY);
+       MUTATOR_HOOK(GiveFragsForKill, freezetag_GiveFragsForKill, CBC_ORDER_FIRST);
+       MUTATOR_HOOK(PlayerPreThink, freezetag_PlayerPreThink, CBC_ORDER_FIRST);
+       MUTATOR_HOOK(PlayerPhysics, freezetag_PlayerPhysics, CBC_ORDER_FIRST);
+       MUTATOR_HOOK(PlayerDamage_Calculate, freezetag_PlayerDamage_Calculate, CBC_ORDER_ANY);
+
+       MUTATOR_ONADD
+       {
+               if(time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+               g_freezetag = 1;
+               freezetag_Initialize();
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               g_freezetag = 0;
+               error("This is a game type and it cannot be removed at runtime.");
+       }
+
+       return 0;
+}
diff --git a/qcsrc/server/mutators/gamemode_keepaway.qc b/qcsrc/server/mutators/gamemode_keepaway.qc
new file mode 100644 (file)
index 0000000..c771ab1
--- /dev/null
@@ -0,0 +1,354 @@
+void ka_SpawnBall(void);
+void ka_TouchEvent(void);
+void ka_RespawnBall(void);
+void ka_DropEvent(entity);
+
+float ka_ballcarrier_waypointsprite_visible_for_player(entity);
+
+void ka_Initialize() // run at the start of a match, initiates game mode
+{
+       if(!g_keepaway)
+               return;
+               
+       precache_sound("keepaway/pickedup.wav");
+       precache_sound("keepaway/dropped.wav");
+       precache_sound("keepaway/respawn.wav");
+       precache_sound("keepaway/touch.wav");
+
+       ScoreRules_keepaway();
+       ka_SpawnBall();
+}
+
+void ka_Reset() // used to clear the ballcarrier whenever the match switches from warmup to normal
+{
+       if(self.owner)
+               if(self.owner.classname == "player")
+                       ka_DropEvent(self.owner);
+
+       ka_RespawnBall();
+}
+
+void ka_SpawnBall() // loads various values for the ball
+{
+       if(!g_keepaway) { return; }
+       
+       entity e;
+       e = spawn();
+       e.model = "models/orbs/orbblue.md3";    
+       e.scale = 1;
+       precache_model(e.model);
+       setmodel(e, e.model);
+       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.classname = "keepawayball";
+       e.damageforcescale = cvar("g_keepawayball_damageforcescale");
+       e.takedamage = DAMAGE_YES;
+       e.glow_color = cvar("g_keepawayball_trail_color");
+       e.glow_trail = TRUE;
+       e.movetype = MOVETYPE_BOUNCE;
+       e.touch = ka_TouchEvent;
+       e.flags = FL_ITEM;
+       e.reset = ka_Reset;
+       e.owner = world;
+
+       InitializeEntity(e, ka_RespawnBall, INITPRIO_SETLOCATION); // is this the right priority? Neh, I have no idea.. Well-- it works! So. 
+}
+
+void ka_RespawnBall() // runs whenever the ball needs to be relocated
+{
+       vector oldballorigin = self.origin;
+
+       if(MoveToRandomMapLocation(self, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256))
+       {
+               makevectors(self.angles);
+               self.movetype = MOVETYPE_BOUNCE;
+               self.velocity = '0 0 200';
+               self.angles = '0 0 0';
+               self.solid = SOLID_TRIGGER;
+               self.think = ka_RespawnBall;
+               self.nextthink = time + cvar("g_keepawayball_respawntime");
+               
+               pointparticles(particleeffectnum("electro_combo"), oldballorigin, '0 0 0', 1);
+               pointparticles(particleeffectnum("electro_combo"), self.origin, '0 0 0', 1);
+
+               WaypointSprite_Spawn("ka-ball", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attachedforcarrier, FALSE);
+               WaypointSprite_UpdateTeamRadar(self.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, '0 1 1');
+               WaypointSprite_Ping(self.waypointsprite_attachedforcarrier);    
+
+               sound(self, CHAN_AUTO, "keepaway/respawn.wav", VOL_BASE, ATTN_NONE); // ATTN_NONE (it's a sound intended to be heard anywhere) 
+       }
+       else
+       {
+               ka_RespawnBall(); // finding a location failed, retry 
+       }
+}
+
+void ka_TouchEvent() // runs any time that the ball comes in contact with something
+{
+       if(!self) { return; }
+       if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
+       { // The ball fell off the map, respawn it since players can't get to it
+               ka_RespawnBall();
+               return;
+       }
+       if(other.deadflag != DEAD_NO) { return; }
+       if(other.classname != "player") 
+       {  // The ball just touched an object, most likely the world
+               pointparticles(particleeffectnum("kaball_sparks"), self.origin, '0 0 0', 1);
+               sound(self, CHAN_AUTO, "keepaway/touch.wav", VOL_BASE, ATTN_NORM);
+               return; 
+       }
+       else if(self.wait > time) { return; }
+
+       // attach the ball to the player
+       self.owner = other;
+       other.ballcarried = self;
+       setattachment(self, other, "");
+       setorigin(self, '3 0 20');
+       
+       // make the ball invisible/unable to do anything
+       self.velocity = '0 0 0';
+       self.movetype = MOVETYPE_NONE;
+       self.touch = SUB_Null;
+       self.effects |= EF_NODRAW;
+       self.think = SUB_Null;
+       self.nextthink = 0;
+       self.takedamage = DAMAGE_NO;
+
+       // apply effects to player
+       other.glow_color = cvar("g_keepawayball_trail_color");
+       other.glow_trail = TRUE;
+       other.effects |= EF_DIMLIGHT;
+       other.alpha = cvar("g_keepaway_ballcarrier_alpha");
+       other.exteriorweaponentity.alpha = cvar("g_keepaway_ballcarrier_alpha");
+
+       // messages and sounds
+       Send_KillNotification(other.netname, "", "", KA_PICKUPBALL, MSG_KA);
+       WriteByte(MSG_BROADCAST, SVC_CENTERPRINT);
+       WriteString(MSG_BROADCAST, strcat("\n\n", other.netname, "^7 has picked up the ball!\n"));
+       sound(self.owner, CHAN_AUTO, "keepaway/pickedup.wav", VOL_BASE, ATTN_NONE); // ATTN_NONE (it's a sound intended to be heard anywhere) 
+       
+       // scoring
+       PlayerScore_Add(other, SP_KEEPAWAY_PICKUPS, 1);
+
+       // waypoints
+       WaypointSprite_AttachCarrier("ka-ballcarrier", other);
+       other.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = ka_ballcarrier_waypointsprite_visible_for_player;
+       WaypointSprite_UpdateRule(other.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
+       WaypointSprite_UpdateTeamRadar(other.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, '1 0 0');
+       WaypointSprite_Ping(other.waypointsprite_attachedforcarrier);   
+       WaypointSprite_Kill(self.waypointsprite_attachedforcarrier);
+}
+
+void ka_DropEvent(entity plyr) // runs any time that a player is supposed to lose the ball
+{
+       entity ball;
+       ball = plyr.ballcarried;
+
+       if(!ball) { return; }
+       
+       // reset the ball
+       setattachment(ball, world, "");
+       ball.movetype = MOVETYPE_BOUNCE;
+       ball.solid = SOLID_TRIGGER; // is this needed? 
+       ball.wait = time + 1; 
+       ball.think = ka_RespawnBall;
+       ball.nextthink = time + cvar("g_keepawayball_respawntime");
+       ball.touch = ka_TouchEvent;
+       ball.takedamage = DAMAGE_YES;
+       ball.effects &~= EF_NODRAW; 
+       setorigin(ball, plyr.origin + '0 0 10');
+       ball.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
+       ball.owner.ballcarried = world; // I hope nothing checks to see if the world has the ball in the rest of my code :P 
+       ball.owner = world;
+       
+       // reset the player effects
+       plyr.effects &~= EF_DIMLIGHT;
+       plyr.alpha = default_player_alpha;
+       plyr.exteriorweaponentity.alpha = default_weapon_alpha; 
+       plyr.glow_trail = FALSE;
+       
+       // messages and sounds
+       Send_KillNotification(plyr.netname, "", "", KA_DROPBALL, MSG_KA);
+       WriteByte(MSG_BROADCAST, SVC_CENTERPRINT);
+       WriteString(MSG_BROADCAST, strcat("\n\n", plyr.netname, "^7 has dropped the ball!\n"));
+       sound(other, CHAN_AUTO, "keepaway/dropped.wav", VOL_BASE, ATTN_NONE);   // ATTN_NONE (it's a sound intended to be heard anywhere) 
+       
+       // scoring
+       PlayerScore_Add(plyr, SP_KEEPAWAY_DROPS, 1);
+       
+       // waypoints
+       WaypointSprite_Spawn("ka-ball", 0, 0, ball, '0 0 64', world, ball.team, ball, waypointsprite_attachedforcarrier, FALSE);
+       WaypointSprite_UpdateRule(ball.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT);
+       WaypointSprite_UpdateTeamRadar(ball.waypointsprite_attachedforcarrier, RADARICON_FLAGCARRIER, '0 1 1');
+       WaypointSprite_Ping(ball.waypointsprite_attachedforcarrier);    
+       WaypointSprite_Kill(plyr.waypointsprite_attachedforcarrier);
+}
+
+float ka_ballcarrier_waypointsprite_visible_for_player(entity e) // runs on waypoints which are attached to ballcarriers, updates once per frame 
+{
+       if(e.ballcarried)
+       {
+               if(other.classname == "spectator") 
+                       return FALSE; // we don't want spectators of the ballcarrier to see the attached waypoint on the top of their screen
+               else if(g_minstagib && (e.items & IT_STRENGTH))
+                       return FALSE; // if the ballcarrier has invisibility, don't draw the waypoint as this is the function of invisibility in keepaway
+       }
+
+       return TRUE;
+}
+
+MUTATOR_HOOKFUNCTION(ka_RemovePlayer)
+{
+       if(self.ballcarried) { ka_DropEvent(self); } // a player with the ball has left the match, drop it
+       return 0;
+}
+
+MUTATOR_HOOKFUNCTION(ka_Scoring)
+{
+       if((frag_attacker != frag_target) && (frag_attacker.classname == "player"))
+       {
+               if(frag_target.ballcarried) { // add to amount of times killing carrier
+                       PlayerScore_Add(frag_attacker, SP_KEEPAWAY_CARRIERKILLS, 1);
+                       if(cvar("g_keepaway_bckillscore")) // add bckills to the score
+                               PlayerScore_Add(frag_attacker, SP_KEEPAWAY_SCORE, 1);
+               }
+               else if(!frag_attacker.ballcarried)
+                       if(cvar("g_keepaway_noncarrier_warn"))
+                               centerprint_atprio(frag_attacker, (CENTERPRIO_SPAM + 5), "Killing people while you don't have the ball gives no points!");
+
+               if(frag_attacker.ballcarried) // add to amount of kills while ballcarrier
+                       PlayerScore_Add(frag_attacker, SP_KEEPAWAY_SCORE, 1);
+       }
+
+       if(self.ballcarried) { ka_DropEvent(self); } // a player with the ball has died, drop it
+       return 0;
+}
+
+MUTATOR_HOOKFUNCTION(ka_GiveFragsForKill)
+{
+       frag_score = 0; // no frags counted in keepaway
+       return 0;
+}
+
+MUTATOR_HOOKFUNCTION(ka_PlayerPreThink)
+{
+       // clear the item used for the ball in keepaway
+       self.items &~= IT_KEY1;
+       
+       // if the player has the ball, make sure they have the item for it (Used for HUD primarily)
+       if(self.ballcarried)
+               self.items |= IT_KEY1;
+
+       // drop the ball if the player presses the use button
+       if(self.BUTTON_USE)
+               if(self.ballcarried) { ka_DropEvent(self); } 
+
+       return 0;
+}
+
+MUTATOR_HOOKFUNCTION(ka_PlayerDamage) // for changing damage and force values that are applied to players in g_damage.qc
+{
+       if(frag_attacker.ballcarried) // if the attacker is a ballcarrier
+       {
+               if(frag_target == frag_attacker) // damage done to yourself
+               {
+                       frag_damage *= cvar("g_keepaway_ballcarrier_selfdamage");
+                       frag_force *= cvar("g_keepaway_ballcarrier_selfforce");
+               }
+               else // damage done to noncarriers
+               {
+                       frag_damage *= cvar("g_keepaway_ballcarrier_damage");
+                       frag_force *= cvar("g_keepaway_ballcarrier_force");
+               }
+       }
+       else if not(frag_target.ballcarried) // if the target is a noncarrier
+       {
+               if(frag_target == frag_attacker) // damage done to yourself
+               {
+                       frag_damage *= cvar("g_keepaway_noncarrier_selfdamage");
+                       frag_force *= cvar("g_keepaway_noncarrier_selfforce");
+               }
+               else // damage done to other noncarriers
+               {
+                       frag_damage *= cvar("g_keepaway_noncarrier_damage");
+                       frag_force *= cvar("g_keepaway_noncarrier_force");
+               }
+       }
+       return 0;
+}
+
+MUTATOR_HOOKFUNCTION(ka_PlayerPowerups)
+{
+       if(self.ballcarried)
+       { 
+               // if the player has the ball, force ballcarrier alpha upon them
+               self.alpha = cvar("g_keepaway_ballcarrier_alpha");
+               self.exteriorweaponentity.alpha = cvar("g_keepaway_ballcarrier_alpha");
+       
+               // if we're in minstagib and a ballcarrier has just picked up invisibility, 
+               // notify all the other players that the ballcarrier no longer has a waypoint
+               if(g_minstagib)
+               {
+                       if(olditems & IT_STRENGTH) 
+                       {
+                               if(time > self.strength_finished) 
+                               {       // this only runs ONCE right after the player loses invisibility
+                                       bprint(self.netname, "^7 isn't invisible from radar anymore.\n");
+                               }
+                       }
+                       else 
+                       {
+                               if(time < self.strength_finished)
+                               {       // this only runs ONCE right after the player gains invisibility
+                                       bprint(self.netname, "^7 has picked up invisibility and can no longer be seen on radar!\n");
+                               }
+                       }
+               }
+       }
+       else if(g_minstagib)
+       {
+               // if we're in minstagib and a noncarrier has invisibility, assure that we apply the invisibility effects normally
+               if(olditems & IT_STRENGTH) 
+               {
+                       self.alpha = g_minstagib_invis_alpha;
+                       self.exteriorweaponentity.alpha = g_minstagib_invis_alpha;
+               }
+       }
+       else
+       {
+               // if we're a normal player with no powerups that edit alpha make sure the alpha is default. 
+               // (normal powerups just use EF_ADDITIVE)
+               self.alpha = default_player_alpha;
+               self.exteriorweaponentity.alpha = default_weapon_alpha;
+       }
+       
+       return 0;
+}
+
+MUTATOR_DEFINITION(gamemode_keepaway)
+{
+       // I don't quite understand these orders, perhaps someone could enlighten me?
+       MUTATOR_HOOK(MakePlayerObserver, ka_RemovePlayer, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ClientDisconnect, ka_RemovePlayer, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerDies, ka_Scoring, CBC_ORDER_ANY);
+       MUTATOR_HOOK(GiveFragsForKill, ka_GiveFragsForKill, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerPreThink, ka_PlayerPreThink, CBC_ORDER_FIRST);
+       MUTATOR_HOOK(PlayerDamage_Calculate, ka_PlayerDamage, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerPowerups, ka_PlayerPowerups, CBC_ORDER_ANY);
+
+       MUTATOR_ONADD
+       {
+               if(time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+               g_keepaway = 1;
+               ka_Initialize();
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               g_keepaway = 0;
+               error("This is a game type and it cannot be removed at runtime.");
+       }
+
+       return 0;
+}
\ No newline at end of file
index 54b5b9f..7e253dd 100644 (file)
@@ -29,7 +29,7 @@ MUTATOR_HOOKFUNCTION(vampire_BuildMutatorsPrettyString)
 
 MUTATOR_DEFINITION(mutator_vampire)
 {
-       MUTATOR_HOOK(PlayerDamage, vampire_PlayerDamage, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerDamage_SplitHealthArmor, vampire_PlayerDamage, CBC_ORDER_ANY);
        MUTATOR_HOOK(BuildMutatorsString, vampire_BuildMutatorsString, CBC_ORDER_ANY);
        MUTATOR_HOOK(BuildMutatorsPrettyString, vampire_BuildMutatorsPrettyString, CBC_ORDER_ANY);
 
index 542014e..d9fff44 100644 (file)
@@ -1,4 +1,6 @@
 MUTATOR_DECLARATION(gamemode_keyhunt);
+MUTATOR_DECLARATION(gamemode_freezetag);
+MUTATOR_DECLARATION(gamemode_keepaway);
 
 MUTATOR_DECLARATION(mutator_nix);
 MUTATOR_DECLARATION(mutator_dodging);
index 58cf663..c76395f 100644 (file)
@@ -181,6 +181,8 @@ playerstats.qc
 
 mutators/base.qc
 mutators/gamemode_keyhunt.qc
+mutators/gamemode_freezetag.qc
+mutators/gamemode_keepaway.qc
 mutators/mutator_nix.qc
 mutators/mutator_dodging.qc
 mutators/mutator_rocketflying.qc
index 4db1b44..96acdba 100644 (file)
@@ -187,3 +187,18 @@ void ScoreRules_nexball(float teams)
        ScoreInfo_SetLabel_PlayerScore(SP_NEXBALL_FAULTS, "faults", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER);
        ScoreRules_basics_end();
 }
+
+// Keep Away stuff
+#define SP_KEEPAWAY_PICKUPS 4
+#define SP_KEEPAWAY_CARRIERKILLS 5
+#define SP_KEEPAWAY_DROPS 6
+#define SP_KEEPAWAY_SCORE 7
+void ScoreRules_keepaway()
+{
+       ScoreRules_basics(0, SFL_SORT_PRIO_PRIMARY, 0, FALSE); // SFL_SORT_PRIO_PRIMARY
+       ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_SCORE,               "score",                SFL_SORT_PRIO_PRIMARY);
+       ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_PICKUPS,             "pickups",              0);
+       ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_CARRIERKILLS,        "bckills",              0);
+       ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_DROPS,               "drops",                SFL_LOWER_IS_BETTER);
+       ScoreRules_basics_end();
+}
index df5788f..e5340ab 100644 (file)
@@ -137,6 +137,7 @@ void trigger_push_touch()
        if (other.classname != "gib")
        if (other.classname != "casing")
        if (other.classname != "droppedweapon")
+       if (other.classname != "keepawayball")
        if (!other.projectiledeathtype || other.classname == "bullet")
                return;
 
index b9064c7..7df082c 100644 (file)
@@ -101,6 +101,8 @@ void WriteGameCvars()
        cvar_set("g_race", ftos(g_race));
        cvar_set("g_nexball", ftos(g_nexball));
        cvar_set("g_cts", ftos(g_cts));
+       cvar_set("g_freezetag", ftos(g_freezetag));
+       cvar_set("g_keepaway", ftos(g_keepaway));
 }
 
 void ReadGameCvars()
@@ -127,6 +129,8 @@ void ReadGameCvars()
                found += (g_race = (!found && (prev != GAME_RACE) && cvar("g_race")));
                found += (g_nexball = (!found && (prev != GAME_NEXBALL) && cvar("g_nexball")));
                found += (g_cts = (!found && (prev != GAME_CTS) && cvar("g_cts")));
+               found += (g_freezetag = (!found && (prev != GAME_FREEZETAG) && cvar("g_freezetag")));
+               found += (g_keepaway = (!found && (prev != GAME_KEEPAWAY) && cvar("g_keepaway")));
 
                if(found)
                        break;
@@ -317,6 +321,16 @@ void InitGameplayMode()
                MUTATOR_ADD(gamemode_keyhunt);
        }
 
+       if(g_freezetag)
+       {
+               game = GAME_FREEZETAG;
+               gamemode_name = "Freeze Tag";
+               ActivateTeamplay();
+               fraglimit_override = cvar("g_freezetag_point_limit");
+               leadlimit_override = cvar("g_freezetag_point_leadlimit");
+               MUTATOR_ADD(gamemode_freezetag);
+       }
+
        if(g_assault)
        {
                game = GAME_ASSAULT;
@@ -373,6 +387,13 @@ void InitGameplayMode()
                have_team_spawns = -1; // request team spawns
        }
 
+       if(g_keepaway)
+       {
+               game = GAME_KEEPAWAY;
+               gamemode_name = "Keepaway";
+               MUTATOR_ADD(gamemode_keepaway);
+       }
+
        if(teams_matter)
                entcs_init();
 
@@ -816,6 +837,8 @@ float FindSmallestTeam(entity pl, float ignore_pl)
                        error("Too few teams available for ctf\n");
                else if(g_keyhunt)
                        error("Too few teams available for key hunt\n");
+               else if(g_freezetag)
+                       error("Too few teams available for freeze tag\n");
                else
                        error("Too few teams available for team deathmatch\n");
        }
index 11f97f0..e7716fe 100644 (file)
@@ -232,7 +232,7 @@ void lgbeam_think()
                remove(self);
                return;
        }
-       if (self.owner.weaponentity.state != WS_INUSE || (self.owner.ammo_cells <= 0 && !(self.owner.items & IT_UNLIMITED_WEAPON_AMMO)) || self.owner.deadflag != DEAD_NO || !self.owner.BUTTON_ATCK)
+       if (self.owner.weaponentity.state != WS_INUSE || (self.owner.ammo_cells <= 0 && !(self.owner.items & IT_UNLIMITED_WEAPON_AMMO)) || self.owner.deadflag != DEAD_NO || !self.owner.BUTTON_ATCK || (g_freezetag && self.owner.freezetag_frozen))
        {
                if(self == self.owner.lgbeam)
                        self.owner.lgbeam = world;
diff --git a/scripts/ctf.shader b/scripts/ctf.shader
new file mode 100644 (file)
index 0000000..2eceff3
--- /dev/null
@@ -0,0 +1,18 @@
+models/ctf/glow_red
+{
+ {
+       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
+ }
+}
\ No newline at end of file
diff --git a/scripts/orbs.shader b/scripts/orbs.shader
new file mode 100644 (file)
index 0000000..26d1fdf
--- /dev/null
@@ -0,0 +1,48 @@
+models/orbs/orbblue
+{
+       deformVertexes autosprite
+       dpnoshadow
+
+ {
+       map models/orbs/orbblue.tga
+       blendfunc add
+       tcmod page 4 4 0.05
+       rgbgen vertex
+ }
+}
+models/orbs/orbred
+{
+       deformVertexes autosprite
+       dpnoshadow
+
+ {
+       map models/orbs/orbred.tga
+       blendfunc add
+       tcmod page 4 4 0.05
+       rgbgen vertex
+ }
+}
+models/orbs/orbyellow
+{
+       deformVertexes autosprite
+       dpnoshadow
+
+ {
+       map models/orbs/orbyellow.tga
+       blendfunc add
+       tcmod page 4 4 0.05
+       rgbgen vertex
+ }
+}
+models/orbs/orbpink
+{
+       deformVertexes autosprite
+       dpnoshadow
+
+ {
+       map models/orbs/orbpink.tga
+       blendfunc add
+       tcmod page 4 4 0.05
+       rgbgen vertex
+ }
+}
\ No newline at end of file
diff --git a/scripts/projectiles.shader b/scripts/projectiles.shader
new file mode 100644 (file)
index 0000000..28b1e94
--- /dev/null
@@ -0,0 +1,90 @@
+crylink_projectile_core
+{
+       surfaceparm nonsolid
+       surfaceparm nolightmap
+       deformVertexes autosprite
+       {
+               map textures/projectiles/crylink_projectile_core.tga
+               blendfunc add
+               rgbGen lightingDiffuse
+       }
+}
+crylink_projectile_long
+{
+       surfaceparm nonsolid
+       surfaceparm nolightmap
+       deformVertexes autosprite2
+       {
+               map textures/projectiles/crylink_projectile_long.tga
+               blendfunc add
+               rgbGen lightingDiffuse
+       }
+}
+hlac_projectile_core
+{
+       surfaceparm nonsolid
+       surfaceparm nolightmap
+       deformVertexes autosprite
+       {
+               map textures/projectiles/hlac_projectile_core.tga
+               blendfunc add
+               rgbGen lightingDiffuse
+       }
+}
+hlac_projectile_long
+{
+       surfaceparm nonsolid
+       surfaceparm nolightmap
+       deformVertexes autosprite2
+       {
+               map textures/projectiles/hlac_projectile_long.tga
+               blendfunc add
+               rgbGen lightingDiffuse
+       }
+}
+electro_projectile_core
+{
+       surfaceparm nonsolid
+       surfaceparm nolightmap
+       deformVertexes autosprite
+       {
+               map textures/projectiles/electro_projectile_core.tga
+               blendfunc add
+               tcmod page 4 1 0.1
+               rgbGen lightingDiffuse
+       }
+}
+electro_projectile_long
+{
+       surfaceparm nonsolid
+       surfaceparm nolightmap
+       deformVertexes autosprite2
+       {
+               map textures/projectiles/electro_projectile_long.tga
+               blendfunc add
+               tcmod page 4 1 0.1
+               rgbGen lightingDiffuse
+       }
+}
+laser_projectile_core
+{
+       surfaceparm nonsolid
+       surfaceparm nolightmap
+       deformVertexes autosprite
+       {
+               map textures/projectiles/laser_projectile_core.tga
+               blendfunc add
+               rgbGen lightingDiffuse
+       }
+}
+laser_projectile_long
+{
+       surfaceparm nonsolid
+       surfaceparm nolightmap
+       deformVertexes autosprite2
+       {
+               map textures/projectiles/laser_projectile_long.tga
+               blendfunc add
+               rgbGen lightingDiffuse
+       }
+}
\ No newline at end of file
index 2ed0c9a..c093eb3 100644 (file)
@@ -102,3 +102,11 @@ seeker
                rgbgen lightingDiffuse
        }
 }
+minelayer
+{
+       dpreflectcube cubemaps/default/sky
+       {
+               map textures/minelayer.tga
+               rgbgen lightingDiffuse
+       }
+}
diff --git a/sound/keepaway/dropped.wav b/sound/keepaway/dropped.wav
new file mode 100644 (file)
index 0000000..6867468
Binary files /dev/null and b/sound/keepaway/dropped.wav differ
diff --git a/sound/keepaway/pickedup.wav b/sound/keepaway/pickedup.wav
new file mode 100644 (file)
index 0000000..55e55a4
Binary files /dev/null and b/sound/keepaway/pickedup.wav differ
diff --git a/sound/keepaway/respawn.wav b/sound/keepaway/respawn.wav
new file mode 100644 (file)
index 0000000..0be02d5
Binary files /dev/null and b/sound/keepaway/respawn.wav differ
diff --git a/sound/keepaway/touch.wav b/sound/keepaway/touch.wav
new file mode 100644 (file)
index 0000000..2ab908b
Binary files /dev/null and b/sound/keepaway/touch.wav differ
diff --git a/textures/flags/flag_blue.tga b/textures/flags/flag_blue.tga
deleted file mode 100644 (file)
index e84fca5..0000000
Binary files a/textures/flags/flag_blue.tga and /dev/null differ
diff --git a/textures/flags/flag_blue_cloth.tga b/textures/flags/flag_blue_cloth.tga
deleted file mode 100644 (file)
index a8ea97f..0000000
Binary files a/textures/flags/flag_blue_cloth.tga and /dev/null differ
diff --git a/textures/flags/flag_blue_gloss.tga b/textures/flags/flag_blue_gloss.tga
deleted file mode 100644 (file)
index 77176c0..0000000
Binary files a/textures/flags/flag_blue_gloss.tga and /dev/null differ
diff --git a/textures/flags/flag_blue_glow.tga b/textures/flags/flag_blue_glow.tga
deleted file mode 100644 (file)
index 88d58c0..0000000
Binary files a/textures/flags/flag_blue_glow.tga and /dev/null differ
diff --git a/textures/flags/flag_blue_laser.tga b/textures/flags/flag_blue_laser.tga
deleted file mode 100644 (file)
index 6901f4f..0000000
Binary files a/textures/flags/flag_blue_laser.tga and /dev/null differ
diff --git a/textures/flags/flag_blue_norm.tga b/textures/flags/flag_blue_norm.tga
deleted file mode 100644 (file)
index fc26022..0000000
Binary files a/textures/flags/flag_blue_norm.tga and /dev/null differ
diff --git a/textures/flags/flag_red.tga b/textures/flags/flag_red.tga
deleted file mode 100644 (file)
index d659d8c..0000000
Binary files a/textures/flags/flag_red.tga and /dev/null differ
diff --git a/textures/flags/flag_red_cloth.tga b/textures/flags/flag_red_cloth.tga
deleted file mode 100644 (file)
index ae451c9..0000000
Binary files a/textures/flags/flag_red_cloth.tga and /dev/null differ
diff --git a/textures/flags/flag_red_gloss.tga b/textures/flags/flag_red_gloss.tga
deleted file mode 100644 (file)
index a499830..0000000
Binary files a/textures/flags/flag_red_gloss.tga and /dev/null differ
diff --git a/textures/flags/flag_red_glow.tga b/textures/flags/flag_red_glow.tga
deleted file mode 100644 (file)
index deae22e..0000000
Binary files a/textures/flags/flag_red_glow.tga and /dev/null differ
diff --git a/textures/flags/flag_red_laser.tga b/textures/flags/flag_red_laser.tga
deleted file mode 100644 (file)
index f4d4de5..0000000
Binary files a/textures/flags/flag_red_laser.tga and /dev/null differ
diff --git a/textures/flags/flag_red_norm.tga b/textures/flags/flag_red_norm.tga
deleted file mode 100644 (file)
index 97480bb..0000000
Binary files a/textures/flags/flag_red_norm.tga and /dev/null differ
diff --git a/textures/minelayer.tga b/textures/minelayer.tga
new file mode 100644 (file)
index 0000000..e0fa48c
Binary files /dev/null and b/textures/minelayer.tga differ
diff --git a/textures/minelayer_gloss.tga b/textures/minelayer_gloss.tga
new file mode 100644 (file)
index 0000000..2ca8b9a
Binary files /dev/null and b/textures/minelayer_gloss.tga differ
diff --git a/textures/minelayer_glow.tga b/textures/minelayer_glow.tga
new file mode 100644 (file)
index 0000000..163735f
Binary files /dev/null and b/textures/minelayer_glow.tga differ
diff --git a/textures/projectiles/crylink_projectile_core.tga b/textures/projectiles/crylink_projectile_core.tga
new file mode 100644 (file)
index 0000000..a9f686b
Binary files /dev/null and b/textures/projectiles/crylink_projectile_core.tga differ
diff --git a/textures/projectiles/crylink_projectile_core_glow.tga b/textures/projectiles/crylink_projectile_core_glow.tga
new file mode 100644 (file)
index 0000000..b53993b
Binary files /dev/null and b/textures/projectiles/crylink_projectile_core_glow.tga differ
diff --git a/textures/projectiles/crylink_projectile_long.tga b/textures/projectiles/crylink_projectile_long.tga
new file mode 100644 (file)
index 0000000..6f78dd0
Binary files /dev/null and b/textures/projectiles/crylink_projectile_long.tga differ
diff --git a/textures/projectiles/crylink_projectile_long_glow.tga b/textures/projectiles/crylink_projectile_long_glow.tga
new file mode 100644 (file)
index 0000000..6f78dd0
Binary files /dev/null and b/textures/projectiles/crylink_projectile_long_glow.tga differ
diff --git a/textures/projectiles/electro_projectile_core.tga b/textures/projectiles/electro_projectile_core.tga
new file mode 100644 (file)
index 0000000..b9a1930
Binary files /dev/null and b/textures/projectiles/electro_projectile_core.tga differ
diff --git a/textures/projectiles/electro_projectile_core_glow.tga b/textures/projectiles/electro_projectile_core_glow.tga
new file mode 100644 (file)
index 0000000..b9a1930
Binary files /dev/null and b/textures/projectiles/electro_projectile_core_glow.tga differ
diff --git a/textures/projectiles/electro_projectile_long.tga b/textures/projectiles/electro_projectile_long.tga
new file mode 100644 (file)
index 0000000..4672763
Binary files /dev/null and b/textures/projectiles/electro_projectile_long.tga differ
diff --git a/textures/projectiles/electro_projectile_long_glow.tga b/textures/projectiles/electro_projectile_long_glow.tga
new file mode 100644 (file)
index 0000000..4672763
Binary files /dev/null and b/textures/projectiles/electro_projectile_long_glow.tga differ
diff --git a/textures/projectiles/hlac_projectile_core.tga b/textures/projectiles/hlac_projectile_core.tga
new file mode 100644 (file)
index 0000000..f5a49e1
Binary files /dev/null and b/textures/projectiles/hlac_projectile_core.tga differ
diff --git a/textures/projectiles/hlac_projectile_core_glow.tga b/textures/projectiles/hlac_projectile_core_glow.tga
new file mode 100644 (file)
index 0000000..f5a49e1
Binary files /dev/null and b/textures/projectiles/hlac_projectile_core_glow.tga differ
diff --git a/textures/projectiles/hlac_projectile_long.tga b/textures/projectiles/hlac_projectile_long.tga
new file mode 100644 (file)
index 0000000..a7e68da
Binary files /dev/null and b/textures/projectiles/hlac_projectile_long.tga differ
diff --git a/textures/projectiles/hlac_projectile_long_glow.tga b/textures/projectiles/hlac_projectile_long_glow.tga
new file mode 100644 (file)
index 0000000..a7e68da
Binary files /dev/null and b/textures/projectiles/hlac_projectile_long_glow.tga differ
diff --git a/textures/projectiles/laser_projectile_core.tga b/textures/projectiles/laser_projectile_core.tga
new file mode 100644 (file)
index 0000000..1fdec98
Binary files /dev/null and b/textures/projectiles/laser_projectile_core.tga differ
diff --git a/textures/projectiles/laser_projectile_core_glow.tga b/textures/projectiles/laser_projectile_core_glow.tga
new file mode 100644 (file)
index 0000000..1fdec98
Binary files /dev/null and b/textures/projectiles/laser_projectile_core_glow.tga differ
diff --git a/textures/projectiles/laser_projectile_long.tga b/textures/projectiles/laser_projectile_long.tga
new file mode 100644 (file)
index 0000000..361342d
Binary files /dev/null and b/textures/projectiles/laser_projectile_long.tga differ
diff --git a/textures/projectiles/laser_projectile_long_glow.tga b/textures/projectiles/laser_projectile_long_glow.tga
new file mode 100644 (file)
index 0000000..361342d
Binary files /dev/null and b/textures/projectiles/laser_projectile_long_glow.tga differ