--- /dev/null
+// This config file is for overkill weapons that were nerfed to have the same
+// stats as vanilla weapons, secondary attack uses stats of vanilla blaster.
+
+// {{{ Overkill Shotgun
+set g_balance_okshotgun_primary_ammo 1
+set g_balance_okshotgun_primary_animtime 0.2
+set g_balance_okshotgun_primary_bot_range 512
+set g_balance_okshotgun_primary_bullets 12
+set g_balance_okshotgun_primary_damage 4
+set g_balance_okshotgun_primary_force 15
+set g_balance_okshotgun_primary_refire 0.75
+set g_balance_okshotgun_primary_solidpenetration 3.8
+set g_balance_okshotgun_primary_spread 0.12
+set g_balance_okshotgun_reload_ammo 0
+set g_balance_okshotgun_reload_time 2
+set g_balance_okshotgun_secondary_animtime 0.2
+set g_balance_okshotgun_secondary_damage 20
+set g_balance_okshotgun_secondary_delay 0
+set g_balance_okshotgun_secondary_edgedamage 10
+set g_balance_okshotgun_secondary_force 300
+set g_balance_okshotgun_secondary_lifetime 5
+set g_balance_okshotgun_secondary_radius 60
+set g_balance_okshotgun_secondary_refire 0.7
+set g_balance_okshotgun_secondary_refire_type 0
+set g_balance_okshotgun_secondary_shotangle 0
+set g_balance_okshotgun_secondary_speed 6000
+set g_balance_okshotgun_secondary_spread 0
+set g_balance_okshotgun_switchdelay_drop 0.2
+set g_balance_okshotgun_switchdelay_raise 0.2
+set g_balance_okshotgun_weaponreplace ""
+set g_balance_okshotgun_weaponstart 0
+set g_balance_okshotgun_weaponstartoverride -1
+set g_balance_okshotgun_weaponthrowable 1
+// }}}
+// {{{ Overkill Machine Gun
+set g_balance_okmachinegun_primary_ammo 1
+set g_balance_okmachinegun_primary_damage 10
+set g_balance_okmachinegun_primary_force 3
+set g_balance_okmachinegun_primary_refire 0.1
+set g_balance_okmachinegun_primary_solidpenetration 13.1
+set g_balance_okmachinegun_primary_spread_add 0.012
+set g_balance_okmachinegun_primary_spread_max 0.05
+set g_balance_okmachinegun_primary_spread_min 0.02
+set g_balance_okmachinegun_reload_ammo 60
+set g_balance_okmachinegun_reload_time 2
+set g_balance_okmachinegun_secondary_animtime 0.2
+set g_balance_okmachinegun_secondary_damage 20
+set g_balance_okmachinegun_secondary_delay 0
+set g_balance_okmachinegun_secondary_edgedamage 10
+set g_balance_okmachinegun_secondary_force 300
+set g_balance_okmachinegun_secondary_lifetime 5
+set g_balance_okmachinegun_secondary_radius 60
+set g_balance_okmachinegun_secondary_refire 0.7
+set g_balance_okmachinegun_secondary_refire_type 0
+set g_balance_okmachinegun_secondary_shotangle 0
+set g_balance_okmachinegun_secondary_speed 6000
+set g_balance_okmachinegun_secondary_spread 0
+set g_balance_okmachinegun_switchdelay_drop 0.2
+set g_balance_okmachinegun_switchdelay_raise 0.2
+set g_balance_okmachinegun_weaponreplace ""
+set g_balance_okmachinegun_weaponstart 0
+set g_balance_okmachinegun_weaponstartoverride -1
+set g_balance_okmachinegun_weaponthrowable 1
+// }}}
+// {{{ Overkill Vortex
+set g_balance_okvortex_charge 1
+set g_balance_okvortex_charge_animlimit 0.5
+set g_balance_okvortex_charge_limit 1
+set g_balance_okvortex_charge_maxspeed 800
+set g_balance_okvortex_charge_mindmg 40
+set g_balance_okvortex_charge_minspeed 400
+set g_balance_okvortex_charge_rate 0.6
+set g_balance_okvortex_charge_rot_pause 0
+set g_balance_okvortex_charge_rot_rate 0
+set g_balance_okvortex_charge_shot_multiplier 0
+set g_balance_okvortex_charge_start 0.5
+set g_balance_okvortex_charge_velocity_rate 0
+set g_balance_okvortex_primary_ammo 6
+set g_balance_okvortex_primary_animtime 0.4
+set g_balance_okvortex_primary_damage 80
+set g_balance_okvortex_primary_damagefalloff_forcehalflife 0
+set g_balance_okvortex_primary_damagefalloff_halflife 0
+set g_balance_okvortex_primary_damagefalloff_maxdist 0
+set g_balance_okvortex_primary_damagefalloff_mindist 0
+set g_balance_okvortex_primary_force 400
+set g_balance_okvortex_primary_refire 1.5
+set g_balance_okvortex_reload_ammo 0
+set g_balance_okvortex_reload_time 2
+set g_balance_okvortex_secondary 0
+set g_balance_okvortex_secondary_ammo 2
+set g_balance_okvortex_secondary_animtime 0
+set g_balance_okvortex_secondary_chargepool 0
+set g_balance_okvortex_secondary_chargepool_pause_regen 1
+set g_balance_okvortex_secondary_chargepool_regen 0.15
+set g_balance_okvortex_secondary_damage 0
+set g_balance_okvortex_secondary_damagefalloff_forcehalflife 0
+set g_balance_okvortex_secondary_damagefalloff_halflife 0
+set g_balance_okvortex_secondary_damagefalloff_maxdist 0
+set g_balance_okvortex_secondary_damagefalloff_mindist 0
+set g_balance_okvortex_secondary_force 0
+set g_balance_okvortex_secondary_refire 0
+set g_balance_okvortex_secondary_refire_type 0
+set g_balance_okvortex_secondary_delay 0
+set g_balance_okvortex_secondary_edgedamage 10
+set g_balance_okvortex_secondary_lifetime 5
+set g_balance_okvortex_secondary_radius 60
+set g_balance_okvortex_secondary_shotangle 0
+set g_balance_okvortex_secondary_speed 6000
+set g_balance_okvortex_secondary_spread 0
+set g_balance_okvortex_switchdelay_drop 0.2
+set g_balance_okvortex_switchdelay_raise 0.2
+set g_balance_okvortex_weaponreplace ""
+set g_balance_okvortex_weaponstart 0
+set g_balance_okvortex_weaponstartoverride -1
+set g_balance_okvortex_weaponthrowable 1
+// }}}
set g_balance_arc_weaponthrowable 1
// }}}
// {{{ #21: Heavy Machine Gun
-set g_balance_hmg_ammo 1
-set g_balance_hmg_damage 30
-set g_balance_hmg_force 10
-set g_balance_hmg_refire 0.05
+set g_balance_hmg_primary_ammo 1
+set g_balance_hmg_primary_damage 30
+set g_balance_hmg_primary_force 10
+set g_balance_hmg_primary_refire 0.05
+set g_balance_hmg_primary_solidpenetration 32
+set g_balance_hmg_primary_spread_add 0.005
+set g_balance_hmg_primary_spread_max 0.06
+set g_balance_hmg_primary_spread_min 0.01
set g_balance_hmg_reload_ammo 120
set g_balance_hmg_reload_time 1
-set g_balance_hmg_solidpenetration 32
-set g_balance_hmg_spread_add 0.005
-set g_balance_hmg_spread_max 0.06
-set g_balance_hmg_spread_min 0.01
set g_balance_hmg_switchdelay_drop 0.2
set g_balance_hmg_switchdelay_raise 0.2
set g_balance_hmg_weaponreplace ""
set g_balance_hmg_weaponthrowable 0
// }}}
// {{{ #22: Rocket Propelled Chainsaw
-set g_balance_rpc_ammo 10
-set g_balance_rpc_animtime 1
-set g_balance_rpc_damage 150
-set g_balance_rpc_damage2 500
-set g_balance_rpc_damageforcescale 2
-set g_balance_rpc_edgedamage 50
-set g_balance_rpc_force 400
-set g_balance_rpc_health 25
-set g_balance_rpc_lifetime 30
-set g_balance_rpc_radius 300
-set g_balance_rpc_refire 1
+set g_balance_rpc_primary_ammo 10
+set g_balance_rpc_primary_animtime 1
+set g_balance_rpc_primary_damage 150
+set g_balance_rpc_primary_damage2 500
+set g_balance_rpc_primary_damageforcescale 2
+set g_balance_rpc_primary_edgedamage 50
+set g_balance_rpc_primary_force 400
+set g_balance_rpc_primary_health 25
+set g_balance_rpc_primary_lifetime 30
+set g_balance_rpc_primary_radius 300
+set g_balance_rpc_primary_refire 1
+set g_balance_rpc_primary_speed 2500
+set g_balance_rpc_primary_speedaccel 5000
set g_balance_rpc_reload_ammo 10
set g_balance_rpc_reload_time 1
-set g_balance_rpc_speed 2500
-set g_balance_rpc_speedaccel 5000
set g_balance_rpc_switchdelay_drop 0.2
set g_balance_rpc_switchdelay_raise 0.2
set g_balance_rpc_weaponreplace ""
set g_balance_rpc_weaponstartoverride 0
set g_balance_rpc_weaponthrowable 0
// }}}
+// {{{ Overkill Shotgun
+set g_balance_okshotgun_primary_ammo 3
+set g_balance_okshotgun_primary_animtime 0.65
+set g_balance_okshotgun_primary_bot_range 512
+set g_balance_okshotgun_primary_bullets 10
+set g_balance_okshotgun_primary_damage 17
+set g_balance_okshotgun_primary_force 80
+set g_balance_okshotgun_primary_refire 0.75
+set g_balance_okshotgun_primary_solidpenetration 3.8
+set g_balance_okshotgun_primary_spread 0.07
+set g_balance_okshotgun_reload_ammo 24
+set g_balance_okshotgun_reload_time 2
+set g_balance_okshotgun_secondary_animtime 0.2
+set g_balance_okshotgun_secondary_damage 25
+set g_balance_okshotgun_secondary_delay 0
+set g_balance_okshotgun_secondary_edgedamage 12.5
+set g_balance_okshotgun_secondary_force 300
+set g_balance_okshotgun_secondary_lifetime 5
+set g_balance_okshotgun_secondary_radius 70
+set g_balance_okshotgun_secondary_refire 0.7
+set g_balance_okshotgun_secondary_refire_type 1
+set g_balance_okshotgun_secondary_shotangle 0
+set g_balance_okshotgun_secondary_speed 6000
+set g_balance_okshotgun_secondary_spread 0
+set g_balance_okshotgun_switchdelay_drop 0.2
+set g_balance_okshotgun_switchdelay_raise 0.2
+set g_balance_okshotgun_weaponreplace ""
+set g_balance_okshotgun_weaponstart 0
+set g_balance_okshotgun_weaponstartoverride -1
+set g_balance_okshotgun_weaponthrowable 1
+// }}}
+// {{{ Overkill Machine Gun
+set g_balance_okmachinegun_primary_ammo 1
+set g_balance_okmachinegun_primary_damage 25
+set g_balance_okmachinegun_primary_force 5
+set g_balance_okmachinegun_primary_refire 0.1
+set g_balance_okmachinegun_primary_solidpenetration 13.1
+set g_balance_okmachinegun_primary_spread_add 0.012
+set g_balance_okmachinegun_primary_spread_max 0.05
+set g_balance_okmachinegun_primary_spread_min 0
+set g_balance_okmachinegun_reload_ammo 30
+set g_balance_okmachinegun_reload_time 1.5
+set g_balance_okmachinegun_secondary_animtime 0.2
+set g_balance_okmachinegun_secondary_damage 25
+set g_balance_okmachinegun_secondary_delay 0
+set g_balance_okmachinegun_secondary_edgedamage 12.5
+set g_balance_okmachinegun_secondary_force 300
+set g_balance_okmachinegun_secondary_lifetime 5
+set g_balance_okmachinegun_secondary_radius 70
+set g_balance_okmachinegun_secondary_refire 0.7
+set g_balance_okmachinegun_secondary_refire_type 1
+set g_balance_okmachinegun_secondary_shotangle 0
+set g_balance_okmachinegun_secondary_speed 6000
+set g_balance_okmachinegun_secondary_spread 0
+set g_balance_okmachinegun_switchdelay_drop 0.2
+set g_balance_okmachinegun_switchdelay_raise 0.2
+set g_balance_okmachinegun_weaponreplace ""
+set g_balance_okmachinegun_weaponstart 0
+set g_balance_okmachinegun_weaponstartoverride -1
+set g_balance_okmachinegun_weaponthrowable 1
+// }}}
+// {{{ Overkill Vortex
+set g_balance_okvortex_charge 0
+set g_balance_okvortex_charge_animlimit 0.5
+set g_balance_okvortex_charge_limit 1
+set g_balance_okvortex_charge_maxspeed 800
+set g_balance_okvortex_charge_mindmg 40
+set g_balance_okvortex_charge_minspeed 400
+set g_balance_okvortex_charge_rate 0.6
+set g_balance_okvortex_charge_rot_pause 0
+set g_balance_okvortex_charge_rot_rate 0
+set g_balance_okvortex_charge_shot_multiplier 0
+set g_balance_okvortex_charge_start 0.5
+set g_balance_okvortex_charge_velocity_rate 0
+set g_balance_okvortex_primary_ammo 10
+set g_balance_okvortex_primary_animtime 0.65
+set g_balance_okvortex_primary_damage 100
+set g_balance_okvortex_primary_damagefalloff_forcehalflife 0
+set g_balance_okvortex_primary_damagefalloff_halflife 0
+set g_balance_okvortex_primary_damagefalloff_maxdist 0
+set g_balance_okvortex_primary_damagefalloff_mindist 0
+set g_balance_okvortex_primary_force 500
+set g_balance_okvortex_primary_refire 1
+set g_balance_okvortex_reload_ammo 50
+set g_balance_okvortex_reload_time 2
+set g_balance_okvortex_secondary 2
+set g_balance_okvortex_secondary_ammo 0
+set g_balance_okvortex_secondary_animtime 0.2
+set g_balance_okvortex_secondary_chargepool 0
+set g_balance_okvortex_secondary_chargepool_pause_regen 1
+set g_balance_okvortex_secondary_chargepool_regen 0.15
+set g_balance_okvortex_secondary_damage 25
+set g_balance_okvortex_secondary_damagefalloff_forcehalflife 0
+set g_balance_okvortex_secondary_damagefalloff_halflife 0
+set g_balance_okvortex_secondary_damagefalloff_maxdist 0
+set g_balance_okvortex_secondary_damagefalloff_mindist 0
+set g_balance_okvortex_secondary_force 300
+set g_balance_okvortex_secondary_refire 0.7
+set g_balance_okvortex_secondary_refire_type 1
+set g_balance_okvortex_secondary_delay 0
+set g_balance_okvortex_secondary_edgedamage 12.5
+set g_balance_okvortex_secondary_lifetime 5
+set g_balance_okvortex_secondary_radius 70
+set g_balance_okvortex_secondary_shotangle 0
+set g_balance_okvortex_secondary_speed 6000
+set g_balance_okvortex_secondary_spread 0
+set g_balance_okvortex_switchdelay_drop 0.2
+set g_balance_okvortex_switchdelay_raise 0.2
+set g_balance_okvortex_weaponreplace ""
+set g_balance_okvortex_weaponstart 0
+set g_balance_okvortex_weaponstartoverride -1
+set g_balance_okvortex_weaponthrowable 1
+// }}}
set g_balance_arc_weaponthrowable 1
// }}}
// {{{ #21: Heavy Machine Gun
-set g_balance_hmg_ammo 1
-set g_balance_hmg_damage 30
-set g_balance_hmg_force 10
-set g_balance_hmg_refire 0.05
+set g_balance_hmg_primary_ammo 1
+set g_balance_hmg_primary_damage 30
+set g_balance_hmg_primary_force 10
+set g_balance_hmg_primary_refire 0.05
+set g_balance_hmg_primary_solidpenetration 32
+set g_balance_hmg_primary_spread_add 0.005
+set g_balance_hmg_primary_spread_max 0.06
+set g_balance_hmg_primary_spread_min 0.01
set g_balance_hmg_reload_ammo 120
set g_balance_hmg_reload_time 1
-set g_balance_hmg_solidpenetration 32
-set g_balance_hmg_spread_add 0.005
-set g_balance_hmg_spread_max 0.06
-set g_balance_hmg_spread_min 0.01
+set g_balance_hmg_secondary_ammo 0
+set g_balance_hmg_secondary_animtime 0.2
+set g_balance_hmg_secondary_damage 25
+set g_balance_hmg_secondary_delay 0
+set g_balance_hmg_secondary_edgedamage 12.5
+set g_balance_hmg_secondary_force 300
+set g_balance_hmg_secondary_lifetime 5
+set g_balance_hmg_secondary_radius 70
+set g_balance_hmg_secondary_refire 0.7
+set g_balance_hmg_secondary_refire_type 1
+set g_balance_hmg_secondary_shotangle 0
+set g_balance_hmg_secondary_speed 6000
+set g_balance_hmg_secondary_spread 0
set g_balance_hmg_switchdelay_drop 0.2
set g_balance_hmg_switchdelay_raise 0.2
set g_balance_hmg_weaponreplace ""
set g_balance_hmg_weaponthrowable 0
// }}}
// {{{ #22: Rocket Propelled Chainsaw
-set g_balance_rpc_ammo 10
-set g_balance_rpc_animtime 1
-set g_balance_rpc_damage 150
-set g_balance_rpc_damage2 500
-set g_balance_rpc_damageforcescale 2
-set g_balance_rpc_edgedamage 50
-set g_balance_rpc_force 400
-set g_balance_rpc_health 25
-set g_balance_rpc_lifetime 30
-set g_balance_rpc_radius 300
-set g_balance_rpc_refire 1
+set g_balance_rpc_primary_ammo 10
+set g_balance_rpc_primary_animtime 1
+set g_balance_rpc_primary_damage 150
+set g_balance_rpc_primary_damage2 500
+set g_balance_rpc_primary_damageforcescale 2
+set g_balance_rpc_primary_edgedamage 50
+set g_balance_rpc_primary_force 400
+set g_balance_rpc_primary_health 25
+set g_balance_rpc_primary_lifetime 30
+set g_balance_rpc_primary_radius 300
+set g_balance_rpc_primary_refire 1
+set g_balance_rpc_primary_speed 2500
+set g_balance_rpc_primary_speedaccel 5000
set g_balance_rpc_reload_ammo 10
set g_balance_rpc_reload_time 1
-set g_balance_rpc_speed 2500
-set g_balance_rpc_speedaccel 5000
+set g_balance_rpc_secondary_ammo 0
+set g_balance_rpc_secondary_animtime 0.2
+set g_balance_rpc_secondary_damage 25
+set g_balance_rpc_secondary_delay 0
+set g_balance_rpc_secondary_edgedamage 12.5
+set g_balance_rpc_secondary_force 300
+set g_balance_rpc_secondary_lifetime 5
+set g_balance_rpc_secondary_radius 70
+set g_balance_rpc_secondary_refire 0.7
+set g_balance_rpc_secondary_refire_type 1
+set g_balance_rpc_secondary_shotangle 0
+set g_balance_rpc_secondary_speed 6000
+set g_balance_rpc_secondary_spread 0
set g_balance_rpc_switchdelay_drop 0.2
set g_balance_rpc_switchdelay_raise 0.2
set g_balance_rpc_weaponreplace ""
set g_balance_rpc_weaponstartoverride 0
set g_balance_rpc_weaponthrowable 0
// }}}
+// {{{ Overkill Shotgun
+set g_balance_okshotgun_primary_ammo 3
+set g_balance_okshotgun_primary_animtime 0.65
+set g_balance_okshotgun_primary_bot_range 512
+set g_balance_okshotgun_primary_bullets 10
+set g_balance_okshotgun_primary_damage 17
+set g_balance_okshotgun_primary_force 80
+set g_balance_okshotgun_primary_refire 0.75
+set g_balance_okshotgun_primary_solidpenetration 3.8
+set g_balance_okshotgun_primary_spread 0.07
+set g_balance_okshotgun_reload_ammo 24
+set g_balance_okshotgun_reload_time 2
+set g_balance_okshotgun_secondary_animtime 0.2
+set g_balance_okshotgun_secondary_damage 25
+set g_balance_okshotgun_secondary_delay 0
+set g_balance_okshotgun_secondary_edgedamage 12.5
+set g_balance_okshotgun_secondary_force 300
+set g_balance_okshotgun_secondary_lifetime 5
+set g_balance_okshotgun_secondary_radius 70
+set g_balance_okshotgun_secondary_refire 0.7
+set g_balance_okshotgun_secondary_refire_type 1
+set g_balance_okshotgun_secondary_shotangle 0
+set g_balance_okshotgun_secondary_speed 6000
+set g_balance_okshotgun_secondary_spread 0
+set g_balance_okshotgun_switchdelay_drop 0.2
+set g_balance_okshotgun_switchdelay_raise 0.2
+set g_balance_okshotgun_weaponreplace ""
+set g_balance_okshotgun_weaponstart 0
+set g_balance_okshotgun_weaponstartoverride -1
+set g_balance_okshotgun_weaponthrowable 1
+// }}}
+// {{{ Overkill Machine Gun
+set g_balance_okmachinegun_primary_ammo 1
+set g_balance_okmachinegun_primary_damage 25
+set g_balance_okmachinegun_primary_force 5
+set g_balance_okmachinegun_primary_refire 0.1
+set g_balance_okmachinegun_primary_solidpenetration 13.1
+set g_balance_okmachinegun_primary_spread_add 0.012
+set g_balance_okmachinegun_primary_spread_max 0.05
+set g_balance_okmachinegun_primary_spread_min 0
+set g_balance_okmachinegun_reload_ammo 30
+set g_balance_okmachinegun_reload_time 1.5
+set g_balance_okmachinegun_secondary_animtime 0.2
+set g_balance_okmachinegun_secondary_damage 25
+set g_balance_okmachinegun_secondary_delay 0
+set g_balance_okmachinegun_secondary_edgedamage 12.5
+set g_balance_okmachinegun_secondary_force 300
+set g_balance_okmachinegun_secondary_lifetime 5
+set g_balance_okmachinegun_secondary_radius 70
+set g_balance_okmachinegun_secondary_refire 0.7
+set g_balance_okmachinegun_secondary_refire_type 1
+set g_balance_okmachinegun_secondary_shotangle 0
+set g_balance_okmachinegun_secondary_speed 6000
+set g_balance_okmachinegun_secondary_spread 0
+set g_balance_okmachinegun_switchdelay_drop 0.2
+set g_balance_okmachinegun_switchdelay_raise 0.2
+set g_balance_okmachinegun_weaponreplace ""
+set g_balance_okmachinegun_weaponstart 0
+set g_balance_okmachinegun_weaponstartoverride -1
+set g_balance_okmachinegun_weaponthrowable 1
+// }}}
+// {{{ Overkill Vortex
+set g_balance_okvortex_charge 0
+set g_balance_okvortex_charge_animlimit 0.5
+set g_balance_okvortex_charge_limit 1
+set g_balance_okvortex_charge_maxspeed 800
+set g_balance_okvortex_charge_mindmg 40
+set g_balance_okvortex_charge_minspeed 400
+set g_balance_okvortex_charge_rate 0.6
+set g_balance_okvortex_charge_rot_pause 0
+set g_balance_okvortex_charge_rot_rate 0
+set g_balance_okvortex_charge_shot_multiplier 0
+set g_balance_okvortex_charge_start 0.5
+set g_balance_okvortex_charge_velocity_rate 0
+set g_balance_okvortex_primary_ammo 10
+set g_balance_okvortex_primary_animtime 0.65
+set g_balance_okvortex_primary_damage 100
+set g_balance_okvortex_primary_damagefalloff_forcehalflife 0
+set g_balance_okvortex_primary_damagefalloff_halflife 0
+set g_balance_okvortex_primary_damagefalloff_maxdist 0
+set g_balance_okvortex_primary_damagefalloff_mindist 0
+set g_balance_okvortex_primary_force 500
+set g_balance_okvortex_primary_refire 1
+set g_balance_okvortex_reload_ammo 50
+set g_balance_okvortex_reload_time 2
+set g_balance_okvortex_secondary 2
+set g_balance_okvortex_secondary_ammo 0
+set g_balance_okvortex_secondary_animtime 0.2
+set g_balance_okvortex_secondary_chargepool 0
+set g_balance_okvortex_secondary_chargepool_pause_regen 1
+set g_balance_okvortex_secondary_chargepool_regen 0.15
+set g_balance_okvortex_secondary_damage 25
+set g_balance_okvortex_secondary_damagefalloff_forcehalflife 0
+set g_balance_okvortex_secondary_damagefalloff_halflife 0
+set g_balance_okvortex_secondary_damagefalloff_maxdist 0
+set g_balance_okvortex_secondary_damagefalloff_mindist 0
+set g_balance_okvortex_secondary_force 300
+set g_balance_okvortex_secondary_refire 0.7
+set g_balance_okvortex_secondary_refire_type 1
+set g_balance_okvortex_secondary_delay 0
+set g_balance_okvortex_secondary_edgedamage 12.5
+set g_balance_okvortex_secondary_lifetime 5
+set g_balance_okvortex_secondary_radius 70
+set g_balance_okvortex_secondary_shotangle 0
+set g_balance_okvortex_secondary_speed 6000
+set g_balance_okvortex_secondary_spread 0
+set g_balance_okvortex_switchdelay_drop 0.2
+set g_balance_okvortex_switchdelay_raise 0.2
+set g_balance_okvortex_weaponreplace ""
+set g_balance_okvortex_weaponstart 0
+set g_balance_okvortex_weaponstartoverride -1
+set g_balance_okvortex_weaponthrowable 1
+// }}}
set menu_use_default_hostname 1
alias sethostname "set menu_use_default_hostname 0; hostname $*"
-seta cl_weaponpriority "vaporizer hmg rpc vortex fireball mortar machinegun hagar rifle arc electro devastator crylink minelayer shotgun shockwave hlac tuba blaster porto seeker hook" "weapon priority list"
+seta cl_weaponpriority "vaporizer hmg rpc okvortex vortex fireball mortar okmachinegun machinegun hagar rifle arc electro devastator crylink minelayer okshotgun shotgun shockwave hlac tuba blaster porto seeker hook" "weapon priority list"
seta cl_weaponpriority_useforcycling 0 "when set, weapon cycling by the mouse wheel makes use of the weapon priority list (the special value 2 uses the weapon ID list for cycling)"
seta cl_weaponpriority0 "rpc devastator mortar hagar seeker fireball" "use weapon_priority_0_prev for prev gun from this list, weapon_priority_0_best for best gun, weapon_priority_0_next for next gun. Default value: explosives"
-seta cl_weaponpriority1 "vaporizer vortex crylink hlac arc electro blaster shockwave" "use weapon_priority_1_prev for prev gun from this list, weapon_priority_1_best for best gun, weapon_priority_1_next for next gun. Default value: energy"
-seta cl_weaponpriority2 "vaporizer vortex rifle" "use weapon_priority_2_prev for prev gun from this list, weapon_priority_2_best for best gun, weapon_priority_2_next for next gun. Default value: hitscan exact"
-seta cl_weaponpriority3 "vaporizer hmg vortex rifle machinegun shotgun shockwave" "use weapon_priority_3_prev for prev gun from this list, weapon_priority_3_best for best gun, weapon_priority_3_next for next gun. Default value: hitscan all"
-seta cl_weaponpriority4 "mortar minelayer hlac hagar crylink seeker shotgun shockwave" "use weapon_priority_4_prev for prev gun from this list, weapon_priority_4_best for best gun, weapon_priority_4_next for next gun. Default value: spam weapons"
+seta cl_weaponpriority1 "vaporizer okvortex vortex crylink hlac arc electro blaster shockwave" "use weapon_priority_1_prev for prev gun from this list, weapon_priority_1_best for best gun, weapon_priority_1_next for next gun. Default value: energy"
+seta cl_weaponpriority2 "vaporizer okvortex vortex rifle" "use weapon_priority_2_prev for prev gun from this list, weapon_priority_2_best for best gun, weapon_priority_2_next for next gun. Default value: hitscan exact"
+seta cl_weaponpriority3 "vaporizer hmg okvortex vortex rifle okmachinegun machinegun okshotgun shotgun shockwave" "use weapon_priority_3_prev for prev gun from this list, weapon_priority_3_best for best gun, weapon_priority_3_next for next gun. Default value: hitscan all"
+seta cl_weaponpriority4 "mortar minelayer hlac hagar crylink seeker okshotgun shotgun shockwave" "use weapon_priority_4_prev for prev gun from this list, weapon_priority_4_best for best gun, weapon_priority_4_next for next gun. Default value: spam weapons"
seta cl_weaponpriority5 "blaster shockwave hook porto" "use weapon_priority_5_prev for prev gun from this list, weapon_priority_5_best for best gun, weapon_priority_5_next for next gun. Default value: weapons for moving"
seta cl_weaponpriority6 "" "use weapon_priority_6_prev for prev gun from this list, weapon_priority_6_best for best gun, weapon_priority_6_next for next gun"
seta cl_weaponpriority7 "" "use weapon_priority_7_prev for prev gun from this list, weapon_priority_7_best for best gun, weapon_priority_7_next for next gun"
set bot_ai_aimskill_offset 0.3 "Amount of error induced to the bots aim"
set bot_ai_aimskill_think 1 "Aiming velocity. Use values below 1 for slower aiming"
set bot_ai_custom_weapon_priority_distances "300 850" "Define close and far distances in any order. Based on the distance to the enemy bots will choose different weapons"
-set bot_ai_custom_weapon_priority_far "vaporizer vortex rifle electro devastator mortar hagar hlac crylink blaster machinegun fireball seeker shotgun shockwave tuba minelayer" "Desired weapons for far distances ordered by priority"
-set bot_ai_custom_weapon_priority_mid "vaporizer devastator vortex fireball seeker mortar electro machinegun arc crylink hlac hagar shotgun shockwave blaster rifle tuba minelayer" "Desired weapons for middle distances ordered by priority"
-set bot_ai_custom_weapon_priority_close "vaporizer vortex shotgun shockwave machinegun arc hlac tuba seeker hagar crylink mortar electro devastator blaster fireball rifle minelayer" "Desired weapons for close distances ordered by priority"
+set bot_ai_custom_weapon_priority_far "vaporizer okvortex vortex rifle electro devastator mortar hagar hlac crylink blaster okmachinegun machinegun fireball seeker okshotgun shotgun shockwave tuba minelayer" "Desired weapons for far distances ordered by priority"
+set bot_ai_custom_weapon_priority_mid "vaporizer devastator okvortex vortex fireball seeker mortar electro okmachinegun machinegun arc crylink hlac hagar okshotgun shotgun shockwave blaster rifle tuba minelayer" "Desired weapons for middle distances ordered by priority"
+set bot_ai_custom_weapon_priority_close "vaporizer okvortex vortex okshotgun shotgun shockwave okmachinegun machinegun arc hlac tuba seeker hagar crylink mortar electro devastator blaster fireball rifle minelayer" "Desired weapons for close distances ordered by priority"
set bot_ai_weapon_combo 1 "Enable bots to do weapon combos"
set bot_ai_weapon_combo_threshold 0.4 "Try to make a combo N seconds after the last attack"
set bot_ai_friends_aware_pickup_radius "500" "Bots will not pickup items if a team mate is this distance near the item"
alias sv_hook_gamestart_ka
alias sv_hook_gamestart_ft
alias sv_hook_gamestart_inv
+alias sv_hook_gamestart_gg
alias sv_hook_gamerestart
alias sv_hook_gameend
alias sv_vote_gametype_hook_dm
alias sv_vote_gametype_hook_dom
alias sv_vote_gametype_hook_ft
+alias sv_vote_gametype_hook_gg
alias sv_vote_gametype_hook_inv
alias sv_vote_gametype_hook_ka
alias sv_vote_gametype_hook_kh
set g_inv_respawn_delay_max 0
set g_inv_respawn_waves 0
set g_inv_weapon_stay 0
-
+set g_gg_respawn_delay_small 0
+set g_gg_respawn_delay_small_count 0
+set g_gg_respawn_delay_large 0
+set g_gg_respawn_delay_large_count 0
+set g_gg_respawn_delay_max 0
+set g_gg_respawn_waves 0
+set g_gg_weapon_stay 0
// =========
// assault
set g_invasion_teams 0 "number of teams in invasion (note: use mapinfo to set this)"
set g_invasion_team_spawns 1 "use team spawns in teamplay invasion mode"
set g_invasion_type 0 "type of invasion mode - 0: round-based, 1: hunting, 2: complete the stage (note: use mapinfo to set this)"
+
+// =========
+// gungame
+// =========
+set g_gg 0 "GunGame: Kill players with all weapons"
+set g_gg_weapons "vortex mortar machinegun hagar arc electro devastator crylink shotgun blaster"
+set g_gg_kills_per_weapon 3 "Number of kills needed to advance to the next weapon"
+
+exec survival.cfg // Lyberta: surv
--- /dev/null
+// Player template example
+// This config file defines a player template named "example" that has vanilla
+// behavior. It is intended to be a starting point for making your own player
+// templates. Just copy the variables and replace "example" with your own name.
+
+// Spawn section
+// Variables in this section define what happens during spawn.
+set g_player_template_example_start_health "default" "How much health does player get during spawn"
+set g_player_template_example_start_armor "default" "How much armor does player get during spawn"
+set g_player_template_example_unlimited_ammo "default" "Whether to give player unlimited ammo"
+set g_player_template_example_start_ammo_shells "default" "How much shells does player get during spawn"
+set g_player_template_example_start_ammo_bullets "default" "How much bullets does player get during spawn"
+set g_player_template_example_start_ammo_rockets "default" "How much rockets does player get during spawn"
+set g_player_template_example_start_ammo_cells "default" "How much cells does player get during spawn"
+// These weapons are given to all players by default. They are usually blaster and shotgun.
+set g_player_template_example_default_start_weapons "default" "Whether to give player default start weapons"
+// These weapons will be given unconditionally.
+set g_player_template_example_start_weapons "default" "Which weapons does player get during spawn"
+// These weapons will be given randomly. The player will get the exact amount of
+// random weapons. The code will make sure that they are not duplicate.
+set g_player_template_example_random_start_weapons_count "default" "Number of weapons that can be randomly given to player during spawn"
+set g_player_template_example_random_start_weapons "default" "Weapons that can be randomly given to player during spawn"
+set g_player_template_example_random_start_shells "default" "How much shells does the player get with random start shell-based weapon"
+set g_player_template_example_random_start_bullets "default" "How much bullets does the player get with random start bullet-based weapon"
+set g_player_template_example_random_start_rockets "default" "How much rockets does the player get with random start rocket-based weapon"
+set g_player_template_example_random_start_cells "default" "How much cells does the player get with random start cell-based weapon"
+
+set g_player_template_example_drop_weapons "default" "Whether the player can drop weapons by throwing them or by dying"
+
+// Item pickup section
+// Variables in this section define what happens during item pickup.
+// Player can get health, armor and/or ammo on top of or instead of an item.
+// The format of the value is:
+// "<add|override> [health] [armor] [shells] [bullets] [rockets] [cells] [plasma] [fuel]"
+// For example "add 50" will give 50 health and the original item.
+// "override 0 0 5" will give 5 shells instead of the item.
+set g_player_template_example_pickup_health_small "default" "What items does player get when they pickup small health"
+set g_player_template_example_pickup_health_medium "default" "What items does player get when they pickup medium health"
+set g_player_template_example_pickup_health_big "default" "What items does player get when they pickup big health"
+set g_player_template_example_pickup_health_mega "default" "What items does player get when they pickup mega health"
+set g_player_template_example_pickup_armor_small "default" "What items does player get when they pickup small armor"
+set g_player_template_example_pickup_armor_medium "default" "What items does player get when they pickup medium armor"
+set g_player_template_example_pickup_armor_big "default" "What items does player get when they pickup big armor"
+set g_player_template_example_pickup_armor_mega "default" "What items does player get when they pickup mega armor"
+set g_player_template_example_pickup_item_shells "default" "What items does player get when they pickup shells"
+set g_player_template_example_pickup_item_bullets "default" "What items does player get when they pickup bullets"
+set g_player_template_example_pickup_item_rockets "default" "What items does player get when they pickup rockets"
+set g_player_template_example_pickup_item_cells "default" "What items does player get when they pickup cells"
+set g_player_template_example_pickup_weapon_machinegun "default" "What items does player get when they pickup a machinegun"
+set g_player_template_example_pickup_weapon_mortar "default" "What items does player get when they pickup a mortar"
+set g_player_template_example_pickup_weapon_electro "default" "What items does player get when they pickup an electro"
+set g_player_template_example_pickup_weapon_crylink "default" "What items does player get when they pickup a crylink"
+set g_player_template_example_pickup_weapon_vortex "default" "What items does player get when they pickup a vortex"
+set g_player_template_example_pickup_weapon_hagar "default" "What items does player get when they pickup a hagar"
+set g_player_template_example_pickup_weapon_devastator "default" "What items does player get when they pickup a devastator"
+set g_player_template_example_pickup_weapon_minelayer "default" "What items does player get when they pickup a mine layer"
+set g_player_template_example_pickup_weapon_hlac "default" "What items does player get when they pickup a HLAC"
+set g_player_template_example_pickup_weapon_rifle "default" "What items does player get when they pickup a rifle"
+set g_player_template_example_pickup_weapon_seeker "default" "What items does player get when they pickup a TAG seeker"
+// These weapons were dropped by other players.
+set g_player_template_example_pickup_weapon_dropped_shotgun "default" "What items does player get when they pickup a dropped shotgun"
+set g_player_template_example_pickup_weapon_dropped_machinegun "default" "What items does player get when they pickup a dropped machinegun"
+set g_player_template_example_pickup_weapon_dropped_mortar "default" "What items does player get when they pickup a dropped mortar"
+set g_player_template_example_pickup_weapon_dropped_electro "default" "What items does player get when they pickup a dropped electro"
+set g_player_template_example_pickup_weapon_dropped_crylink "default" "What items does player get when they pickup a dropped crylink"
+set g_player_template_example_pickup_weapon_dropped_vortex "default" "What items does player get when they pickup a dropped vortex"
+set g_player_template_example_pickup_weapon_dropped_hagar "default" "What items does player get when they pickup a dropped hagar"
+set g_player_template_example_pickup_weapon_dropped_devastator "default" "What items does player get when they pickup a dropped devastator"
+set g_player_template_example_pickup_weapon_dropped_minelayer "default" "What items does player get when they pickup a dropped mine layer"
+set g_player_template_example_pickup_weapon_dropped_hlac "default" "What items does player get when they pickup a dropped HLAC"
+set g_player_template_example_pickup_weapon_dropped_rifle "default" "What items does player get when they pickup a dropped rifle"
+set g_player_template_example_pickup_weapon_dropped_seeker "default" "What items does player get when they pickup a dropped TAG seeker"
+set g_player_template_example_pickup_item_strength "default" "What items does player get when they pickup strength"
+set g_player_template_example_pickup_item_shield "default" "What items does player get when they pickup shield"
+
+// Damage section
+// Variables in this section define what happens during damage.
+set g_player_template_example_attack_scale "default" "How much player damages others. Higher values mean more damage"
+set g_player_template_example_defense_scale "default" "How much player gets damaged. Higher values mean less damage"
+set g_player_template_example_blaster_self_damage "default" "Whether player gets damaged with their own blaster"
}
}
+void HUD_Mod_GG(vector pos, vector mySize)
+{
+ mod_active = 1; // required in each mod function that always shows something
+ int stat_weapon = STAT(GUNGAME_LEADING_WEAPON);
+ vector pic_pos, pic_size;
+ if (mySize.x > mySize.y)
+ {
+ pic_pos = pos + eX * 0.25 * mySize.x;
+ pic_size = vec2(0.5 * mySize.x, mySize.y);
+ }
+ else
+ {
+ pic_pos = pos + eY * 0.25 * mySize.y;
+ pic_size = vec2(mySize.x, 0.5 * mySize.y);
+ }
+ string weapon_pic = string_null;
+ FOREACH(Weapons, it != WEP_Null,
+ {
+ if (it.m_id == stat_weapon)
+ {
+ weapon_pic = it.model2;
+ break;
+ }
+ });
+ if (!weapon_pic)
+ {
+ return;
+ }
+ drawpic_aspect_skin(pic_pos, weapon_pic, pic_size, '1 1 1', 1,
+ DRAWFLAG_NORMAL);
+}
+
+// Lyberta: surv
+void HUD_Mod_SURV(vector mypos, vector mysize)
+{
+ mod_active = 1; // required in each mod function that always shows something
+ float defenderhealth = STAT(SURV_DEFENDER_HEALTH);
+ // Draw a health bar
+ float margin = mysize.y / 10; // Leave a small margin to be stylish
+ vector healthbarpos = mypos;
+ healthbarpos.x += margin;
+ healthbarpos.y += margin;
+ vector healthbarsize = mysize;
+ healthbarsize.x -= margin * 2;
+ healthbarsize.y -= margin * 2;
+ vector healthbarcolor;
+ healthbarcolor.z = 0;
+ if (defenderhealth > 0.5)
+ {
+ healthbarcolor.x = defenderhealth * -2 + 2;
+ healthbarcolor.y = 1;
+ }
+ else
+ {
+ healthbarcolor.x = 1;
+ healthbarcolor.y = defenderhealth;
+ }
+ HUD_Panel_DrawProgressBar(healthbarpos, healthbarsize, "progressbar",
+ defenderhealth, false, 0, healthbarcolor, 0.50, DRAWFLAG_NORMAL);
+ // Draw defender picture
+ int defenderteam = STAT(SURV_DEFENDER_TEAM);
+ string defenderpic = "";
+ vector defendercolor;
+ switch (defenderteam)
+ {
+ case 1:
+ {
+ defenderpic = "player_red";
+ defendercolor = '1 0 0';
+ break;
+ }
+ case 2:
+ {
+ defenderpic = "player_blue";
+ defendercolor = '0 0 1';
+ break;
+ }
+ default:
+ {
+ defendercolor = '1 1 1';
+ break;
+ }
+ }
+ vector picpos = mypos;
+ vector picsize = mysize;
+ picsize.x = picsize.y;
+ drawpic_aspect_skin(picpos, defenderpic, picsize, '1 1 1', 1,
+ DRAWFLAG_NORMAL);
+ // Draw number of defenders
+ int numalive = STAT(SURV_DEFENDERS_ALIVE);
+ vector alivepos = mypos;
+ alivepos.x += picsize.x;
+ vector alivesize = picsize;
+ drawstring_aspect(alivepos, ftos(numalive), alivesize, defendercolor, 1,
+ DRAWFLAG_NORMAL);
+ // Draw the time left
+ float roundtime = STAT(SURV_ROUND_TIME);
+ if (roundtime < 0)
+ {
+ roundtime = 0;
+ }
+ vector roundtimepos = mypos;
+ roundtimepos.x += picsize.x * 2;
+ vector roundtimesize = mysize;
+ roundtimesize.x = mysize.x - picsize.x * 2;
+ drawstring_aspect(roundtimepos, seconds_tostring(roundtime), roundtimesize,
+ '1 1 1', 1, DRAWFLAG_NORMAL);
+ if (autocvar_developer == 0)
+ {
+ return;
+ }
+ // Debug info below
+ int redalive = STAT(REDALIVE);
+ int bluealive = STAT(BLUEALIVE);
+ int yellowalive = STAT(YELLOWALIVE);
+ int pinkalive = STAT(PINKALIVE);
+ string message = strcat(ftos(redalive), "/", ftos(yellowalive));
+ vector redpos = mypos;
+ redpos.y += mysize.y;
+ vector statsize = mysize;
+ statsize.x /= 2;
+ drawstring_aspect(redpos, message, statsize, '1 0 0', 1, DRAWFLAG_NORMAL);
+ message = strcat(ftos(bluealive), "/", ftos(pinkalive));
+ vector bluepos = mypos;
+ bluepos.x += mysize.x / 2;
+ bluepos.y += mysize.y;
+ drawstring_aspect(bluepos, message, statsize, '0 0 1', 1, DRAWFLAG_NORMAL);
+}
+
void HUD_ModIcons_SetFunc()
{
HUD_ModIcons_GameType = gametype.m_modicons;
#include <common/vehicles/all.qh>
#include <common/weapons/_all.qh>
+#include <common/mutators/mutator/overkill/okvortex.qh>
#include <common/viewloc.qh>
#include <common/minigames/cl_minigames.qh>
#include <common/minigames/cl_minigames_hud.qh>
{
entity wepent = viewmodels[slot];
if(wepent.switchweapon == wepent.activeweapon)
- if((wepent.activeweapon == WEP_VORTEX && !WEP_CVAR(vortex, secondary)) || (wepent.activeweapon == WEP_RIFLE && !WEP_CVAR(rifle, secondary))) // do NOT use switchweapon here
+ if((wepent.activeweapon == WEP_VORTEX && !WEP_CVAR(vortex, secondary)) || (wepent.activeweapon == WEP_RIFLE && !WEP_CVAR(rifle, secondary)) || (wepent.activeweapon == WEP_OVERKILL_VORTEX && !WEP_CVAR(okvortex, secondary))) // do NOT use switchweapon here
zoomdir += button_attack2;
}
}
case WEP_MORTAR: // toss curve
return SHOTTYPE_HITWORLD;
case WEP_VORTEX:
+ case WEP_OVERKILL_VORTEX:
case WEP_VAPORIZER:
mv = MOVE_NORMAL;
break;
vortex_charge = STAT(VORTEX_CHARGE);
vortex_chargepool = STAT(VORTEX_CHARGEPOOL);
+ float okvortex_charge, okvortex_chargepool;
+ okvortex_charge = STAT(OVERKILL_VORTEX_CHARGE);
+ okvortex_chargepool = STAT(OVERKILL_VORTEX_CHARGEPOOL);
+
float arc_heat = STAT(ARC_HEAT);
if(vortex_charge_movingavg == 0) // this should only happen if we have just loaded up the game
ring_rgb = wcross_color;
ring_image = "gfx/crosshair_ring_nexgun.tga";
}
+ else if (autocvar_crosshair_ring && (wepent.activeweapon == WEP_OVERKILL_VORTEX) && okvortex_charge && autocvar_crosshair_ring_vortex)
+ {
+ if (okvortex_chargepool || use_vortex_chargepool) {
+ use_vortex_chargepool = 1;
+ ring_inner_value = okvortex_chargepool;
+ } else {
+ vortex_charge_movingavg = (1 - autocvar_crosshair_ring_vortex_currentcharge_movingavg_rate) * vortex_charge_movingavg + autocvar_crosshair_ring_vortex_currentcharge_movingavg_rate * okvortex_charge;
+ ring_inner_value = bound(0, autocvar_crosshair_ring_vortex_currentcharge_scale * (okvortex_charge - vortex_charge_movingavg), 1);
+ }
+
+ ring_inner_alpha = autocvar_crosshair_ring_vortex_inner_alpha;
+ ring_inner_rgb = eX * autocvar_crosshair_ring_vortex_inner_color_red + eY * autocvar_crosshair_ring_vortex_inner_color_green + eZ * autocvar_crosshair_ring_vortex_inner_color_blue;
+ ring_inner_image = "gfx/crosshair_ring_inner.tga";
+
+ // draw the outer ring to show the current charge of the weapon
+ ring_value = okvortex_charge;
+ ring_alpha = autocvar_crosshair_ring_vortex_alpha;
+ ring_rgb = wcross_color;
+ ring_image = "gfx/crosshair_ring_nexgun.tga";
+ }
else if (autocvar_crosshair_ring && wepent.activeweapon == WEP_MINE_LAYER && WEP_CVAR(minelayer, limit) && autocvar_crosshair_ring_minelayer)
{
ring_value = bound(0, STAT(LAYED_MINES) / WEP_CVAR(minelayer, limit), 1); // if you later need to use the count of bullets in another place, then add a float for it. For now, no need to.
// generated file; do not modify
+#include <common/gamemodes/gamemode/gungame/_mod.inc>
#include <common/gamemodes/gamemode/nexball/_mod.inc>
#include <common/gamemodes/gamemode/onslaught/_mod.inc>
+#include <common/gamemodes/gamemode/survival/_mod.inc>
// generated file; do not modify
+#include <common/gamemodes/gamemode/gungame/_mod.qh>
#include <common/gamemodes/gamemode/nexball/_mod.qh>
#include <common/gamemodes/gamemode/onslaught/_mod.qh>
+#include <common/gamemodes/gamemode/survival/_mod.qh>
--- /dev/null
+// generated file; do not modify
+#ifdef SVQC
+ #include <common/gamemodes/gamemode/gungame/sv_gungame.qc>
+#endif
--- /dev/null
+// generated file; do not modify
+#ifdef SVQC
+ #include <common/gamemodes/gamemode/gungame/sv_gungame.qh>
+#endif
--- /dev/null
+/// \file
+/// \brief Source file that contains implementation of the GunGame gamemode.
+/// \author Lyberta
+/// \copyright GNU GPLv2 or any later version.
+
+#include "sv_gungame.qh"
+
+//============================ Constants ======================================
+
+const string GUNGAME_WEAPONS = "g_gg_weapons";
+
+//======================= Global variables ====================================
+
+/// \brief Number of kills needed to advance to the next weapon.
+int autocvar_g_gg_kills_per_weapon;
+
+int gungame_maxlevel; ///< Player who reaches this level wins.
+string gungame_weapons; ///< Holds weapons corresponding to levels.
+
+entity gungame_leading_player; ///< Holds the leading player.
+int gungame_leading_level; ///< Holds the leading level.
+entity gungame_leading_weapon; ///< Holds the leading weapon.
+
+//====================== Forward declarations =================================
+
+/// \brief Resets the state to initial one.
+/// \return No return.
+void GunGame_Reset();
+
+/// \brief Returns the weapon that corresponds to the given level.
+/// \param[in] level Level of the weapon.
+/// \return Weapon corresponding to the given level.
+entity GunGame_GetWeapon(int level);
+
+/// \brief Updates stats of all players.
+/// \return No return.
+void GunGame_UpdateStats();
+
+//========================= Free functions ====================================
+
+void GunGame_Initialize()
+{
+ GunGame_Reset();
+}
+
+void GunGame_Reset()
+{
+ if (gungame_weapons)
+ {
+ strunzone(gungame_weapons);
+ }
+ gungame_weapons = strzone(cvar_string(GUNGAME_WEAPONS));
+ gungame_maxlevel = tokenize_console(gungame_weapons) *
+ autocvar_g_gg_kills_per_weapon;
+ if (gungame_maxlevel == 0)
+ {
+ error("GunGame: Invalid weapon configuration.");
+ }
+ GameRules_limit_score(gungame_maxlevel);
+ gungame_leading_player = NULL;
+ gungame_leading_level = 0;
+ gungame_leading_weapon = GunGame_GetWeapon(0);
+ GunGame_UpdateStats();
+}
+
+entity GunGame_GetWeapon(int level)
+{
+ if (level >= gungame_maxlevel)
+ {
+ return NULL;
+ }
+ tokenize_console(gungame_weapons);
+ string weapon = argv(floor(level / autocvar_g_gg_kills_per_weapon));
+ FOREACH(Weapons, it != WEP_Null,
+ {
+ if (it.netname == weapon)
+ {
+ return it;
+ }
+ });
+ error("GunGame_GetWeapon: Invalid level or weapon name");
+ return NULL;
+}
+
+/// \brief Returns the player level.
+/// \param[in] player Player to check.
+/// \return Level of the player.
+int GunGame_GetPlayerLevel(entity player)
+{
+ return PlayerScore_Get(player, SP_SCORE);
+}
+
+/// \brief Updates the information about the leading player.
+/// \return No return.
+void GunGame_UpdateLeadingPlayer()
+{
+ entity previous_leader = gungame_leading_player;
+ FOREACH_CLIENT(true,
+ {
+ if (gungame_leading_player == NULL)
+ {
+ gungame_leading_player = it;
+ continue;
+ }
+ if (GunGame_GetPlayerLevel(it) > GunGame_GetPlayerLevel(
+ gungame_leading_player))
+ {
+ gungame_leading_player = it;
+ }
+ });
+ if (gungame_leading_player == NULL)
+ {
+ return;
+ }
+ if ((gungame_leading_player == previous_leader) &&
+ (GunGame_GetPlayerLevel(gungame_leading_player) ==
+ gungame_leading_level))
+ {
+ return;
+ }
+ gungame_leading_level = GunGame_GetPlayerLevel(gungame_leading_player);
+ gungame_leading_weapon = GunGame_GetWeapon(gungame_leading_level);
+ GunGame_UpdateStats();
+ //PrintToChatAll(strcat(gungame_leading_player.netname,
+ // " is leading with level ", ftos(gungame_leading_level)));
+}
+
+void GunGame_UpdateStats()
+{
+ FOREACH_CLIENT(IS_REAL_CLIENT(it),
+ {
+ STAT(GUNGAME_LEADING_WEAPON, it) = gungame_leading_weapon.m_id;
+ });
+}
+
+/// \brief Gives the player a weapon that corresponds to their level.
+/// \param[in,out] player Player to give weapon to.
+/// \return No return.
+void GunGame_GivePlayerWeapon(entity player)
+{
+ int level = GunGame_GetPlayerLevel(player);
+ if (level >= gungame_maxlevel)
+ {
+ return;
+ }
+ entity weapon = GunGame_GetWeapon(level);
+ player.weapons |= weapon.m_wepset;
+ centerprint(player, strcat("^3Level ", ftos(level + 1), ": ^2",
+ weapon.m_name));
+}
+
+//============================= Hooks ========================================
+
+/// \brief Hook that is called to determine if there is a weapon arena.
+MUTATOR_HOOKFUNCTION(gg, SetWeaponArena)
+{
+ //PrintToChatAll("SetWeaponArena");
+ M_ARGV(0, string) = "off";
+}
+
+/// \brief Hook that is called to determine start items of all players.
+MUTATOR_HOOKFUNCTION(gg, SetStartItems)
+{
+ //PrintToChatAll("SetStartItems");
+ start_weapons = WEPSET(Null);
+ warmup_start_weapons = WEPSET(Null);
+}
+
+/// \brief Hook that is called when an item is about to spawn.
+MUTATOR_HOOKFUNCTION(gg, FilterItem)
+{
+ //PrintToChatAll("FilterItem");
+ entity item = M_ARGV(0, entity);
+ if (item.itemdef.instanceOfWeaponPickup)
+ {
+ // Block weapons from spawning.
+ return true;
+ }
+}
+
+/// \brief Hook that is called when player connects to the server.
+MUTATOR_HOOKFUNCTION(gg, ClientConnect)
+{
+ entity player = M_ARGV(0, entity);
+ if (!IS_REAL_CLIENT(player))
+ {
+ return true;
+ }
+ STAT(GUNGAME_LEADING_WEAPON, player) = gungame_leading_weapon.m_id;
+ return true;
+}
+
+MUTATOR_HOOKFUNCTION(gg, reset_map_global)
+{
+ GunGame_Reset();
+}
+
+/// \brief Hook that is called when player spawns.
+MUTATOR_HOOKFUNCTION(gg, PlayerSpawn, CBC_ORDER_LAST)
+{
+ entity player = M_ARGV(0, entity);
+ player.weapons = WEPSET(Null);
+ GunGame_GivePlayerWeapon(player);
+ player.items |= IT_UNLIMITED_AMMO;
+}
+
+/// \brief Hook which is called when the player tries to throw their weapon.
+MUTATOR_HOOKFUNCTION(gg, ForbidThrowCurrentWeapon)
+{
+ return true;
+}
+
+/// \brief Hook that is called when player dies.
+MUTATOR_HOOKFUNCTION(gg, PlayerDies)
+{
+ GunGame_UpdateLeadingPlayer();
+ entity attacker = M_ARGV(1, entity);
+ if (!IS_PLAYER(attacker) || IS_DEAD(attacker) || (GunGame_GetPlayerLevel(
+ attacker) >= gungame_maxlevel))
+ {
+ return;
+ }
+ attacker.weapons = WEPSET(Null);
+ GunGame_GivePlayerWeapon(attacker);
+}
+
+/// \brief Hook that determines whether remaining frags are announced.
+MUTATOR_HOOKFUNCTION(gg, Scores_CountFragsRemaining)
+{
+ // announce remaining frags
+ return true;
+}
--- /dev/null
+/// \file
+/// \brief Header file that describes the GunGame gamemode.
+/// \author Lyberta
+/// \copyright GNU GPLv2 or any later version.
+
+#pragma once
+
+/// \brief Initializes global data for the gametype.
+/// \return No return.
+void GunGame_Initialize();
+
+REGISTER_MUTATOR(gg, false)
+{
+ MUTATOR_STATIC();
+ MUTATOR_ONADD
+ {
+ GunGame_Initialize();
+ }
+ return 0;
+}
--- /dev/null
+// generated file; do not modify
+#ifdef SVQC
+ #include <common/gamemodes/gamemode/survival/sv_survival.qc>
+#endif
--- /dev/null
+// generated file; do not modify
+#ifdef SVQC
+ #include <common/gamemodes/gamemode/survival/sv_survival.qh>
+#endif
--- /dev/null
+#include "sv_survival.qh"
+
+#include <common/mutators/mutator/overkill/hmg.qh>
+#include <common/mutators/mutator/overkill/rpc.qh>
+
+//============================ Constants ======================================
+
+const int SURVIVAL_TEAM_1_BIT = BIT(0); ///< Bitmask of the first team.
+const int SURVIVAL_TEAM_2_BIT = BIT(1); ///< Bitmask of the second team.
+
+/// \brief Used when bitfield team count is requested.
+const int SURVIVAL_TEAM_BITS = 3;
+
+enum
+{
+ SURVIVAL_TYPE_COOP, ///< All humans are on the defender team.
+ SURVIVAL_TYPE_VERSUS ///< Humans take turns between attackers and defenders.
+};
+
+const string SURVIVAL_TYPE_COOP_VALUE = "coop"; ///< Cvar value for coop.
+const string SURVIVAL_TYPE_VERSUS_VALUE = "versus"; ///< Cvar value for versus.
+
+enum
+{
+ /// \brief First round where there is timelimit set by the server.
+ SURVIVAL_ROUND_FIRST,
+ /// \brief Second round where defender team tries to survive for the first
+ /// round's time.
+ SURVIVAL_ROUND_SECOND
+};
+
+enum
+{
+ /// \brief Player is spectating and has no intention of playing.
+ SURVIVAL_STATE_NOT_PLAYING,
+ /// \brief Player is playing the game.
+ SURVIVAL_STATE_PLAYING = 1
+};
+
+enum
+{
+ SURVIVAL_ROLE_NONE, ///< Player is not playing.
+ SURVIVAL_ROLE_PLAYER, ///< Player is an attacker or defender.
+ SURVIVAL_ROLE_CANNON_FODDER ///< Player is a cannon fodder.
+};
+
+SOUND(SURV_3_FRAGS_LEFT, "announcer/default/3fragsleft");
+SOUND(SURV_2_FRAGS_LEFT, "announcer/default/2fragsleft");
+SOUND(SURV_1_FRAG_LEFT, "announcer/default/1fragleft");
+
+SOUND(SURV_RED_SCORES, "ctf/red_capture");
+SOUND(SURV_BLUE_SCORES, "ctf/blue_capture");
+
+//======================= Global variables ====================================
+
+float autocvar_g_surv_warmup; ///< Warmup time in seconds.
+float autocvar_g_surv_round_timelimit; ///< First round time limit in seconds.
+
+int autocvar_g_surv_point_limit; ///< Maximum number of points.
+int autocvar_g_surv_point_leadlimit; ///< Maximum lead of a single team.
+
+/// \brief How much players are allowed in teams (excluding cannon fodder).
+int autocvar_g_surv_team_size;
+/// \brief If set, defenders will not be shown on the radar.
+int autocvar_g_surv_stealth;
+/// \brief Whether to allow spectating enemy players while dead.
+bool autocvar_g_surv_spectate_enemies;
+
+/// \brief Whether to force overkill player models for attackers.
+int autocvar_g_surv_attacker_force_overkill_models;
+/// \brief Whether to force overkill player models for defenders.
+int autocvar_g_surv_defender_force_overkill_models;
+/// \brief Whether to force overkill player models for cannon fodder.
+int autocvar_g_surv_cannon_fodder_force_overkill_models;
+
+/// \brief How much score attackers gain per 1 point of damage.
+float autocvar_g_surv_attacker_damage_score;
+
+/// \brief How much score attackers get for fragging defenders.
+float autocvar_g_surv_attacker_frag_score;
+
+/// \brief How much health do defenders get when they frag an attacker.
+int autocvar_g_surv_defender_attacker_frag_health;
+/// \brief How much armor do defenders get when they frag an attacker.
+int autocvar_g_surv_defender_attacker_frag_armor;
+/// \brief How many shells do defenders get when they frag an attacker.
+int autocvar_g_surv_defender_attacker_frag_shells;
+/// \brief How many bullets do defenders get when they frag an attacker.
+int autocvar_g_surv_defender_attacker_frag_bullets;
+/// \brief How many rockets do defenders get when they frag an attacker.
+int autocvar_g_surv_defender_attacker_frag_rockets;
+/// \brief How many cells do defenders get when they frag an attacker.
+int autocvar_g_surv_defender_attacker_frag_cells;
+/// \brief How much plasma do defenders get when they frag an attacker.
+int autocvar_g_surv_defender_attacker_frag_plasma;
+/// \brief How much fuel do defenders get when they frag an attacker.
+int autocvar_g_surv_defender_attacker_frag_fuel;
+/// \brief How much health do defenders get when they frag cannon fodder.
+int autocvar_g_surv_defender_cannon_fodder_frag_health;
+/// \brief How much armor do defenders get when they frag cannon fodder.
+int autocvar_g_surv_defender_cannon_fodder_frag_armor;
+/// \brief How many shells do defenders get when they frag cannon fodder.
+int autocvar_g_surv_defender_cannon_fodder_frag_shells;
+/// \brief How many bullets do defenders get when they frag cannon fodder.
+int autocvar_g_surv_defender_cannon_fodder_frag_bullets;
+/// \brief How many rockets do defenders get when they frag cannon fodder.
+int autocvar_g_surv_defender_cannon_fodder_frag_rockets;
+/// \brief How many cells do defenders get when they frag cannon fodder.
+int autocvar_g_surv_defender_cannon_fodder_frag_cells;
+/// \brief How much plasma do defenders get when they frag cannon fodder.
+int autocvar_g_surv_defender_cannon_fodder_frag_plasma;
+/// \brief How much fuel do defenders get when they frag cannon fodder.
+int autocvar_g_surv_defender_cannon_fodder_frag_fuel;
+
+/// \brief Holds the state of the player. See SURVIVAL_STATE constants.
+.int surv_state;
+/// \brief Holds the role of the player. See SURVIVAL_ROLE constants.
+.int surv_role;
+.string surv_savedplayermodel; ///< Initial player model.
+/// \brief Player state used during replacement of bot player with real player.
+.entity surv_savedplayerstate;
+.string surv_playermodel; ///< Player model forced by the game.
+
+.entity surv_attack_sprite; ///< Holds the sprite telling attackers to attack.
+.entity surv_defend_sprite; ///< Holds the sprite telling defenders to defend.
+
+int surv_type; ///< Holds the type of survival. See SURVIVAL_TYPE constants.
+bool surv_warmup; ///< Holds whether warmup is active.
+/// \brief Holds the type of the current round. See SURVIVAL_ROUND constants.
+int surv_roundtype;
+bool surv_isroundactive; ///< Holds whether the round is active.
+float surv_roundstarttime; ///< Holds the time of the round start.
+/// \brief Holds the time needed to survive in the second round.
+float surv_timetobeat;
+
+int surv_attackerteam; ///< Holds the attacker team.
+int surv_defenderteam; ///< Holds the defender team.
+
+int surv_attackerteambit; ///< Hols the attacker team bitmask.
+int surv_defenderteambit; ///< Holds the defender team bitmask.
+
+int surv_numattackers; ///< Holds the number of players in attacker team.
+int surv_numdefenders; ///< Holds the number of players in defender team.
+
+/// \brief Holds the number of humans in attacker team.
+int surv_numattackerhumans;
+/// \brief Holds the number of humans in defender team.
+int surv_numdefenderhumans;
+
+/// \brief Holds the number of attacker players that are alive.
+int surv_numattackersalive;
+/// \brief Holds the number of defender players that are alive.
+int surv_numdefendersalive;
+
+bool surv_autobalance; ///< Holds whether autobalance is active.
+bool surv_announcefrags; ///< Holds whether remaining frags must be announced.
+bool surv_allowed_to_spawn; ///< Holds whether players are allowed to spawn.
+
+//====================== Forward declarations =================================
+
+/// \brief Determines whether the round can start.
+/// \return True if the round can start, false otherwise.
+bool Surv_CanRoundStart();
+
+/// \brief Determines whether the round can end.
+/// \return True if the round can end, false otherwise.
+bool Surv_CanRoundEnd();
+
+/// \brief Called when the round starts.
+/// \return No return.
+void Surv_RoundStart();
+
+/// \brief Returns whether player has been eliminated.
+/// \param[in] player Player to check.
+/// \return True if player is eliminated, false otherwise.
+bool Surv_IsEliminated(entity player);
+
+/// \brief Updates stats of team count on HUD.
+/// \return No return.
+void Surv_UpdateTeamStats();
+
+/// \brief Updates stats of alive players on HUD.
+/// \return No return.
+void Surv_UpdateAliveStats();
+
+/// \brief Updates defender health on the HUD.
+/// \return No return.
+void Surv_UpdateDefenderHealthStat();
+
+/// \brief Updates the health of defender sprite.
+/// \param[in,out] player Player that has the sprite.
+/// \return No return.
+void Surv_UpdateWaypointSpriteHealth(entity player);
+
+//========================= Free functions ====================================
+
+void Surv_Initialize()
+{
+ switch (cvar_string("g_surv_type"))
+ {
+ case SURVIVAL_TYPE_COOP_VALUE:
+ {
+ surv_type = SURVIVAL_TYPE_COOP;
+ break;
+ }
+ case SURVIVAL_TYPE_VERSUS_VALUE:
+ {
+ surv_type = SURVIVAL_TYPE_VERSUS;
+ break;
+ }
+ default:
+ {
+ error("Invalid survival type.");
+ }
+ }
+ surv_roundtype = SURVIVAL_ROUND_FIRST;
+ surv_isroundactive = false;
+ surv_roundstarttime = time;
+ surv_timetobeat = autocvar_g_surv_round_timelimit;
+ if (random() < 0.5)
+ {
+ surv_attackerteam = NUM_TEAM_1;
+ surv_defenderteam = NUM_TEAM_2;
+ surv_attackerteambit = SURVIVAL_TEAM_1_BIT;
+ surv_defenderteambit = SURVIVAL_TEAM_2_BIT;
+ }
+ else
+ {
+ surv_attackerteam = NUM_TEAM_2;
+ surv_defenderteam = NUM_TEAM_1;
+ surv_attackerteambit = SURVIVAL_TEAM_2_BIT;
+ surv_defenderteambit = SURVIVAL_TEAM_1_BIT;
+ }
+ surv_numattackers = 0;
+ surv_numdefenders = 0;
+ surv_numattackerhumans = 0;
+ surv_numdefenderhumans = 0;
+ surv_numattackersalive = 0;
+ surv_numdefendersalive = 0;
+ surv_autobalance = true;
+ surv_announcefrags = true;
+ surv_allowed_to_spawn = true;
+ precache_all_playermodels("models/ok_player/*.dpm");
+ ScoreRules_basics(SURVIVAL_TEAM_BITS, SFL_SORT_PRIO_PRIMARY, 0, true);
+ ScoreInfo_SetLabel_TeamScore(1, "rounds", SFL_SORT_PRIO_PRIMARY);
+ ScoreRules_basics_end();
+ round_handler_Spawn(Surv_CanRoundStart, Surv_CanRoundEnd, Surv_RoundStart);
+ round_handler_Init(5, autocvar_g_surv_warmup, surv_timetobeat);
+ EliminatedPlayers_Init(Surv_IsEliminated);
+ GameRules_teams(true);
+ GameRules_limit_score(autocvar_g_surv_point_limit);
+ GameRules_limit_lead(autocvar_g_surv_point_leadlimit);
+}
+
+/// \brief Returns the name of the template of the given player.
+/// \param[in] player Player to inspect.
+/// \return Name of the template of the given player.
+string Surv_GetPlayerTemplate(entity player)
+{
+ switch (player.team)
+ {
+ case surv_attackerteam:
+ {
+ switch (player.surv_role)
+ {
+ case SURVIVAL_ROLE_NONE:
+ case SURVIVAL_ROLE_PLAYER:
+ {
+ return cvar_string("g_surv_attacker_template");
+ }
+ case SURVIVAL_ROLE_CANNON_FODDER:
+ {
+ return cvar_string("g_surv_cannon_fodder_template");
+ }
+ }
+ }
+ case surv_defenderteam:
+ {
+ return cvar_string("g_surv_defender_template");
+ }
+ }
+ return "default";
+}
+
+/// \brief Saves the player state. Used to seamlessly swap bots with humans.
+/// \param[in] player Player to save the state of.
+/// \return Entity containing the player state.
+entity Surv_SavePlayerState(entity player)
+{
+ entity state = spawn();
+ state.origin = player.origin;
+ state.velocity = player.velocity;
+ state.angles = player.angles;
+ state.health = player.health;
+ state.armorvalue = player.armorvalue;
+ state.ammo_shells = player.ammo_shells;
+ state.ammo_nails = player.ammo_nails;
+ state.ammo_rockets = player.ammo_rockets;
+ state.ammo_cells = player.ammo_cells;
+ state.weapons = player.weapons;
+ state.items = player.items;
+ state.superweapons_finished = player.superweapons_finished;
+ return state;
+}
+
+/// \brief Restores a saved player state.
+/// \param[in] player Player to restore the state of.
+/// \param[in] st State to restore.
+/// \return No return.
+void Surv_RestorePlayerState(entity player, entity st)
+{
+ player.origin = st.origin;
+ player.velocity = st.velocity;
+ player.angles = st.angles;
+ player.health = st.health;
+ player.armorvalue = st.armorvalue;
+ player.ammo_shells = st.ammo_shells;
+ player.ammo_nails = st.ammo_nails;
+ player.ammo_rockets = st.ammo_rockets;
+ player.ammo_cells = st.ammo_cells;
+ player.weapons = st.weapons;
+ player.items = st.items;
+ player.superweapons_finished = st.superweapons_finished;
+}
+
+/// \brief Returns the attacker with the lowest score.
+/// \param[in] bot Whether to search only for bots.
+/// \return Attacker with the lowest score or NULL if not found.
+entity Surv_FindLowestAttacker(bool bot)
+{
+ entity player = NULL;
+ float score = FLOAT_MAX;
+ FOREACH_CLIENT(bot ? IS_BOT_CLIENT(it) : true,
+ {
+ if ((it.team == surv_attackerteam) && (it.surv_role ==
+ SURVIVAL_ROLE_PLAYER))
+ {
+ float tempscore = PlayerScore_Get(it, SP_SCORE);
+ if (tempscore < score)
+ {
+ player = it;
+ score = tempscore;
+ }
+ }
+ });
+ return player;
+}
+
+/// \brief Returns the defender with the lowest score.
+/// \param[in] bot Whether to search only for bots.
+/// \param[in] alive Whether to search only for alive players.
+/// \return Defender with the lowest score or NULL if not found.
+entity Surv_FindLowestDefender(bool bot, bool alive)
+{
+ entity player = NULL;
+ float score = FLOAT_MAX;
+ FOREACH_CLIENT(bot ? IS_BOT_CLIENT(it) : true,
+ {
+ if ((it.team == surv_defenderteam) && (alive ? !IS_DEAD(it) : true))
+ {
+ float tempscore = PlayerScore_Get(it, SP_SCORE);
+ if (tempscore < score)
+ {
+ player = it;
+ score = tempscore;
+ }
+ }
+ });
+ return player;
+}
+
+/// \brief Returns the cannon fodder.
+/// \return Cannon fodder or NULL if not found.
+entity Surv_FindCannonFodder()
+{
+ FOREACH_CLIENT(IS_BOT_CLIENT(it),
+ {
+ if (it.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
+ {
+ return it;
+ }
+ });
+ return NULL;
+}
+
+/// \brief Changes the number of players in a team.
+/// \param[in] teamnum Team to adjust.
+/// \param[in] delta Amount to adjust by.
+/// \return No return.
+void Surv_ChangeNumberOfPlayers(int teamnum, int delta)
+{
+ switch (teamnum)
+ {
+ case surv_attackerteam:
+ {
+ surv_numattackers += delta;
+ LOG_TRACE("Number of attackers = ", ftos(surv_numattackers),
+ " was = ", ftos(surv_numattackers - delta));
+ Surv_UpdateTeamStats();
+ return;
+ }
+ case surv_defenderteam:
+ {
+ surv_numdefenders += delta;
+ LOG_TRACE("Number of defenders = ", ftos(surv_numdefenders),
+ " was = ", ftos(surv_numdefenders - delta));
+ Surv_UpdateTeamStats();
+ return;
+ }
+ }
+}
+
+/// \brief Changes the number of alive players in a team.
+/// \param[in] teamnum Team to adjust.
+/// \param[in] delta Amount to adjust by.
+/// \return No return.
+void Surv_ChangeNumberOfAlivePlayers(int teamnum, int delta)
+{
+ switch (teamnum)
+ {
+ case surv_attackerteam:
+ {
+ surv_numattackersalive += delta;
+ LOG_TRACE("Number of alive attackers = ", ftos(
+ surv_numattackersalive), " was = ", ftos(surv_numattackersalive
+ - delta));
+ break;
+ }
+ case surv_defenderteam:
+ {
+ surv_numdefendersalive += delta;
+ LOG_TRACE("Number of alive defenders = ", ftos(
+ surv_numdefendersalive), " was = ", ftos(surv_numdefendersalive
+ - delta));
+ break;
+ }
+ }
+ Surv_UpdateAliveStats();
+ eliminatedPlayers.SendFlags |= 1;
+}
+
+/// \brief Sets the player role.
+/// \param[in,out] player Player to adjust.
+/// \param[in] role Role to set.
+/// \return No return.
+void Surv_SetPlayerRole(entity player, int role)
+{
+ if (player.surv_role == role)
+ {
+ return;
+ }
+ player.surv_role = role;
+ switch (role)
+ {
+ case SURVIVAL_ROLE_NONE:
+ {
+ LOG_TRACE(player.netname, " now has no role.");
+ break;
+ }
+ case SURVIVAL_ROLE_PLAYER:
+ {
+ LOG_TRACE(player.netname, " is now a player.");
+ break;
+ }
+ case SURVIVAL_ROLE_CANNON_FODDER:
+ {
+ LOG_TRACE(player.netname, " is now a cannon fodder.");
+ break;
+ }
+ }
+}
+
+/// \brief Adds player to team. Handles bookkeeping information.
+/// \param[in] player Player to add.
+/// \param[in] teamnum Team to add to.
+/// \return True on success, false otherwise.
+bool Surv_AddPlayerToTeam(entity player, int teamnum)
+{
+ LOG_TRACE("Survival: AddPlayerToTeam, player: ", player.netname);
+ switch (teamnum)
+ {
+ case surv_attackerteam:
+ {
+ LOG_TRACE("Attacker team");
+ if (player.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
+ {
+ LOG_TRACE("Cannon fodder is switching team");
+ return true;
+ }
+ if (IS_BOT_CLIENT(player))
+ {
+ LOG_TRACE("Client is bot");
+ LOG_TRACE("Attackers = ", ftos(surv_numattackers));
+ if (surv_numattackers < autocvar_g_surv_team_size)
+ {
+ Surv_SetPlayerRole(player, SURVIVAL_ROLE_PLAYER);
+ Surv_ChangeNumberOfPlayers(teamnum, +1);
+ return true;
+ }
+ Surv_SetPlayerRole(player, SURVIVAL_ROLE_CANNON_FODDER);
+ return true;
+ }
+ LOG_TRACE("Client is not a bot");
+ LOG_TRACE("Attackers = ", ftos(surv_numattackers));
+ if (surv_numattackers >= autocvar_g_surv_team_size)
+ {
+ LOG_TRACE("Removing bot");
+ // Remove bot to make space for human.
+ entity bot = Surv_FindLowestAttacker(true);
+ if (bot == NULL)
+ {
+ LOG_TRACE("No valid bot to remove");
+ // No space in team, denying team change.
+ TRANSMUTE(Spectator, player);
+ return false;
+ }
+ LOG_TRACE("Changing ", bot.netname,
+ " from attacker to cannon fodder.");
+ Surv_SetPlayerRole(bot, SURVIVAL_ROLE_CANNON_FODDER);
+ if (!IS_DEAD(bot))
+ {
+ Surv_ChangeNumberOfAlivePlayers(teamnum, -1);
+ }
+ Surv_ChangeNumberOfPlayers(teamnum, -1);
+ LOG_TRACE("Removed bot");
+ }
+ Surv_SetPlayerRole(player, SURVIVAL_ROLE_PLAYER);
+ Surv_ChangeNumberOfPlayers(teamnum, +1);
+ ++surv_numattackerhumans;
+ LOG_TRACE("Human attackers = ", ftos(surv_numattackerhumans));
+ if ((surv_autobalance == false) || (surv_numattackers -
+ surv_numdefenders) < 2)
+ {
+ return true;
+ }
+ entity lowestplayer = Surv_FindLowestAttacker(true);
+ if (lowestplayer != NULL)
+ {
+ bool savedautobalance = surv_autobalance;
+ surv_autobalance = false;
+ SetPlayerTeamSimple(lowestplayer, surv_defenderteam);
+ surv_autobalance = savedautobalance;
+ return true;
+ }
+ lowestplayer = Surv_FindLowestAttacker(false);
+ if (lowestplayer != NULL)
+ {
+ bool savedautobalance = surv_autobalance;
+ surv_autobalance = false;
+ SetPlayerTeamSimple(lowestplayer, surv_defenderteam);
+ surv_autobalance = savedautobalance;
+ }
+ return true;
+ }
+ case surv_defenderteam:
+ {
+ LOG_TRACE("Defender team");
+ if (IS_BOT_CLIENT(player))
+ {
+ LOG_TRACE("Client is bot");
+ LOG_TRACE("Defenders = ", ftos(surv_numdefenders));
+ if (surv_numdefenders < autocvar_g_surv_team_size)
+ {
+ Surv_SetPlayerRole(player, SURVIVAL_ROLE_PLAYER);
+ Surv_ChangeNumberOfPlayers(teamnum, +1);
+ return true;
+ }
+ LOG_TRACE("No space for defender, switching to attacker");
+ SetPlayerTeamSimple(player, surv_attackerteam);
+ return false;
+ }
+ LOG_TRACE("Client is not a bot");
+ LOG_TRACE("Defenders = ", ftos(surv_numdefenders));
+ if (surv_numdefenders >= autocvar_g_surv_team_size)
+ {
+ LOG_TRACE("Removing bot");
+ // Remove bot to make space for human.
+ entity bot = Surv_FindLowestDefender(true, true);
+ if (bot == NULL)
+ {
+ bot = Surv_FindLowestDefender(true, false);
+ }
+ if (bot == NULL)
+ {
+ LOG_TRACE("No valid bot to remove");
+ // No space in team, denying team change.
+ TRANSMUTE(Spectator, player);
+ return false;
+ }
+ LOG_TRACE("Changing ", bot.netname,
+ " from defender to cannon fodder.");
+ if (!IS_DEAD(bot))
+ {
+ player.surv_savedplayerstate = Surv_SavePlayerState(bot);
+ }
+ bool savedautobalance = surv_autobalance;
+ surv_autobalance = false;
+ surv_announcefrags = false;
+ SetPlayerTeamSimple(bot, surv_attackerteam);
+ surv_autobalance = savedautobalance;
+ surv_announcefrags = true;
+ LOG_TRACE("Removed bot");
+ }
+ Surv_SetPlayerRole(player, SURVIVAL_ROLE_PLAYER);
+ Surv_ChangeNumberOfPlayers(teamnum, +1);
+ ++surv_numdefenderhumans;
+ LOG_TRACE("Human defenders = ", ftos(surv_numdefenderhumans));
+ if ((surv_autobalance == false) || (surv_numdefenders -
+ surv_numattackers) < 2)
+ {
+ return true;
+ }
+ entity lowestplayer = Surv_FindLowestDefender(true, false);
+ if (lowestplayer != NULL)
+ {
+ bool savedautobalance = surv_autobalance;
+ surv_autobalance = false;
+ SetPlayerTeamSimple(lowestplayer, surv_attackerteam);
+ surv_autobalance = savedautobalance;
+ return true;
+ }
+ lowestplayer = Surv_FindLowestDefender(false, false);
+ if (lowestplayer != NULL)
+ {
+ bool savedautobalance = surv_autobalance;
+ surv_autobalance = false;
+ SetPlayerTeamSimple(lowestplayer, surv_attackerteam);
+ surv_autobalance = savedautobalance;
+ }
+ return true;
+ }
+ case -1:
+ {
+ LOG_TRACE("Spectator team");
+ player.surv_role = SURVIVAL_ROLE_NONE;
+ return false;
+ }
+ }
+ LOG_TRACE("Invalid team");
+ player.surv_role = SURVIVAL_ROLE_NONE;
+ return false;
+}
+
+/// \brief Removes player from team. Handles bookkeeping information.
+/// \param[in] player Player to remove.
+/// \param[in] Team to remove from.
+/// \return No return.
+void Surv_RemovePlayerFromTeam(entity player, int teamnum)
+{
+ LOG_TRACE("Survival: RemovePlayerFromTeam, player: ", player.netname);
+ switch (teamnum)
+ {
+ case surv_attackerteam:
+ {
+ LOG_TRACE("Attacker team");
+ if (player.surv_role == SURVIVAL_ROLE_NONE)
+ {
+ string message = strcat("RemovePlayerFromTeam: ",
+ player.netname, " has invalid role.");
+ DebugPrintToChatAll(message);
+ return;
+ }
+ if (player.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
+ {
+ Surv_SetPlayerRole(player, SURVIVAL_ROLE_NONE);
+ return;
+ }
+ Surv_SetPlayerRole(player, SURVIVAL_ROLE_NONE);
+ Surv_ChangeNumberOfPlayers(teamnum, -1);
+ if (!IS_BOT_CLIENT(player))
+ {
+ --surv_numattackerhumans;
+ }
+ if ((surv_autobalance == false) || (surv_numattackers >=
+ surv_numdefenders))
+ {
+ return;
+ }
+ // Add bot to keep teams balanced.
+ entity lowestplayer = Surv_FindCannonFodder();
+ if (lowestplayer != NULL)
+ {
+ LOG_TRACE("Changing ", lowestplayer.netname,
+ " from cannon fodder to attacker.");
+ Surv_SetPlayerRole(lowestplayer, SURVIVAL_ROLE_PLAYER);
+ Surv_ChangeNumberOfPlayers(teamnum, +1);
+ if (!IS_DEAD(lowestplayer))
+ {
+ Surv_ChangeNumberOfAlivePlayers(teamnum, +1);
+ }
+ return;
+ }
+ lowestplayer = Surv_FindLowestDefender(true, false);
+ if (lowestplayer != NULL)
+ {
+ bool savedautobalance = surv_autobalance;
+ surv_autobalance = false;
+ SetPlayerTeamSimple(lowestplayer, surv_attackerteam);
+ surv_autobalance = savedautobalance;
+ return;
+ }
+ lowestplayer = Surv_FindLowestDefender(false, false);
+ if (lowestplayer == NULL)
+ {
+ return;
+ }
+ bool savedautobalance = surv_autobalance;
+ surv_autobalance = false;
+ SetPlayerTeamSimple(lowestplayer, surv_attackerteam);
+ surv_autobalance = savedautobalance;
+ return;
+ }
+ case surv_defenderteam:
+ {
+ LOG_TRACE("Defender team");
+ if (player.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
+ {
+ // This happens during team switch. We don't need to change
+ // anything.
+ LOG_TRACE("Cannon fodder. Assuming team switch");
+ return;
+ }
+ if (player.surv_role != SURVIVAL_ROLE_PLAYER)
+ {
+ string message = strcat("RemovePlayerFromTeam: ",
+ player.netname, " has invalid role.");
+ DebugPrintToChatAll(message);
+ return;
+ }
+ Surv_SetPlayerRole(player, SURVIVAL_ROLE_NONE);
+ Surv_ChangeNumberOfPlayers(teamnum, -1);
+ if (!IS_BOT_CLIENT(player))
+ {
+ --surv_numdefenderhumans;
+ }
+ if ((surv_autobalance == false) || (surv_numdefenders >=
+ surv_numattackers))
+ {
+ return;
+ }
+ // Add bot to keep teams balanced.
+ entity lowestplayer = Surv_FindCannonFodder();
+ if (lowestplayer != NULL)
+ {
+ LOG_TRACE("Changing ", lowestplayer.netname,
+ " from cannon fodder to defender.");
+ if (!IS_DEAD(player))
+ {
+ lowestplayer.surv_savedplayerstate =
+ Surv_SavePlayerState(player);
+ }
+ bool savedautobalance = surv_autobalance;
+ surv_autobalance = false;
+ surv_announcefrags = false;
+ SetPlayerTeamSimple(lowestplayer, surv_defenderteam);
+ surv_autobalance = savedautobalance;
+ surv_announcefrags = true;
+ return;
+ }
+ lowestplayer = Surv_FindLowestAttacker(true);
+ if (lowestplayer != NULL)
+ {
+ bool savedautobalance = surv_autobalance;
+ surv_autobalance = false;
+ surv_announcefrags = false;
+ SetPlayerTeamSimple(lowestplayer, surv_defenderteam);
+ surv_autobalance = savedautobalance;
+ surv_announcefrags = true;
+ return;
+ }
+ lowestplayer = Surv_FindLowestAttacker(false);
+ if (lowestplayer == NULL)
+ {
+ return;
+ }
+ bool savedautobalance = surv_autobalance;
+ surv_autobalance = false;
+ surv_announcefrags = false;
+ SetPlayerTeamSimple(lowestplayer, surv_defenderteam);
+ surv_autobalance = savedautobalance;
+ surv_announcefrags = true;
+ return;
+ }
+ case -1:
+ {
+ LOG_TRACE("Spectator team");
+ return;
+ }
+ default:
+ {
+ LOG_TRACE("Invalid team");
+ return;
+ }
+ }
+}
+
+/// \brief Updates stats of team count on HUD.
+/// \return No return.
+void Surv_UpdateTeamStats()
+{
+ // Debug stuff
+ if (surv_attackerteam == NUM_TEAM_1)
+ {
+ yellowalive = surv_numattackers;
+ pinkalive = surv_numdefenders;
+ }
+ else
+ {
+ pinkalive = surv_numattackers;
+ yellowalive = surv_numdefenders;
+ }
+ FOREACH_CLIENT(IS_REAL_CLIENT(it),
+ {
+ it.yellowalive_stat = yellowalive;
+ it.pinkalive_stat = pinkalive;
+ });
+}
+
+/// \brief Adds player to alive list. Handles bookkeeping information.
+/// \param[in] player Player to add.
+/// \param[in] teamnum Team of the player.
+/// \return No return.
+void Surv_AddPlayerToAliveList(entity player, int teamnum)
+{
+ switch (teamnum)
+ {
+ case surv_attackerteam:
+ {
+ if (player.surv_role == SURVIVAL_ROLE_PLAYER)
+ {
+ Surv_ChangeNumberOfAlivePlayers(teamnum, +1);
+ }
+ return;
+ }
+ case surv_defenderteam:
+ {
+ Surv_ChangeNumberOfAlivePlayers(teamnum, +1);
+ return;
+ }
+ }
+}
+
+/// \brief Removes player from alive list. Handles bookkeeping information.
+/// \param[in] player Player to remove.
+/// \param[in] teamnum Team of the player.
+/// \return No return.
+void Surv_RemovePlayerFromAliveList(entity player, int teamnum)
+{
+ if (player.surv_attack_sprite)
+ {
+ WaypointSprite_Kill(player.surv_attack_sprite);
+ }
+ if (player.surv_defend_sprite)
+ {
+ WaypointSprite_Kill(player.surv_defend_sprite);
+ }
+ switch (teamnum)
+ {
+ case surv_attackerteam:
+ {
+ if (player.surv_role == SURVIVAL_ROLE_PLAYER)
+ {
+ Surv_ChangeNumberOfAlivePlayers(teamnum, -1);
+ }
+ return;
+ }
+ case surv_defenderteam:
+ {
+ if (player.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
+ {
+ // This happens during team switch. We don't need to change
+ // anything.
+ return;
+ }
+ Surv_ChangeNumberOfAlivePlayers(teamnum, -1);
+ if (warmup_stage || surv_allowed_to_spawn || !surv_announcefrags)
+ {
+ return;
+ }
+ switch (surv_numdefendersalive)
+ {
+ case 1:
+ {
+ sound(NULL, CH_TRIGGER, SND_SURV_1_FRAG_LEFT,
+ VOL_BASE, ATTEN_NONE);
+ FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
+ {
+ if (it.team == surv_defenderteam)
+ {
+ Send_Notification(NOTIF_ONE, it, MSG_CENTER,
+ CENTER_ALONE);
+ return;
+ }
+ });
+ return;
+ }
+ case 2:
+ {
+ sound(NULL, CH_TRIGGER, SND_SURV_2_FRAGS_LEFT,
+ VOL_BASE, ATTEN_NONE);
+ return;
+ }
+ case 3:
+ {
+ sound(NULL, CH_TRIGGER, SND_SURV_3_FRAGS_LEFT,
+ VOL_BASE, ATTEN_NONE);
+ return;
+ }
+ }
+ return;
+ }
+ }
+}
+
+/// \brief Counts alive players.
+/// \return No return.
+/// \note In a perfect world this function shouldn't exist. However, since QC
+/// code is so bad and spurious mutators can really mess with your code, this
+/// function is called as a last resort.
+void Surv_CountAlivePlayers()
+{
+ int savednumdefenders = surv_numdefendersalive;
+ surv_numattackersalive = 0;
+ surv_numdefendersalive = 0;
+ FOREACH_CLIENT(IS_PLAYER(it),
+ {
+ switch (it.team)
+ {
+ case surv_attackerteam:
+ {
+ if ((it.surv_role == SURVIVAL_ROLE_PLAYER) && !IS_DEAD(it))
+ {
+ ++surv_numattackersalive;
+ }
+ break;
+ }
+ case surv_defenderteam:
+ {
+ if ((it.surv_role == SURVIVAL_ROLE_PLAYER) && !IS_DEAD(it))
+ {
+ ++surv_numdefendersalive;
+ }
+ break;
+ }
+ }
+ });
+ Surv_UpdateAliveStats();
+ eliminatedPlayers.SendFlags |= 1;
+ if (warmup_stage || surv_allowed_to_spawn || (savednumdefenders <=
+ surv_numdefendersalive))
+ {
+ return;
+ }
+ switch (surv_numdefendersalive)
+ {
+ case 1:
+ {
+ sound(NULL, CH_TRIGGER, SND_SURV_1_FRAG_LEFT, VOL_BASE, ATTEN_NONE);
+ FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it),
+ {
+ if (it.team == surv_defenderteam)
+ {
+ Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_ALONE);
+ return;
+ }
+ });
+ return;
+ }
+ case 2:
+ {
+ sound(NULL, CH_TRIGGER, SND_SURV_2_FRAGS_LEFT, VOL_BASE,
+ ATTEN_NONE);
+ return;
+ }
+ case 3:
+ {
+ sound(NULL, CH_TRIGGER, SND_SURV_3_FRAGS_LEFT, VOL_BASE,
+ ATTEN_NONE);
+ return;
+ }
+ }
+}
+
+/// \brief Updates stats of alive players on HUD.
+/// \return No return.
+void Surv_UpdateAliveStats()
+{
+ // Debug stuff
+ if (surv_attackerteam == NUM_TEAM_1)
+ {
+ redalive = surv_numattackersalive;
+ bluealive = surv_numdefendersalive;
+ }
+ else
+ {
+ bluealive = surv_numattackersalive;
+ redalive = surv_numdefendersalive;
+ }
+ FOREACH_CLIENT(IS_REAL_CLIENT(it),
+ {
+ STAT(SURV_DEFENDERS_ALIVE, it) = surv_numdefendersalive;
+ it.redalive_stat = redalive;
+ it.bluealive_stat = bluealive;
+ });
+ Surv_UpdateDefenderHealthStat();
+}
+
+/// \brief Updates defender health on the HUD.
+/// \return No return.
+void Surv_UpdateDefenderHealthStat()
+{
+ float maxhealth;
+ float totalhealth = 0;
+ if (autocvar_g_instagib == 1)
+ {
+ maxhealth = surv_numdefenders * (PlayerTemplate_GetFloatValue(
+ "surv_defender", "start_armor") + 1);
+ FOREACH_CLIENT(IS_PLAYER(it),
+ {
+ if (it.team == surv_defenderteam)
+ {
+ totalhealth += GetResourceAmount(it, RESOURCE_ARMOR) + 1;
+ }
+ });
+ }
+ else
+ {
+ maxhealth = surv_numdefenders * (PlayerTemplate_GetFloatValue(
+ "surv_defender", "start_health") + PlayerTemplate_GetFloatValue(
+ "surv_defender", "start_armor"));
+ FOREACH_CLIENT(IS_PLAYER(it),
+ {
+ if (it.team == surv_defenderteam)
+ {
+ totalhealth += GetResourceAmount(it, RESOURCE_HEALTH);
+ totalhealth += GetResourceAmount(it, RESOURCE_ARMOR);
+ }
+ });
+ }
+ float healthratio;
+ if (maxhealth == 0)
+ {
+ healthratio = 0;
+ }
+ else
+ {
+ healthratio = totalhealth / maxhealth;
+ }
+ FOREACH_CLIENT(IS_REAL_CLIENT(it),
+ {
+ STAT(SURV_DEFENDER_HEALTH, it) = healthratio;
+ });
+}
+
+/// \brief Returns whether the player can spawn.
+/// \param[in] player Player to check.
+/// \return True if the player can spawn, false otherwise.
+bool Surv_CanPlayerSpawn(entity player)
+{
+ if ((player.team == surv_attackerteam) ||
+ (player.surv_savedplayerstate != NULL))
+ {
+ return true;
+ }
+ return surv_allowed_to_spawn;
+}
+
+/// \brief Switches the round type.
+/// \return No return.
+void Surv_SwitchRoundType()
+{
+ switch (surv_roundtype)
+ {
+ case SURVIVAL_ROUND_FIRST:
+ {
+ surv_roundtype = SURVIVAL_ROUND_SECOND;
+ return;
+ }
+ case SURVIVAL_ROUND_SECOND:
+ {
+ surv_roundtype = SURVIVAL_ROUND_FIRST;
+ return;
+ }
+ }
+}
+
+/// \brief Cleans up the mess after the round has finished.
+/// \return No return.
+void Surv_RoundCleanup()
+{
+ surv_allowed_to_spawn = false;
+ surv_isroundactive = false;
+ game_stopped = true;
+ FOREACH_CLIENT(true,
+ {
+ if (it.surv_attack_sprite)
+ {
+ WaypointSprite_Kill(it.surv_attack_sprite);
+ }
+ if (it.surv_defend_sprite)
+ {
+ WaypointSprite_Kill(it.surv_defend_sprite);
+ }
+ if (it.surv_savedplayerstate)
+ {
+ delete(it.surv_savedplayerstate);
+ it.surv_savedplayerstate = NULL;
+ }
+ });
+ if (surv_type == SURVIVAL_TYPE_VERSUS)
+ {
+ Surv_SwitchRoundType();
+ round_handler_Init(5, autocvar_g_surv_warmup, surv_timetobeat);
+ return;
+ }
+ round_handler_Init(5, autocvar_g_surv_warmup,
+ autocvar_g_surv_round_timelimit);
+}
+
+/// \brief Swaps attacker and defender teams.
+/// \return No return.
+void Surv_SwapTeams()
+{
+ int temp = surv_attackerteam;
+ surv_attackerteam = surv_defenderteam;
+ surv_defenderteam = temp;
+ temp = surv_attackerteambit;
+ surv_attackerteambit = surv_defenderteambit;
+ surv_defenderteambit = temp;
+ temp = surv_numattackers;
+ surv_numattackers = surv_numdefenders;
+ surv_numdefenders = temp;
+ temp = surv_numattackerhumans;
+ surv_numattackerhumans = surv_numdefenderhumans;
+ surv_numdefenderhumans = temp;
+ FOREACH_CLIENT(true,
+ {
+ if ((it.team == surv_defenderteam) && (it.surv_role ==
+ SURVIVAL_ROLE_CANNON_FODDER))
+ {
+ SetPlayerTeamSimple(it, surv_attackerteam);
+ }
+ });
+ FOREACH_CLIENT(IS_REAL_CLIENT(it),
+ {
+ STAT(SURV_DEFENDER_TEAM, it) = Team_TeamToNumber(surv_defenderteam);
+ });
+}
+
+/// \brief Forces the overkill model for specific player.
+/// \param[in,out] player Player to force the model of.
+/// \return No return.
+void Surv_ForceOverkillPlayerModel(entity player)
+{
+ switch (player.team)
+ {
+ case NUM_TEAM_1:
+ {
+ switch (floor(random() * 4))
+ {
+ case 0:
+ {
+ player.surv_playermodel = "models/ok_player/okrobot1.dpm";
+ return;
+ }
+ case 1:
+ {
+ player.surv_playermodel = "models/ok_player/okrobot2.dpm";
+ return;
+ }
+ case 2:
+ {
+ player.surv_playermodel = "models/ok_player/okrobot3.dpm";
+ return;
+ }
+ case 3:
+ {
+ player.surv_playermodel = "models/ok_player/okrobot4.dpm";
+ return;
+ }
+ }
+ return;
+ }
+ case NUM_TEAM_2:
+ {
+ switch (floor(random() * 4))
+ {
+ case 0:
+ {
+ player.surv_playermodel = "models/ok_player/okmale1.dpm";
+ return;
+ }
+ case 1:
+ {
+ player.surv_playermodel = "models/ok_player/okmale2.dpm";
+ return;
+ }
+ case 2:
+ {
+ player.surv_playermodel = "models/ok_player/okmale3.dpm";
+ return;
+ }
+ case 3:
+ {
+ player.surv_playermodel = "models/ok_player/okmale4.dpm";
+ return;
+ }
+ }
+ return;
+ }
+ }
+}
+
+/// \brief Determines the player model to the one configured for the gamemode.
+/// \param[in,out] player Player to determine the model of.
+/// \return No return.
+void Surv_DeterminePlayerModel(entity player)
+{
+ switch (player.team)
+ {
+ case surv_attackerteam:
+ {
+ switch (player.surv_role)
+ {
+ case SURVIVAL_ROLE_PLAYER:
+ {
+ if (!autocvar_g_surv_attacker_force_overkill_models)
+ {
+ player.surv_playermodel = player.surv_savedplayermodel;
+ return;
+ }
+ Surv_ForceOverkillPlayerModel(player);
+ return;
+ }
+ case SURVIVAL_ROLE_CANNON_FODDER:
+ {
+ if (!autocvar_g_surv_cannon_fodder_force_overkill_models)
+ {
+ player.surv_playermodel = player.surv_savedplayermodel;
+ return;
+ }
+ Surv_ForceOverkillPlayerModel(player);
+ return;
+ }
+ }
+ }
+ case surv_defenderteam:
+ {
+ if (!autocvar_g_surv_defender_force_overkill_models)
+ {
+ player.surv_playermodel = player.surv_savedplayermodel;
+ return;
+ }
+ Surv_ForceOverkillPlayerModel(player);
+ return;
+ }
+ }
+}
+
+/// \brief Setups a waypoint sprite used to track defenders.
+/// \param[in] player Player to attach sprite too.
+/// \return No return.
+void Surv_SetupWaypointSprite(entity player)
+{
+ WaypointSprite_Spawn(WP_SurvivalKill, 0, 0, player, '0 0 64', NULL,
+ surv_attackerteam, player, surv_attack_sprite, false,
+ RADARICON_OBJECTIVE);
+ WaypointSprite_Spawn(WP_SurvivalDefend, 0, 0, player, '0 0 64', NULL,
+ surv_defenderteam, player, surv_defend_sprite, false,
+ RADARICON_OBJECTIVE);
+ //player.surv_attack_sprite.colormod = colormapPaletteColor(player.team - 1,
+ // false);
+ //player.surv_defend_sprite.colormod = colormapPaletteColor(player.team - 1,
+ // false);
+ float max_hp;
+ if (autocvar_g_instagib == 1)
+ {
+ max_hp = PlayerTemplate_GetFloatValue(Surv_GetPlayerTemplate(player),
+ "start_armor") + 1;
+ WaypointSprite_UpdateMaxHealth(player.surv_attack_sprite, max_hp);
+ WaypointSprite_UpdateMaxHealth(player.surv_defend_sprite, max_hp);
+ Surv_UpdateWaypointSpriteHealth(player);
+ return;
+ }
+ max_hp = PlayerTemplate_GetFloatValue(Surv_GetPlayerTemplate(player),
+ "start_health") + PlayerTemplate_GetFloatValue(Surv_GetPlayerTemplate(
+ player), "start_armor");
+ WaypointSprite_UpdateMaxHealth(player.surv_attack_sprite, max_hp);
+ WaypointSprite_UpdateMaxHealth(player.surv_defend_sprite, max_hp);
+ Surv_UpdateWaypointSpriteHealth(player);
+}
+
+void Surv_UpdateWaypointSpriteHealth(entity player)
+{
+ float hp;
+ if (autocvar_g_instagib == 1)
+ {
+ hp = GetResourceAmount(player, RESOURCE_ARMOR) + 1;
+ }
+ else
+ {
+ hp = GetResourceAmount(player, RESOURCE_HEALTH) + GetResourceAmount(
+ player, RESOURCE_ARMOR);
+ }
+ WaypointSprite_UpdateHealth(player.surv_attack_sprite, hp);
+ WaypointSprite_UpdateHealth(player.surv_defend_sprite, hp);
+}
+
+//=============================== Callbacks ===================================
+
+bool Surv_CanRoundStart()
+{
+ return (surv_numattackersalive > 0) && (surv_numdefendersalive > 0);
+}
+
+bool Surv_CanRoundEnd()
+{
+ if (warmup_stage)
+ {
+ return false;
+ }
+ if((round_handler_GetEndTime() > 0) && (round_handler_GetEndTime() -
+ time <= 0))
+ {
+ if (surv_roundtype == SURVIVAL_ROUND_FIRST)
+ {
+ surv_timetobeat = time - surv_roundstarttime;
+ Send_Notification(NOTIF_ALL, NULL, MSG_CENTER,
+ CENTER_SURVIVAL_DEFENDERS_SURVIVED);
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO,
+ INFO_SURVIVAL_DEFENDERS_SURVIVED);
+ Surv_RoundCleanup();
+ return true;
+ }
+ surv_timetobeat = autocvar_g_surv_round_timelimit;
+ Send_Notification(NOTIF_ALL, NULL, MSG_CENTER,
+ CENTER_SURVIVAL_DEFENDERS_SURVIVED);
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO,
+ INFO_SURVIVAL_DEFENDERS_SURVIVED);
+ switch (surv_defenderteam)
+ {
+ case NUM_TEAM_1:
+ {
+ sound(NULL, CH_TRIGGER, SND_SURV_RED_SCORES, VOL_BASE,
+ ATTEN_NONE);
+ break;
+ }
+ case NUM_TEAM_2:
+ {
+ sound(NULL, CH_TRIGGER, SND_SURV_BLUE_SCORES, VOL_BASE,
+ ATTEN_NONE);
+ break;
+ }
+ }
+ TeamScore_AddToTeam(surv_defenderteam, 1, 1);
+ Surv_RoundCleanup();
+ return true;
+ }
+ if (surv_numdefendersalive > 0)
+ {
+ return false;
+ }
+ if (surv_roundtype == SURVIVAL_ROUND_FIRST)
+ {
+ surv_timetobeat = time - surv_roundstarttime;
+ Send_Notification(NOTIF_ALL, NULL, MSG_CENTER,
+ CENTER_SURVIVAL_DEFENDERS_ELIMINATED_IN, surv_timetobeat);
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO,
+ INFO_SURVIVAL_DEFENDERS_ELIMINATED_IN, surv_timetobeat);
+ Surv_RoundCleanup();
+ return true;
+ }
+ surv_timetobeat = autocvar_g_surv_round_timelimit;
+ Send_Notification(NOTIF_ALL, NULL, MSG_CENTER,
+ CENTER_SURVIVAL_DEFENDERS_ELIMINATED);
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO,
+ INFO_SURVIVAL_DEFENDERS_ELIMINATED);
+ switch (surv_attackerteam)
+ {
+ case NUM_TEAM_1:
+ {
+ sound(NULL, CH_TRIGGER, SND_SURV_RED_SCORES, VOL_BASE,
+ ATTEN_NONE);
+ break;
+ }
+ case NUM_TEAM_2:
+ {
+ sound(NULL, CH_TRIGGER, SND_SURV_BLUE_SCORES, VOL_BASE,
+ ATTEN_NONE);
+ break;
+ }
+ }
+ TeamScore_AddToTeam(surv_attackerteam, 1, 1);
+ Surv_RoundCleanup();
+ return true;
+}
+
+void Surv_RoundStart()
+{
+ if (warmup_stage)
+ {
+ surv_allowed_to_spawn = true;
+ return;
+ }
+ surv_isroundactive = true;
+ surv_roundstarttime = time;
+ surv_allowed_to_spawn = false;
+ switch (surv_roundtype)
+ {
+ case SURVIVAL_ROUND_FIRST:
+ {
+ FOREACH_CLIENT(IS_PLAYER(it),
+ {
+ if (it.team == surv_attackerteam)
+ {
+ Send_Notification(NOTIF_TEAM, it, MSG_CENTER,
+ CENTER_SURVIVAL_1ST_ROUND_ATTACKER);
+ Send_Notification(NOTIF_TEAM, it, MSG_INFO,
+ INFO_SURVIVAL_1ST_ROUND_ATTACKER);
+ break;
+ }
+ });
+ FOREACH_CLIENT(IS_PLAYER(it),
+ {
+ if (it.team == surv_defenderteam)
+ {
+ if (surv_type == SURVIVAL_TYPE_COOP)
+ {
+ Send_Notification(NOTIF_TEAM, it, MSG_CENTER,
+ CENTER_SURVIVAL_COOP_DEFENDER);
+ Send_Notification(NOTIF_TEAM, it, MSG_INFO,
+ INFO_SURVIVAL_COOP_DEFENDER);
+ break;
+ }
+ Send_Notification(NOTIF_TEAM, it, MSG_CENTER,
+ CENTER_SURVIVAL_1ST_ROUND_DEFENDER);
+ Send_Notification(NOTIF_TEAM, it, MSG_INFO,
+ INFO_SURVIVAL_1ST_ROUND_DEFENDER);
+ break;
+ }
+ });
+ break;
+ }
+ case SURVIVAL_ROUND_SECOND:
+ {
+ FOREACH_CLIENT(IS_PLAYER(it),
+ {
+ if (it.team == surv_attackerteam)
+ {
+ Send_Notification(NOTIF_TEAM, it, MSG_CENTER,
+ CENTER_SURVIVAL_2ND_ROUND_ATTACKER, surv_timetobeat);
+ Send_Notification(NOTIF_TEAM, it, MSG_INFO,
+ INFO_SURVIVAL_2ND_ROUND_ATTACKER, surv_timetobeat);
+ break;
+ }
+ });
+ FOREACH_CLIENT(IS_PLAYER(it),
+ {
+ if (it.team == surv_defenderteam)
+ {
+ Send_Notification(NOTIF_TEAM, it, MSG_CENTER,
+ CENTER_SURVIVAL_2ND_ROUND_DEFENDER, surv_timetobeat);
+ Send_Notification(NOTIF_TEAM, it, MSG_INFO,
+ INFO_SURVIVAL_2ND_ROUND_DEFENDER, surv_timetobeat);
+ break;
+ }
+ });
+ break;
+ }
+ }
+ if (autocvar_g_surv_stealth)
+ {
+ return;
+ }
+ FOREACH_CLIENT(IS_PLAYER(it),
+ {
+ switch (it.team)
+ {
+ case surv_defenderteam:
+ {
+ if (it.surv_role == SURVIVAL_ROLE_PLAYER)
+ {
+ Surv_SetupWaypointSprite(it);
+ }
+ break;
+ }
+ }
+ });
+}
+
+bool Surv_IsEliminated(entity player)
+{
+ switch (player.surv_state)
+ {
+ case SURVIVAL_STATE_NOT_PLAYING:
+ {
+ return true;
+ }
+ case SURVIVAL_STATE_PLAYING:
+ {
+ if (player.surv_role == SURVIVAL_ROLE_CANNON_FODDER)
+ {
+ // A hack until proper scoreboard is done.
+ return true;
+ }
+ if ((player.team == surv_defenderteam) && (IS_DEAD(player) ||
+ IS_OBSERVER(player)))
+ {
+ return true;
+ }
+ return false;
+ }
+ }
+ // Should never reach here
+ return true;
+}
+
+//============================= Hooks ========================================
+
+/// \brief Hook that is called to determine general rules of the game.
+MUTATOR_HOOKFUNCTION(surv, ReadLevelCvars)
+{
+ surv_warmup = warmup_stage;
+}
+
+/// \brief Hook that is called to determine if there is a weapon arena.
+MUTATOR_HOOKFUNCTION(surv, SetWeaponArena)
+{
+ // Removing any weapon arena.
+ M_ARGV(0, string) = "off";
+}
+
+/// \brief Hook that is called to determine start items of all players.
+MUTATOR_HOOKFUNCTION(surv, SetStartItems)
+{
+ if (autocvar_g_instagib == 1)
+ {
+ return;
+ }
+ start_weapons = WEPSET(Null);
+ warmup_start_weapons = WEPSET(Null);
+}
+
+/// \brief Hook that is called on every frame.
+MUTATOR_HOOKFUNCTION(surv, SV_StartFrame)
+{
+ if (game_stopped || !surv_isroundactive)
+ {
+ return;
+ }
+ float roundtime = 0;
+ switch (surv_roundtype)
+ {
+ case SURVIVAL_ROUND_FIRST:
+ {
+ roundtime = time - surv_roundstarttime;
+ break;
+ }
+ case SURVIVAL_ROUND_SECOND:
+ {
+ roundtime = round_handler_GetEndTime() - time;
+ break;
+ }
+ }
+ FOREACH_CLIENT(IS_REAL_CLIENT(it),
+ {
+ STAT(SURV_ROUND_TIME, it) = roundtime;
+ });
+}
+
+/// \brief Hook that determines which team player can join. This is called
+/// before ClientConnect.
+MUTATOR_HOOKFUNCTION(surv, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
+{
+ entity player = M_ARGV(2, entity);
+ LOG_TRACE("Survival: CheckAllowedTeams, player = ", player.netname);
+ if (player == NULL)
+ {
+ return SURVIVAL_TEAM_BITS;
+ }
+ if (IS_BOT_CLIENT(player))
+ {
+ int teambits = surv_attackerteambit;
+ if ((player.team == surv_defenderteam) || (surv_numdefenders <
+ autocvar_g_surv_team_size))
+ {
+ teambits |= surv_defenderteambit;
+ }
+ M_ARGV(0, float) = teambits;
+ return;
+ }
+ if (surv_type == SURVIVAL_TYPE_COOP)
+ {
+ if (surv_numdefenderhumans < autocvar_g_surv_team_size)
+ {
+ M_ARGV(0, float) = surv_defenderteambit;
+ return;
+ }
+ M_ARGV(0, float) = 0;
+ return;
+ }
+ int teambits = 0;
+ if (surv_numattackerhumans < autocvar_g_surv_team_size)
+ {
+ LOG_TRACE("Player can join attackers");
+ teambits |= surv_attackerteambit;
+ }
+ if (surv_numdefenderhumans < autocvar_g_surv_team_size)
+ {
+ LOG_TRACE("Player can join defenders");
+ teambits |= surv_defenderteambit;
+ }
+ M_ARGV(0, float) = teambits;
+ return;
+}
+
+/// \brief Hook that override team counts.
+MUTATOR_HOOKFUNCTION(surv, GetTeamCounts, CBC_ORDER_EXCLUSIVE)
+{
+ return true;
+}
+
+/// \brief Hook that sets the team count.
+MUTATOR_HOOKFUNCTION(surv, GetTeamCount, CBC_ORDER_EXCLUSIVE)
+{
+ float teamnum = M_ARGV(0, float);
+ entity ignore = M_ARGV(1, entity);
+ switch (teamnum)
+ {
+ case surv_attackerteam:
+ {
+ M_ARGV(2, float) = surv_numattackers;
+ M_ARGV(3, float) = surv_numattackers - surv_numattackerhumans;
+ if (ignore.team == surv_attackerteam)
+ {
+ --M_ARGV(2, float);
+ if (IS_BOT_CLIENT(ignore))
+ {
+ --M_ARGV(3, float);
+ }
+ }
+ entity lowestplayer = NULL;
+ float lowestplayerscore = FLOAT_MAX;
+ entity lowestbot = NULL;
+ float lowestbotscore = FLOAT_MAX;
+ FOREACH_CLIENT((it.team == surv_attackerteam) && (it.surv_role ==
+ SURVIVAL_ROLE_PLAYER),
+ {
+ if (it == ignore)
+ {
+ continue;
+ }
+ if (IS_BOT_CLIENT(it))
+ {
+ float tempscore = PlayerScore_Get(it, SP_SCORE);
+ if (tempscore < lowestbotscore)
+ {
+ lowestbot = it;
+ lowestbotscore = tempscore;
+ continue;
+ }
+ }
+ float tempscore = PlayerScore_Get(it, SP_SCORE);
+ if (tempscore < lowestplayerscore)
+ {
+ lowestplayer = it;
+ lowestplayerscore = tempscore;
+ }
+ });
+ M_ARGV(4, entity) = lowestplayer;
+ M_ARGV(5, entity) = lowestbot;
+ break;
+ }
+ case surv_defenderteam:
+ {
+ M_ARGV(2, float) = surv_numdefenders;
+ M_ARGV(3, float) = surv_numdefenders - surv_numdefenderhumans;
+ if (ignore.team == surv_defenderteam)
+ {
+ --M_ARGV(2, float);
+ if (IS_BOT_CLIENT(ignore))
+ {
+ --M_ARGV(3, float);
+ }
+ }
+ entity lowestplayer = NULL;
+ float lowestplayerscore = FLOAT_MAX;
+ entity lowestbot = NULL;
+ float lowestbotscore = FLOAT_MAX;
+ FOREACH_CLIENT((it.team == surv_defenderteam),
+ {
+ if (it == ignore)
+ {
+ continue;
+ }
+ if (IS_BOT_CLIENT(it))
+ {
+ float tempscore = PlayerScore_Get(it, SP_SCORE);
+ if (tempscore < lowestbotscore)
+ {
+ lowestbot = it;
+ lowestbotscore = tempscore;
+ continue;
+ }
+ }
+ float tempscore = PlayerScore_Get(it, SP_SCORE);
+ if (tempscore < lowestplayerscore)
+ {
+ lowestplayer = it;
+ lowestplayerscore = tempscore;
+ }
+ });
+ M_ARGV(4, entity) = lowestplayer;
+ M_ARGV(5, entity) = lowestbot;
+ break;
+ }
+ }
+ return true;
+}
+
+/// \brief Hook that determines the best teams for the player to join.
+MUTATOR_HOOKFUNCTION(surv, FindBestTeams, CBC_ORDER_EXCLUSIVE)
+{
+ if (surv_type == SURVIVAL_TYPE_COOP)
+ {
+ return false;
+ }
+ entity player = M_ARGV(0, entity);
+ if (IS_BOT_CLIENT(player))
+ {
+ return false;
+ }
+ int numattackerhumans = surv_numattackerhumans;
+ int numdefenderhumans = surv_numdefenderhumans;
+ if (player.team == surv_attackerteam)
+ {
+ --numattackerhumans;
+ }
+ else if (player.team == surv_defenderteam)
+ {
+ --numdefenderhumans;
+ }
+ if (numattackerhumans < numdefenderhumans)
+ {
+ M_ARGV(1, float) = BIT(Team_TeamToNumber(surv_attackerteam) - 1);
+ return true;
+ }
+ if (numattackerhumans > numdefenderhumans)
+ {
+ M_ARGV(1, float) = BIT(Team_TeamToNumber(surv_defenderteam) - 1);
+ return true;
+ }
+ M_ARGV(1, float) = SURVIVAL_TEAM_BITS;
+ return true;
+}
+
+/// \brief Hook that is called when player has changed the team.
+MUTATOR_HOOKFUNCTION(surv, Player_ChangedTeam)
+{
+ entity player = M_ARGV(0, entity);
+ int oldteam = M_ARGV(1, float);
+ int newteam = M_ARGV(2, float);
+ string message = strcat("Survival: Player_ChangedTeam, ", player.netname,
+ ", old team = ", ftos(oldteam), " new team = ", ftos(newteam));
+ LOG_TRACE(message);
+ DebugPrintToChatAll(message);
+ if ((oldteam != -1) && IS_PLAYER(player) && !IS_DEAD(player))
+ {
+ Surv_RemovePlayerFromAliveList(player, oldteam);
+ }
+ Surv_RemovePlayerFromTeam(player, oldteam);
+ if (Surv_AddPlayerToTeam(player, newteam) == false)
+ {
+ return;
+ }
+ //Surv_CountAlivePlayers();
+ if ((oldteam != -1) && IS_PLAYER(player) && !IS_DEAD(player))
+ {
+ Surv_AddPlayerToAliveList(player, newteam);
+ }
+}
+
+/// \brief Hook that is called when player is about to be killed when changing
+/// teams.
+MUTATOR_HOOKFUNCTION(surv, Player_ChangeTeamKill)
+{
+ entity player = M_ARGV(0, entity);
+ if (player.team != surv_defenderteam)
+ {
+ return false;
+ }
+ if (player.surv_savedplayerstate == NULL)
+ {
+ return false;
+ }
+ Surv_RestorePlayerState(player, player.surv_savedplayerstate);
+ delete(player.surv_savedplayerstate);
+ player.surv_savedplayerstate = NULL;
+ return true;
+}
+
+/// \brief Hook that is called when player is about to be killed as a result of
+/// the kill command or changing teams.
+MUTATOR_HOOKFUNCTION(surv, ClientKill_Now)
+{
+ entity player = M_ARGV(0, entity);
+ if (player.team == surv_defenderteam)
+ {
+ // Deny suicide.
+ return true;
+ }
+}
+
+/// \brief Hook that is called when player connects to the server.
+MUTATOR_HOOKFUNCTION(surv, ClientConnect)
+{
+ entity player = M_ARGV(0, entity);
+ LOG_TRACE("Survival: ClientConnect, player = ", player.netname);
+ player.surv_savedplayermodel = player.playermodel;
+ if (IS_REAL_CLIENT(player))
+ {
+ STAT(SURV_DEFENDER_TEAM, player) = Team_TeamToNumber(surv_defenderteam);
+ STAT(SURV_DEFENDERS_ALIVE, player) = surv_numdefendersalive;
+ player.redalive_stat = redalive;
+ player.bluealive_stat = bluealive;
+ player.yellowalive_stat = yellowalive;
+ player.pinkalive_stat = pinkalive;
+ }
+ if (player.surv_role == SURVIVAL_ROLE_NONE)
+ {
+ Surv_AddPlayerToTeam(player, player.team);
+ }
+ return true;
+}
+
+/// \brief Hook that is called when player disconnects from the server.
+MUTATOR_HOOKFUNCTION(surv, ClientDisconnect)
+{
+ entity player = M_ARGV(0, entity);
+ if (!IS_DEAD(player))
+ {
+ Surv_RemovePlayerFromAliveList(player, player.team);
+ }
+ Surv_RemovePlayerFromTeam(player, player.team);
+ //Surv_CountAlivePlayers();
+}
+
+/// \brief Hook that determines whether player can spawn. It is not called for
+/// players who have joined the team and are dead.
+MUTATOR_HOOKFUNCTION(surv, ForbidSpawn)
+{
+ entity player = M_ARGV(0, entity);
+ LOG_TRACE("Survival: ForbidSpawn, player = ", player.netname);
+ if (player.surv_state == SURVIVAL_STATE_NOT_PLAYING)
+ {
+ return false;
+ }
+ return !Surv_CanPlayerSpawn(player);
+}
+
+MUTATOR_HOOKFUNCTION(surv, PutClientInServer)
+{
+ entity player = M_ARGV(0, entity);
+ LOG_TRACE("Survival: PutClientInServer, player = ", player.netname);
+ if (!Surv_CanPlayerSpawn(player) && IS_PLAYER(player))
+ {
+ LOG_TRACE("Transmuting to observer");
+ TRANSMUTE(Observer, player);
+ }
+}
+
+MUTATOR_HOOKFUNCTION(surv, MakePlayerObserver)
+{
+ entity player = M_ARGV(0, entity);
+ LOG_TRACE("Survival: MakePlayerObserver, player = ", player.netname);
+ if (player.killindicator_teamchange == -2) // player wants to spectate
+ {
+ LOG_TRACE("killindicator_teamchange == -2");
+ player.surv_state = SURVIVAL_STATE_NOT_PLAYING;
+ }
+ if (player.surv_state == SURVIVAL_STATE_NOT_PLAYING)
+ {
+ return false; // allow team reset
+ }
+ return true; // prevent team reset
+}
+
+MUTATOR_HOOKFUNCTION(surv, reset_map_global)
+{
+ LOG_TRACE("Survival: reset_map_global");
+ surv_allowed_to_spawn = true;
+ if (surv_roundtype == SURVIVAL_ROUND_FIRST)
+ {
+ FOREACH_CLIENT(IS_REAL_CLIENT(it),
+ {
+ STAT(SURV_ROUND_TIME, it) = 0;
+ });
+ }
+ return true;
+}
+
+MUTATOR_HOOKFUNCTION(surv, reset_map_players)
+{
+ LOG_TRACE("Survival: reset_map_players");
+ surv_numattackersalive = 0;
+ surv_numdefendersalive = 0;
+ if (surv_warmup)
+ {
+ surv_warmup = false;
+ }
+ else if (surv_type == SURVIVAL_TYPE_VERSUS)
+ {
+ Surv_SwapTeams();
+ }
+ FOREACH_CLIENT(true,
+ {
+ it.killcount = 0;
+ if ((it.surv_state == SURVIVAL_STATE_NOT_PLAYING) && IS_BOT_CLIENT(it))
+ {
+ it.team = -1;
+ it.surv_state = SURVIVAL_STATE_PLAYING;
+ }
+ if (it.surv_state == SURVIVAL_STATE_PLAYING)
+ {
+ TRANSMUTE(Player, it);
+ it.surv_state = SURVIVAL_STATE_PLAYING;
+ PutClientInServer(it);
+ }
+ });
+ bot_relinkplayerlist();
+ return true;
+}
+
+/// \brief Hook that is called when player spawns.
+MUTATOR_HOOKFUNCTION(surv, PlayerSpawn)
+{
+ entity player = M_ARGV(0, entity);
+ LOG_TRACE("Survival: PlayerSpawn, player = ", player.netname);
+ player.surv_state = SURVIVAL_STATE_PLAYING;
+ Surv_DeterminePlayerModel(player);
+ if (player.surv_savedplayerstate != NULL)
+ {
+ Surv_RestorePlayerState(player, player.surv_savedplayerstate);
+ delete(player.surv_savedplayerstate);
+ player.surv_savedplayerstate = NULL;
+ }
+ else
+ {
+ PlayerTemplateHook_PlayerSpawn(player, Surv_GetPlayerTemplate(player));
+ }
+ switch (player.team)
+ {
+ case surv_attackerteam:
+ {
+ if (player.surv_role == SURVIVAL_ROLE_PLAYER)
+ {
+ Send_Notification(NOTIF_ONE, player, MSG_CENTER,
+ CENTER_ASSAULT_ATTACKING);
+ }
+ break;
+ }
+ case surv_defenderteam:
+ {
+ Send_Notification(NOTIF_ONE, player, MSG_CENTER,
+ CENTER_ASSAULT_DEFENDING);
+ break;
+ }
+ }
+ //Surv_CountAlivePlayers();
+ Surv_AddPlayerToAliveList(player, player.team);
+}
+
+/// \brief UGLY HACK. This is called every frame to keep player model correct.
+MUTATOR_HOOKFUNCTION(surv, FixPlayermodel)
+{
+ entity player = M_ARGV(2, entity);
+ M_ARGV(0, string) = player.surv_playermodel;
+}
+
+/// \brief Hook which is called when the player tries to throw their weapon.
+MUTATOR_HOOKFUNCTION(surv, ForbidThrowCurrentWeapon)
+{
+ entity player = M_ARGV(0, entity);
+ return PlayerTemplateHook_ForbidThrowCurrentWeapon(
+ Surv_GetPlayerTemplate(player));
+}
+
+/// \brief Hook that is called every frame to determine how player health should
+/// regenerate.
+MUTATOR_HOOKFUNCTION(surv, PlayerRegen)
+{
+ entity player = M_ARGV(0, entity);
+ if (player.team == surv_defenderteam)
+ {
+ return true;
+ }
+ return PlayerTemplateHook_PlayerRegen(player,
+ Surv_GetPlayerTemplate(player));
+}
+
+/// \brief Hook that is called to determine if balance messages will appear.
+MUTATOR_HOOKFUNCTION(surv, HideTeamNagger)
+{
+ return true;
+}
+
+/// \brief Hook that is called when player touches an item.
+MUTATOR_HOOKFUNCTION(surv, ItemTouch)
+{
+ entity item = M_ARGV(0, entity);
+ entity player = M_ARGV(1, entity);
+ switch (player.team)
+ {
+ case surv_attackerteam:
+ {
+ return PlayerTemplateHook_ItemTouch(player, item,
+ Surv_GetPlayerTemplate(player));
+ }
+ case surv_defenderteam:
+ {
+ switch (item.classname)
+ {
+ case "item_strength":
+ {
+ W_GiveWeapon(player, WEP_HMG.m_id);
+ player.superweapons_finished = max(
+ player.superweapons_finished, time) +
+ autocvar_g_balance_superweapons_time;
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_Strength, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ case "item_invincible":
+ {
+ W_GiveWeapon(player, WEP_RPC.m_id);
+ player.superweapons_finished = max(
+ player.superweapons_finished, time) +
+ autocvar_g_balance_superweapons_time;
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_Shield, VOL_BASE, ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return PlayerTemplateHook_ItemTouch(player, item,
+ Surv_GetPlayerTemplate(player));
+ }
+ }
+ DebugPrintToChat(player, item.classname);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ }
+ return MUT_ITEMTOUCH_CONTINUE;
+}
+
+/// \brief Hook which is called when the damage amount must be determined.
+MUTATOR_HOOKFUNCTION(surv, Damage_Calculate)
+{
+ entity frag_attacker = M_ARGV(1, entity);
+ entity frag_target = M_ARGV(2, entity);
+ float deathtype = M_ARGV(3, float);
+ float damage = M_ARGV(4, float);
+ M_ARGV(4, float) = PlayerTemplateHook_Damage_Calculate(frag_attacker,
+ Surv_GetPlayerTemplate(frag_attacker), frag_target,
+ Surv_GetPlayerTemplate(frag_target), deathtype, damage);
+}
+
+/// \brief Hook which is called when the player was damaged.
+MUTATOR_HOOKFUNCTION(surv, PlayerDamaged)
+{
+ entity target = M_ARGV(1, entity);
+ if (target.team != surv_defenderteam)
+ {
+ return;
+ }
+ Surv_UpdateDefenderHealthStat();
+ entity attacker = M_ARGV(0, entity);
+ if ((attacker.team == surv_attackerteam) && (attacker.surv_role ==
+ SURVIVAL_ROLE_PLAYER))
+ {
+ float health = M_ARGV(2, float);
+ float armor = M_ARGV(3, float);
+ float score = (health + armor) * autocvar_g_surv_attacker_damage_score;
+ GameRules_scoring_add(attacker, SCORE, score);
+ }
+ if (autocvar_g_surv_stealth)
+ {
+ return;
+ }
+ if (GetResourceAmount(target, RESOURCE_HEALTH) < 1)
+ {
+ WaypointSprite_Kill(target.surv_attack_sprite);
+ WaypointSprite_Kill(target.surv_defend_sprite);
+ }
+ else
+ {
+ Surv_UpdateWaypointSpriteHealth(target);
+ }
+}
+
+/// \brief Hook which is called when the player dies.
+MUTATOR_HOOKFUNCTION(surv, PlayerDies, CBC_ORDER_FIRST)
+{
+ //DebugPrintToChatAll("PlayerDies");
+ entity attacker = M_ARGV(1, entity);
+ entity victim = M_ARGV(2, entity);
+ PlayerTemplateHook_PlayerDies(victim, Surv_GetPlayerTemplate(victim));
+ if ((attacker.team == surv_defenderteam) &&
+ (victim.team == surv_attackerteam))
+ {
+ switch (victim.surv_role)
+ {
+ case SURVIVAL_ROLE_PLAYER:
+ {
+ GiveResource(attacker, RESOURCE_HEALTH,
+ autocvar_g_surv_defender_attacker_frag_health);
+ GiveResource(attacker, RESOURCE_ARMOR,
+ autocvar_g_surv_defender_attacker_frag_armor);
+ GiveResource(attacker, RESOURCE_SHELLS,
+ autocvar_g_surv_defender_attacker_frag_shells);
+ GiveResource(attacker, RESOURCE_BULLETS,
+ autocvar_g_surv_defender_attacker_frag_bullets);
+ GiveResource(attacker, RESOURCE_ROCKETS,
+ autocvar_g_surv_defender_attacker_frag_rockets);
+ GiveResource(attacker, RESOURCE_CELLS,
+ autocvar_g_surv_defender_attacker_frag_cells);
+ GiveResource(attacker, RESOURCE_PLASMA,
+ autocvar_g_surv_defender_attacker_frag_plasma);
+ GiveResource(attacker, RESOURCE_FUEL,
+ autocvar_g_surv_defender_attacker_frag_fuel);
+ break;
+ }
+ case SURVIVAL_ROLE_CANNON_FODDER:
+ {
+ GiveResource(attacker, RESOURCE_HEALTH,
+ autocvar_g_surv_defender_cannon_fodder_frag_health);
+ GiveResource(attacker, RESOURCE_ARMOR,
+ autocvar_g_surv_defender_cannon_fodder_frag_armor);
+ GiveResource(attacker, RESOURCE_SHELLS,
+ autocvar_g_surv_defender_cannon_fodder_frag_shells);
+ GiveResource(attacker, RESOURCE_BULLETS,
+ autocvar_g_surv_defender_cannon_fodder_frag_bullets);
+ GiveResource(attacker, RESOURCE_ROCKETS,
+ autocvar_g_surv_defender_cannon_fodder_frag_rockets);
+ GiveResource(attacker, RESOURCE_CELLS,
+ autocvar_g_surv_defender_cannon_fodder_frag_cells);
+ GiveResource(attacker, RESOURCE_PLASMA,
+ autocvar_g_surv_defender_cannon_fodder_frag_plasma);
+ GiveResource(attacker, RESOURCE_FUEL,
+ autocvar_g_surv_defender_cannon_fodder_frag_fuel);
+ break;
+ }
+ }
+ }
+ if (!Surv_CanPlayerSpawn(victim))
+ {
+ victim.respawn_flags = RESPAWN_SILENT;
+ if (IS_BOT_CLIENT(victim))
+ {
+ bot_clear(victim);
+ }
+ }
+ return true;
+}
+
+/// \brief Hook which is called after the player died.
+MUTATOR_HOOKFUNCTION(surv, PlayerDied)
+{
+ //DebugPrintToChatAll("PlayerDied");
+ entity player = M_ARGV(0, entity);
+ Surv_RemovePlayerFromAliveList(player, player.team);
+ //Surv_CountAlivePlayers();
+}
+
+/// \brief Hook which is called when player has scored a frag.
+MUTATOR_HOOKFUNCTION(surv, GiveFragsForKill, CBC_ORDER_FIRST)
+{
+ if (surv_type == SURVIVAL_TYPE_COOP)
+ {
+ return true;
+ }
+ entity attacker = M_ARGV(0, entity);
+ if ((attacker.team == surv_defenderteam) || (attacker.surv_role ==
+ SURVIVAL_ROLE_CANNON_FODDER))
+ {
+ M_ARGV(2, float) = 0;
+ return true;
+ }
+ entity target = M_ARGV(1, entity);
+ if ((attacker.surv_role == SURVIVAL_ROLE_PLAYER) && (target.team ==
+ surv_defenderteam))
+ {
+ M_ARGV(2, float) = autocvar_g_surv_attacker_frag_score;
+ }
+ return true;
+}
+
+MUTATOR_HOOKFUNCTION(surv, SpectateSet)
+{
+ entity client = M_ARGV(0, entity);
+ entity targ = M_ARGV(1, entity);
+
+ if (!autocvar_g_surv_spectate_enemies &&
+ (client.surv_state == SURVIVAL_STATE_PLAYING) &&
+ DIFF_TEAM(targ, client))
+ {
+ return true;
+ }
+}
+
+MUTATOR_HOOKFUNCTION(surv, SpectateNext)
+{
+ entity client = M_ARGV(0, entity);
+
+ if (!autocvar_g_surv_spectate_enemies &&
+ (client.surv_state == SURVIVAL_STATE_PLAYING))
+ {
+ entity targ = M_ARGV(1, entity);
+ M_ARGV(1, entity) = CA_SpectateNext(client, targ);
+ return true;
+ }
+}
+
+MUTATOR_HOOKFUNCTION(surv, SpectatePrev)
+{
+ entity client = M_ARGV(0, entity);
+ entity targ = M_ARGV(1, entity);
+ entity first = M_ARGV(2, entity);
+
+ if (!autocvar_g_surv_spectate_enemies &&
+ (client.surv_state == SURVIVAL_STATE_PLAYING))
+ {
+ do
+ {
+ targ = targ.chain;
+ }
+ while (targ && DIFF_TEAM(targ, client));
+ if (!targ)
+ {
+ for (targ = first; targ && DIFF_TEAM(targ, client);
+ targ = targ.chain);
+
+ if (targ == client.enemy)
+ {
+ return MUT_SPECPREV_RETURN;
+ }
+ }
+ }
+ M_ARGV(1, entity) = targ;
+ return MUT_SPECPREV_FOUND;
+}
+
+/// \brief I'm not sure exactly what this function does but it is very
+/// important. Without it bots are completely broken. Is it a hack? Of course.
+MUTATOR_HOOKFUNCTION(surv, Bot_FixCount, CBC_ORDER_EXCLUSIVE)
+{
+ FOREACH_CLIENT(IS_REAL_CLIENT(it),
+ {
+ if (IS_PLAYER(it) || (it.surv_state == SURVIVAL_STATE_PLAYING))
+ {
+ ++M_ARGV(0, int);
+ }
+ ++M_ARGV(1, int);
+ });
+ return true;
+}
+
+MUTATOR_HOOKFUNCTION(surv, Scores_CountFragsRemaining)
+{
+ // Don't announce remaining frags
+ return false;
+}
--- /dev/null
+#pragma once
+
+/// \brief Initializes global data for the gametype.
+/// \return No return.
+void Surv_Initialize();
+
+REGISTER_MUTATOR(surv, false)
+{
+ MUTATOR_STATIC();
+ MUTATOR_ONADD
+ {
+ Surv_Initialize();
+ }
+ return 0;
+}
}
ENDCLASS(Gametype)
-REGISTRY(Gametypes, 24)
+REGISTRY(Gametypes, 25) // Lyberta: added 1
#define Gametypes_from(i) _Gametypes_from(i, NULL)
REGISTER_REGISTRY(Gametypes)
REGISTRY_CHECK(Gametypes)
ENDCLASS(Invasion)
REGISTER_GAMETYPE(INVASION, NEW(Invasion));
+//=============================================================================
+
+#ifdef CSQC
+void HUD_Mod_GG(vector pos, vector mySize);
+#endif
+CLASS(GunGame, Gametype)
+ INIT(GunGame)
+ {
+ this.gametype_init(this, _("GunGame"), "gg", "g_gg", false, "", "timelimit=20", _("Kill players with all weapons"));
+ }
+ METHOD(GunGame, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
+ {
+ return true;
+ }
+#ifdef CSQC
+ ATTRIB(GunGame, m_modicons, void(vector pos, vector mySize), HUD_Mod_GG);
+#endif
+ENDCLASS(GunGame)
+REGISTER_GAMETYPE(GUNGAME, NEW(GunGame));
+#define g_gg IS_GAMETYPE(GUNGAME)
+
+//=============================================================================
+
+// Lyberta: adding survival gametype
+
+#ifdef CSQC
+void HUD_Mod_SURV(vector pos, vector mySize);
+#endif
+CLASS(Survival, Gametype)
+ INIT(Survival)
+ {
+ this.gametype_init(this, _("Survival"), "surv", "g_surv", true, "", "timelimit=20 pointlimit=5 teams=2 leadlimit=0", _("Survive as long as you can"));
+ }
+ METHOD(Survival, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
+ {
+ return true;
+ }
+#ifdef CSQC
+ ATTRIB(Survival, m_modicons, void(vector pos, vector mySize), HUD_Mod_SURV);
+#endif
+ENDCLASS(Survival)
+REGISTER_GAMETYPE(SURVIVAL, NEW(Survival));
+#define g_surv IS_GAMETYPE(SURVIVAL)
+
+//=============================================================================
+
const int MAPINFO_FEATURE_WEAPONS = 1; // not defined for instagib-only maps
const int MAPINFO_FEATURE_VEHICLES = 2;
const int MAPINFO_FEATURE_TURRETS = 4;
// generated file; do not modify
#include <common/mutators/mutator/overkill/hmg.qc>
+#include <common/mutators/mutator/overkill/okmachinegun.qc>
+#include <common/mutators/mutator/overkill/okshotgun.qc>
+#include <common/mutators/mutator/overkill/okvortex.qc>
#include <common/mutators/mutator/overkill/overkill.qc>
#ifdef CSQC
#include <common/mutators/mutator/overkill/cl_overkill.qc>
// generated file; do not modify
#include <common/mutators/mutator/overkill/hmg.qh>
+#include <common/mutators/mutator/overkill/okmachinegun.qh>
+#include <common/mutators/mutator/overkill/okshotgun.qh>
+#include <common/mutators/mutator/overkill/okvortex.qh>
#include <common/mutators/mutator/overkill/overkill.qh>
#ifdef CSQC
#include <common/mutators/mutator/overkill/cl_overkill.qh>
{
MUTATOR_ONADD {
cvar_settemp("g_overkill", "1");
- WEP_SHOTGUN.mdl = "ok_shotgun";
- WEP_MACHINEGUN.mdl = "ok_mg";
- WEP_VORTEX.mdl = "ok_sniper";
}
}
return;
}
- W_DecreaseAmmo(WEP_HMG, actor, WEP_CVAR(hmg, ammo), weaponentity);
+ W_DecreaseAmmo(WEP_HMG, actor, WEP_CVAR_PRI(hmg, ammo), weaponentity);
- W_SetupShot (actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(hmg, damage));
+ W_SetupShot (actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(hmg, damage));
if(!autocvar_g_norecoil)
{
actor.punchangle_y = random () - 0.5;
}
- float hmg_spread = bound(WEP_CVAR(hmg, spread_min), WEP_CVAR(hmg, spread_min) + (WEP_CVAR(hmg, spread_add) * actor.(weaponentity).misc_bulletcounter), WEP_CVAR(hmg, spread_max));
- fireBullet(actor, weaponentity, w_shotorg, w_shotdir, hmg_spread, WEP_CVAR(hmg, solidpenetration), WEP_CVAR(hmg, damage), WEP_CVAR(hmg, force), WEP_HMG.m_id, 0);
+ float hmg_spread = bound(WEP_CVAR_PRI(hmg, spread_min), WEP_CVAR_PRI(hmg, spread_min) + (WEP_CVAR_PRI(hmg, spread_add) * actor.(weaponentity).misc_bulletcounter), WEP_CVAR_PRI(hmg, spread_max));
+ fireBullet(actor, weaponentity, w_shotorg, w_shotdir, hmg_spread, WEP_CVAR_PRI(hmg, solidpenetration), WEP_CVAR_PRI(hmg, damage), WEP_CVAR_PRI(hmg, force), WEP_HMG.m_id, 0);
actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1;
}
int slot = weaponslot(weaponentity);
- ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(hmg, refire) * W_WeaponRateFactor(actor);
- weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(hmg, refire), W_HeavyMachineGun_Attack_Auto);
+ ATTACK_FINISHED(actor, slot) = time + WEP_CVAR_PRI(hmg, refire) * W_WeaponRateFactor(actor);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hmg, refire), W_HeavyMachineGun_Attack_Auto);
}
METHOD(HeavyMachineGun, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
METHOD(HeavyMachineGun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
- if(WEP_CVAR(hmg, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR(hmg, ammo)) { // forced reload
+ if ((WEP_CVAR_SEC(hmg, refire_type) == 1) && (fire & 2) && (time >= actor.jump_interval))
+ {
+ // Secondary uses it's own refire timer if refire_type is 1.
+ actor.jump_interval = time + WEP_CVAR_SEC(hmg, refire) * W_WeaponRateFactor(actor);
+ // Ugly hack to reuse the fire mode of the blaster.
+ makevectors(actor.v_angle);
+ Weapon oldwep = actor.(weaponentity).m_weapon; // we can't avoid this hack
+ actor.(weaponentity).m_weapon = WEP_BLASTER;
+ W_Blaster_Attack(
+ actor,
+ weaponentity,
+ WEP_BLASTER.m_id | HITTYPE_SECONDARY,
+ WEP_CVAR_SEC(hmg, shotangle),
+ WEP_CVAR_SEC(hmg, damage),
+ WEP_CVAR_SEC(hmg, edgedamage),
+ WEP_CVAR_SEC(hmg, radius),
+ WEP_CVAR_SEC(hmg, force),
+ WEP_CVAR_SEC(hmg, speed),
+ WEP_CVAR_SEC(hmg, spread),
+ WEP_CVAR_SEC(hmg, delay),
+ WEP_CVAR_SEC(hmg, lifetime)
+ );
+ actor.(weaponentity).m_weapon = oldwep;
+ if ((actor.(weaponentity).wframe == WFRAME_IDLE) ||
+ (actor.(weaponentity).wframe == WFRAME_FIRE2))
+ {
+ // Set secondary fire animation.
+ vector a = '0 0 0';
+ actor.(weaponentity).wframe = WFRAME_FIRE2;
+ a = actor.(weaponentity).anim_fire2;
+ a.z *= g_weaponratefactor;
+ FOREACH_CLIENT(true, LAMBDA(
+ if (it == actor || (IS_SPEC(it) && it.enemy == actor))
+ {
+ wframe_send(it, actor.(weaponentity), a, true);
+ }
+ ));
+ animdecide_setaction(actor, ANIMACTION_SHOOT, true);
+ }
+ }
+ if (WEP_CVAR(hmg, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR_PRI(hmg, ammo))
+ {
+ // Forced reload.
thiswep.wr_reload(thiswep, actor, weaponentity);
- } else
- {
- if (fire & 1)
- if (weapon_prepareattack(thiswep, actor, weaponentity, false, 0))
- {
- actor.(weaponentity).misc_bulletcounter = 0;
- W_HeavyMachineGun_Attack_Auto(thiswep, actor, weaponentity, fire);
- }
- }
+ return;
+ }
+ if (fire & 1) // Primary attack
+ {
+ if (!weapon_prepareattack(thiswep, actor, weaponentity, false, 0))
+ {
+ return;
+ }
+ actor.(weaponentity).misc_bulletcounter = 0;
+ W_HeavyMachineGun_Attack_Auto(thiswep, actor, weaponentity, fire);
+ return;
+ }
+ if ((fire & 2) && (WEP_CVAR_SEC(hmg, refire_type) == 0)) // Secondary attack
+ {
+ if (!weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(hmg, refire)))
+ {
+ return;
+ }
+ // ugly instagib hack to reuse the fire mode of the laser
+ makevectors(actor.v_angle);
+ Weapon oldwep = actor.(weaponentity).m_weapon; // we can't avoid this hack
+ actor.(weaponentity).m_weapon = WEP_BLASTER;
+ W_Blaster_Attack(
+ actor,
+ weaponentity,
+ WEP_BLASTER.m_id | HITTYPE_SECONDARY,
+ WEP_CVAR_SEC(hmg, shotangle),
+ WEP_CVAR_SEC(hmg, damage),
+ WEP_CVAR_SEC(hmg, edgedamage),
+ WEP_CVAR_SEC(hmg, radius),
+ WEP_CVAR_SEC(hmg, force),
+ WEP_CVAR_SEC(hmg, speed),
+ WEP_CVAR_SEC(hmg, spread),
+ WEP_CVAR_SEC(hmg, delay),
+ WEP_CVAR_SEC(hmg, lifetime)
+ );
+ actor.(weaponentity).m_weapon = oldwep;
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hmg, animtime), w_ready);
+ }
}
METHOD(HeavyMachineGun, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(hmg, ammo);
-
- if(autocvar_g_balance_hmg_reload_ammo)
- ammo_amount += actor.(weaponentity).(weapon_load[WEP_HMG.m_id]) >= WEP_CVAR(hmg, ammo);
-
- return ammo_amount;
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(hmg, ammo);
+ if (autocvar_g_balance_hmg_reload_ammo)
+ {
+ ammo_amount += actor.(weaponentity).(weapon_load[WEP_HMG.m_id]) >= WEP_CVAR_PRI(hmg, ammo);
+ }
+ return ammo_amount;
}
METHOD(HeavyMachineGun, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(hmg, ammo);
-
- if(autocvar_g_balance_hmg_reload_ammo)
- ammo_amount += actor.(weaponentity).(weapon_load[WEP_HMG.m_id]) >= WEP_CVAR(hmg, ammo);
-
- return ammo_amount;
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(hmg, ammo);
+ if (autocvar_g_balance_hmg_reload_ammo)
+ {
+ ammo_amount += actor.(weaponentity).(weapon_load[WEP_HMG.m_id]) >= WEP_CVAR_SEC(hmg, ammo);
+ }
+ return ammo_amount;
}
METHOD(HeavyMachineGun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
{
- W_Reload(actor, weaponentity, WEP_CVAR(hmg, ammo), SND_RELOAD);
+ W_Reload(actor, weaponentity, WEP_CVAR_PRI(hmg, ammo), SND_RELOAD);
}
METHOD(HeavyMachineGun, wr_suicidemessage, Notification(entity thiswep))
#define X(BEGIN, P, END, class, prefix) \
BEGIN(class) \
- P(class, prefix, ammo, float, NONE) \
- P(class, prefix, damage, float, NONE) \
- P(class, prefix, force, float, NONE) \
- P(class, prefix, refire, float, NONE) \
+ P(class, prefix, ammo, float, PRI) \
+ P(class, prefix, damage, float, PRI) \
+ P(class, prefix, force, float, PRI) \
+ P(class, prefix, refire, float, PRI) \
+ P(class, prefix, solidpenetration, float, PRI) \
+ P(class, prefix, spread_add, float, PRI) \
+ P(class, prefix, spread_max, float, PRI) \
+ P(class, prefix, spread_min, float, PRI) \
+ P(class, prefix, ammo, float, SEC) \
+ P(class, prefix, animtime, float, SEC) \
+ P(class, prefix, damage, float, SEC) \
+ P(class, prefix, delay, float, SEC) \
+ P(class, prefix, edgedamage, float, SEC) \
+ P(class, prefix, force, float, SEC) \
+ P(class, prefix, lifetime, float, SEC) \
+ P(class, prefix, radius, float, SEC) \
+ P(class, prefix, refire, float, SEC) \
+ P(class, prefix, refire_type, float, SEC) \
+ P(class, prefix, shotangle, float, SEC) \
+ P(class, prefix, speed, float, SEC) \
+ P(class, prefix, spread, float, SEC) \
P(class, prefix, reload_ammo, float, NONE) \
P(class, prefix, reload_time, float, NONE) \
- P(class, prefix, solidpenetration, float, NONE) \
- P(class, prefix, spread_add, float, NONE) \
- P(class, prefix, spread_max, float, NONE) \
- P(class, prefix, spread_min, float, NONE) \
P(class, prefix, switchdelay_drop, float, NONE) \
P(class, prefix, switchdelay_raise, float, NONE) \
P(class, prefix, weaponreplace, string, NONE) \
--- /dev/null
+#include "okmachinegun.qh"
+
+#ifdef SVQC
+
+spawnfunc(weapon_okmachinegun)
+{
+ if(autocvar_sv_q3acompat_machineshotgunswap)
+ if(this.classname != "droppedweapon")
+ {
+ weapon_defaultspawnfunc(this, WEP_SHOCKWAVE);
+ return;
+ }
+ weapon_defaultspawnfunc(this, WEP_OVERKILL_MACHINEGUN);
+}
+
+void W_OverkillMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire)
+{
+ float okmachinegun_spread;
+
+ if(!(fire & 1))
+ {
+ w_ready(thiswep, actor, weaponentity, fire);
+ return;
+ }
+
+ if(!thiswep.wr_checkammo1(thiswep, actor, weaponentity))
+ if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
+ {
+ W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity);
+ w_ready(thiswep, actor, weaponentity, fire);
+ return;
+ }
+
+ W_DecreaseAmmo(WEP_OVERKILL_MACHINEGUN, actor, WEP_CVAR_PRI(okmachinegun, ammo), weaponentity);
+
+ W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(okmachinegun, damage));
+ if(!autocvar_g_norecoil)
+ {
+ actor.punchangle_x = random() - 0.5;
+ actor.punchangle_y = random() - 0.5;
+ }
+
+ okmachinegun_spread = bound(WEP_CVAR_PRI(okmachinegun, spread_min), WEP_CVAR_PRI(okmachinegun, spread_min) + (WEP_CVAR_PRI(okmachinegun, spread_add) * actor.(weaponentity).misc_bulletcounter), WEP_CVAR_PRI(okmachinegun, spread_max));
+ fireBullet(actor, weaponentity, w_shotorg, w_shotdir, okmachinegun_spread, WEP_CVAR_PRI(okmachinegun, solidpenetration), WEP_CVAR_PRI(okmachinegun, damage), WEP_CVAR_PRI(okmachinegun, force), WEP_OVERKILL_MACHINEGUN.m_id, 0);
+
+ actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1;
+
+ Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
+
+ W_MachineGun_MuzzleFlash(actor, weaponentity);
+ W_AttachToShotorg(actor, weaponentity, actor.(weaponentity).muzzle_flash, '5 0 0');
+
+ if(autocvar_g_casings >= 2) // casing code
+ {
+ makevectors(actor.v_angle); // for some reason, this is lost
+ SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity);
+ }
+
+ int slot = weaponslot(weaponentity);
+ ATTACK_FINISHED(actor, slot) = time + WEP_CVAR_PRI(okmachinegun, refire) * W_WeaponRateFactor(actor);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(okmachinegun, refire), W_OverkillMachineGun_Attack_Auto);
+}
+
+METHOD(OverkillMachineGun, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
+{
+ if(vdist(actor.origin - actor.enemy.origin, <, 3000 - bound(0, skill, 10) * 200))
+ PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false);
+ else
+ PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false);
+}
+
+METHOD(OverkillMachineGun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
+{
+ if ((WEP_CVAR_SEC(okmachinegun, refire_type) == 1) && (fire & 2) && (time >= actor.jump_interval))
+ {
+ // Secondary uses it's own refire timer if refire_type is 1.
+ actor.jump_interval = time + WEP_CVAR_SEC(okmachinegun, refire) * W_WeaponRateFactor(actor);
+ // Ugly hack to reuse the fire mode of the blaster.
+ makevectors(actor.v_angle);
+ Weapon oldwep = actor.(weaponentity).m_weapon; // we can't avoid this hack
+ actor.(weaponentity).m_weapon = WEP_BLASTER;
+ W_Blaster_Attack(
+ actor,
+ weaponentity,
+ WEP_BLASTER.m_id | HITTYPE_SECONDARY,
+ WEP_CVAR_SEC(okmachinegun, shotangle),
+ WEP_CVAR_SEC(okmachinegun, damage),
+ WEP_CVAR_SEC(okmachinegun, edgedamage),
+ WEP_CVAR_SEC(okmachinegun, radius),
+ WEP_CVAR_SEC(okmachinegun, force),
+ WEP_CVAR_SEC(okmachinegun, speed),
+ WEP_CVAR_SEC(okmachinegun, spread),
+ WEP_CVAR_SEC(okmachinegun, delay),
+ WEP_CVAR_SEC(okmachinegun, lifetime)
+ );
+ actor.(weaponentity).m_weapon = oldwep;
+ if ((actor.(weaponentity).wframe == WFRAME_IDLE) ||
+ (actor.(weaponentity).wframe == WFRAME_FIRE2))
+ {
+ // Set secondary fire animation.
+ vector a = '0 0 0';
+ actor.(weaponentity).wframe = WFRAME_FIRE2;
+ a = actor.(weaponentity).anim_fire2;
+ a.z *= g_weaponratefactor;
+ FOREACH_CLIENT(true, LAMBDA(
+ if (it == actor || (IS_SPEC(it) && it.enemy == actor))
+ {
+ wframe_send(it, actor.(weaponentity), a, true);
+ }
+ ));
+ animdecide_setaction(actor, ANIMACTION_SHOOT, true);
+ }
+ }
+ if (WEP_CVAR(okmachinegun, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR_PRI(okmachinegun, ammo))
+ {
+ // Forced reload
+ thiswep.wr_reload(thiswep, actor, weaponentity);
+ return;
+ }
+ if (fire & 1) // Primary attack
+ {
+ if (!weapon_prepareattack(thiswep, actor, weaponentity, false, 0))
+ {
+ return;
+ }
+ actor.(weaponentity).misc_bulletcounter = 0;
+ W_OverkillMachineGun_Attack_Auto(thiswep, actor, weaponentity, fire);
+ return;
+ }
+ if ((fire & 2) && (WEP_CVAR_SEC(okmachinegun, refire_type) == 0)) // Secondary attack
+ {
+ if (!weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(okmachinegun, refire)))
+ {
+ return;
+ }
+ // Ugly hack to reuse the fire mode of the blaster.
+ makevectors(actor.v_angle);
+ Weapon oldwep = actor.(weaponentity).m_weapon; // we can't avoid this hack
+ actor.(weaponentity).m_weapon = WEP_BLASTER;
+ W_Blaster_Attack(
+ actor,
+ weaponentity,
+ WEP_BLASTER.m_id | HITTYPE_SECONDARY,
+ WEP_CVAR_SEC(okmachinegun, shotangle),
+ WEP_CVAR_SEC(okmachinegun, damage),
+ WEP_CVAR_SEC(okmachinegun, edgedamage),
+ WEP_CVAR_SEC(okmachinegun, radius),
+ WEP_CVAR_SEC(okmachinegun, force),
+ WEP_CVAR_SEC(okmachinegun, speed),
+ WEP_CVAR_SEC(okmachinegun, spread),
+ WEP_CVAR_SEC(okmachinegun, delay),
+ WEP_CVAR_SEC(okmachinegun, lifetime)
+ );
+ actor.(weaponentity).m_weapon = oldwep;
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(okmachinegun, animtime), w_ready);
+ }
+}
+
+METHOD(OverkillMachineGun, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
+{
+ float ammo_amount;
+ ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(okmachinegun, ammo);
+ if (WEP_CVAR(okmachinegun, reload_ammo))
+ {
+ ammo_amount += actor.(weaponentity).(weapon_load[WEP_OVERKILL_MACHINEGUN.m_id]) >= WEP_CVAR_PRI(okmachinegun, ammo);
+ }
+ return ammo_amount;
+}
+
+METHOD(OverkillMachineGun, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
+{
+ return true; // Blaster secondary is unlimited.
+}
+
+METHOD(OverkillMachineGun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
+{
+ W_Reload(actor, weaponentity, WEP_CVAR_PRI(okmachinegun, ammo), SND_RELOAD);
+}
+
+METHOD(OverkillMachineGun, wr_suicidemessage, Notification(entity thiswep))
+{
+ return WEAPON_THINKING_WITH_PORTALS;
+}
+
+METHOD(OverkillMachineGun, wr_killmessage, Notification(entity thiswep))
+{
+ if(w_deathtype & HITTYPE_SECONDARY)
+ return WEAPON_MACHINEGUN_MURDER_SNIPE;
+ else
+ return WEAPON_MACHINEGUN_MURDER_SPRAY;
+}
+
+#endif
+#ifdef CSQC
+
+METHOD(OverkillMachineGun, wr_impacteffect, void(entity thiswep, entity actor))
+{
+ vector org2;
+ org2 = w_org + w_backoff * 2;
+ pointparticles(EFFECT_MACHINEGUN_IMPACT, org2, w_backoff * 1000, 1);
+ if(!w_issilent)
+ sound(actor, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTN_NORM);
+}
+
+#endif
+
--- /dev/null
+#pragma once
+
+CLASS(OverkillMachineGun, Weapon)
+/* ammotype */ ATTRIB(OverkillMachineGun, ammo_type, int, RESOURCE_BULLETS);
+/* impulse */ ATTRIB(OverkillMachineGun, impulse, int, 3);
+/* flags */ ATTRIB(OverkillMachineGun, spawnflags, int, WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_PENETRATEWALLS);
+/* rating */ ATTRIB(OverkillMachineGun, bot_pickupbasevalue, float, 7000);
+/* color */ ATTRIB(OverkillMachineGun, wpcolor, vector, '1 1 0');
+/* modelname */ ATTRIB(OverkillMachineGun, mdl, string, "ok_mg");
+#ifdef GAMEQC
+/* model */ ATTRIB(OverkillMachineGun, m_model, Model, MDL_OK_MG_ITEM);
+#endif
+/* crosshair */ ATTRIB(OverkillMachineGun, w_crosshair, string, "gfx/crosshairuzi");
+/* crosshair */ ATTRIB(OverkillMachineGun, w_crosshair_size, float, 0.6);
+/* wepimg */ ATTRIB(OverkillMachineGun, model2, string, "weaponuzi");
+/* refname */ ATTRIB(OverkillMachineGun, netname, string, "okmachinegun");
+/* wepname */ ATTRIB(OverkillMachineGun, m_name, string, _("Overkill MachineGun"));
+
+#define X(BEGIN, P, END, class, prefix) \
+ BEGIN(class) \
+ P(class, prefix, ammo, float, PRI) \
+ P(class, prefix, damage, float, PRI) \
+ P(class, prefix, force, float, PRI) \
+ P(class, prefix, refire, float, PRI) \
+ P(class, prefix, solidpenetration, float, PRI) \
+ P(class, prefix, spread_add, float, PRI) \
+ P(class, prefix, spread_max, float, PRI) \
+ P(class, prefix, spread_min, float, PRI) \
+ P(class, prefix, animtime, float, SEC) \
+ P(class, prefix, damage, float, SEC) \
+ P(class, prefix, delay, float, SEC) \
+ P(class, prefix, edgedamage, float, SEC) \
+ P(class, prefix, force, float, SEC) \
+ P(class, prefix, lifetime, float, SEC) \
+ P(class, prefix, radius, float, SEC) \
+ P(class, prefix, refire, float, SEC) \
+ P(class, prefix, refire_type, float, SEC) \
+ P(class, prefix, shotangle, float, SEC) \
+ P(class, prefix, speed, float, SEC) \
+ P(class, prefix, spread, float, SEC) \
+ P(class, prefix, reload_ammo, float, NONE) \
+ P(class, prefix, reload_time, float, NONE) \
+ P(class, prefix, switchdelay_drop, float, NONE) \
+ P(class, prefix, switchdelay_raise, float, NONE) \
+ P(class, prefix, weaponreplace, string, NONE) \
+ P(class, prefix, weaponstartoverride, float, NONE) \
+ P(class, prefix, weaponstart, float, NONE) \
+ P(class, prefix, weaponthrowable, float, NONE) \
+ END()
+ W_PROPS(X, OverkillMachineGun, okmachinegun)
+#undef X
+
+ENDCLASS(OverkillMachineGun)
+REGISTER_WEAPON(OVERKILL_MACHINEGUN, okmachinegun, NEW(OverkillMachineGun));
+
--- /dev/null
+#include "okshotgun.qh"
+
+#ifdef SVQC
+spawnfunc(weapon_okshotgun) { weapon_defaultspawnfunc(this, WEP_OVERKILL_SHOTGUN); }
+
+METHOD(OverkillShotgun, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
+{
+ if (vdist(actor.origin - actor.enemy.origin, >, WEP_CVAR_PRI(okshotgun, bot_range)))
+ {
+ PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false);
+ }
+ else
+ {
+ PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false);
+ }
+}
+
+METHOD(OverkillShotgun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
+{
+ if ((WEP_CVAR_SEC(okshotgun, refire_type) == 1) && (fire & 2) && (time >= actor.jump_interval))
+ {
+ // Secondary uses it's own refire timer if refire_type is 1.
+ actor.jump_interval = time + WEP_CVAR_SEC(okshotgun, refire) * W_WeaponRateFactor(actor);
+ // Ugly hack to reuse the fire mode of the blaster.
+ makevectors(actor.v_angle);
+ Weapon oldwep = actor.(weaponentity).m_weapon; // we can't avoid this hack
+ actor.(weaponentity).m_weapon = WEP_BLASTER;
+ W_Blaster_Attack(
+ actor,
+ weaponentity,
+ WEP_BLASTER.m_id | HITTYPE_SECONDARY,
+ WEP_CVAR_SEC(okshotgun, shotangle),
+ WEP_CVAR_SEC(okshotgun, damage),
+ WEP_CVAR_SEC(okshotgun, edgedamage),
+ WEP_CVAR_SEC(okshotgun, radius),
+ WEP_CVAR_SEC(okshotgun, force),
+ WEP_CVAR_SEC(okshotgun, speed),
+ WEP_CVAR_SEC(okshotgun, spread),
+ WEP_CVAR_SEC(okshotgun, delay),
+ WEP_CVAR_SEC(okshotgun, lifetime)
+ );
+ actor.(weaponentity).m_weapon = oldwep;
+ if ((actor.(weaponentity).wframe == WFRAME_IDLE) ||
+ (actor.(weaponentity).wframe == WFRAME_FIRE2))
+ {
+ // Set secondary fire animation.
+ vector a = '0 0 0';
+ actor.(weaponentity).wframe = WFRAME_FIRE2;
+ a = actor.(weaponentity).anim_fire2;
+ a.z *= g_weaponratefactor;
+ FOREACH_CLIENT(true, LAMBDA(
+ if (it == actor || (IS_SPEC(it) && it.enemy == actor))
+ {
+ wframe_send(it, actor.(weaponentity), a, true);
+ }
+ ));
+ animdecide_setaction(actor, ANIMACTION_SHOOT, true);
+ }
+ }
+ if (WEP_CVAR(okshotgun, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR_PRI(okshotgun, ammo))
+ {
+ // Forced reload
+ thiswep.wr_reload(thiswep, actor, weaponentity);
+ return;
+ }
+ if (fire & 1) // Primary attack
+ {
+ if (!weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(okshotgun, animtime)))
+ {
+ return;
+ }
+ W_Shotgun_Attack(thiswep, actor, weaponentity, true,
+ WEP_CVAR_PRI(okshotgun, ammo),
+ WEP_CVAR_PRI(okshotgun, damage),
+ WEP_CVAR_PRI(okshotgun, bullets),
+ WEP_CVAR_PRI(okshotgun, spread),
+ WEP_CVAR_PRI(okshotgun, solidpenetration),
+ WEP_CVAR_PRI(okshotgun, force));
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(okshotgun, animtime), w_ready);
+ return;
+ }
+ if ((fire & 2) && (WEP_CVAR_SEC(okshotgun, refire_type) == 0)) // Secondary attack
+ {
+ if (!weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(okshotgun, refire)))
+ {
+ return;
+ }
+ // Ugly hack to reuse the fire mode of the blaster.
+ makevectors(actor.v_angle);
+ Weapon oldwep = actor.(weaponentity).m_weapon; // we can't avoid this hack
+ actor.(weaponentity).m_weapon = WEP_BLASTER;
+ W_Blaster_Attack(
+ actor,
+ weaponentity,
+ WEP_BLASTER.m_id | HITTYPE_SECONDARY,
+ WEP_CVAR_SEC(okshotgun, shotangle),
+ WEP_CVAR_SEC(okshotgun, damage),
+ WEP_CVAR_SEC(okshotgun, edgedamage),
+ WEP_CVAR_SEC(okshotgun, radius),
+ WEP_CVAR_SEC(okshotgun, force),
+ WEP_CVAR_SEC(okshotgun, speed),
+ WEP_CVAR_SEC(okshotgun, spread),
+ WEP_CVAR_SEC(okshotgun, delay),
+ WEP_CVAR_SEC(okshotgun, lifetime)
+ );
+ actor.(weaponentity).m_weapon = oldwep;
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(okshotgun, animtime), w_ready);
+ }
+}
+
+METHOD(OverkillShotgun, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
+{
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(okshotgun, ammo);
+ ammo_amount += actor.(weaponentity).(weapon_load[WEP_OVERKILL_SHOTGUN.m_id]) >= WEP_CVAR_PRI(okshotgun, ammo);
+ return ammo_amount;
+}
+
+METHOD(OverkillShotgun, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
+{
+ return true; // Blaster secondary is unlimited.
+}
+
+METHOD(OverkillShotgun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
+{
+ W_Reload(actor, weaponentity, WEP_CVAR_PRI(okshotgun, ammo), SND_RELOAD); // WEAPONTODO
+}
+
+METHOD(OverkillShotgun, wr_suicidemessage, Notification(entity thiswep))
+{
+ return WEAPON_THINKING_WITH_PORTALS;
+}
+
+METHOD(OverkillShotgun, wr_killmessage, Notification(entity thiswep))
+{
+ if(w_deathtype & HITTYPE_SECONDARY)
+ return WEAPON_BLASTER_MURDER;
+ else
+ return WEAPON_SHOTGUN_MURDER;
+}
+
+#endif
+#ifdef CSQC
+.float prevric;
+
+METHOD(OverkillShotgun, wr_impacteffect, void(entity thiswep, entity actor))
+{
+ vector org2 = w_org + w_backoff * 2;
+ pointparticles(EFFECT_SHOTGUN_IMPACT, org2, w_backoff * 1000, 1);
+ if(!w_issilent && time - actor.prevric > 0.25)
+ {
+ if(w_random < 0.05)
+ sound(actor, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM);
+ actor.prevric = time;
+ }
+}
+
+#endif
--- /dev/null
+#pragma once
+
+CLASS(OverkillShotgun, Weapon)
+/* ammotype */ ATTRIB(OverkillShotgun, ammo_type, int, RESOURCE_SHELLS);
+/* impulse */ ATTRIB(OverkillShotgun, impulse, int, 2);
+/* flags */ ATTRIB(OverkillShotgun, spawnflags, int, WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN);
+/* rating */ ATTRIB(OverkillShotgun, bot_pickupbasevalue, float, 6000);
+/* color */ ATTRIB(OverkillShotgun, wpcolor, vector, '0.5 0.25 0');
+/* modelname */ ATTRIB(OverkillShotgun, mdl, string, "ok_shotgun");
+#ifdef GAMEQC
+/* model */ ATTRIB(OverkillShotgun, m_model, Model, MDL_OK_SHOTGUN_ITEM);
+#endif
+/* crosshair */ ATTRIB(OverkillShotgun, w_crosshair, string, "gfx/crosshairshotgun");
+/* crosshair */ ATTRIB(OverkillShotgun, w_crosshair_size, float, 0.65);
+/* wepimg */ ATTRIB(OverkillShotgun, model2, string, "weaponshotgun");
+/* refname */ ATTRIB(OverkillShotgun, netname, string, "okshotgun");
+/* wepname */ ATTRIB(OverkillShotgun, m_name, string, _("Overkill Shotgun"));
+
+#define X(BEGIN, P, END, class, prefix) \
+ BEGIN(class) \
+ P(class, prefix, ammo, float, PRI) \
+ P(class, prefix, animtime, float, PRI) \
+ P(class, prefix, bot_range, float, PRI) \
+ P(class, prefix, bullets, float, PRI) \
+ P(class, prefix, damage, float, PRI) \
+ P(class, prefix, force, float, PRI) \
+ P(class, prefix, refire, float, PRI) \
+ P(class, prefix, solidpenetration, float, PRI) \
+ P(class, prefix, spread, float, PRI) \
+ P(class, prefix, animtime, float, SEC) \
+ P(class, prefix, damage, float, SEC) \
+ P(class, prefix, delay, float, SEC) \
+ P(class, prefix, edgedamage, float, SEC) \
+ P(class, prefix, force, float, SEC) \
+ P(class, prefix, lifetime, float, SEC) \
+ P(class, prefix, radius, float, SEC) \
+ P(class, prefix, refire, float, SEC) \
+ P(class, prefix, refire_type, float, SEC) \
+ P(class, prefix, shotangle, float, SEC) \
+ P(class, prefix, speed, float, SEC) \
+ P(class, prefix, spread, float, SEC) \
+ P(class, prefix, reload_ammo, float, NONE) \
+ P(class, prefix, reload_time, float, NONE) \
+ P(class, prefix, switchdelay_drop, float, NONE) \
+ P(class, prefix, switchdelay_raise, float, NONE) \
+ P(class, prefix, weaponreplace, string,NONE) \
+ P(class, prefix, weaponstartoverride, float, NONE) \
+ P(class, prefix, weaponstart, float, NONE) \
+ P(class, prefix, weaponthrowable, float, NONE) \
+ END()
+ W_PROPS(X, OverkillShotgun, okshotgun)
+#undef X
+
+ENDCLASS(OverkillShotgun)
+REGISTER_WEAPON(OVERKILL_SHOTGUN, okshotgun, NEW(OverkillShotgun));
--- /dev/null
+#include "okvortex.qh"
+
+#ifdef SVQC
+
+.float okvortex_lasthit;
+#endif
+
+#if defined(GAMEQC)
+
+METHOD(OverkillVortex, wr_glow, vector(OverkillVortex this, entity actor, entity wepent))
+{
+ if (!WEP_CVAR(okvortex, charge)) return '0 0 0';
+ float charge = wepent.okvortex_charge;
+ float animlimit = WEP_CVAR(okvortex, charge_animlimit);
+ vector g;
+ g.x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, charge / animlimit);
+ g.y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, charge / animlimit);
+ g.z = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_half * min(1, charge / animlimit);
+ if (charge > animlimit)
+ {
+ g.x += autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (charge - animlimit) / (1 - animlimit);
+ g.y += autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (charge - animlimit) / (1 - animlimit);
+ g.z += autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (charge - animlimit) / (1 - animlimit);
+ }
+ return g;
+}
+#endif
+
+#ifdef SVQC
+spawnfunc(weapon_okvortex) { weapon_defaultspawnfunc(this, WEP_OVERKILL_VORTEX); }
+
+REGISTER_MUTATOR(okvortex_charge, true);
+
+MUTATOR_HOOKFUNCTION(okvortex_charge, GetPressedKeys)
+{
+ entity player = M_ARGV(0, entity);
+
+ // WEAPONTODO
+ if(!WEP_CVAR(okvortex, charge) || !WEP_CVAR(okvortex, charge_velocity_rate))
+ return;
+
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ .entity weaponentity = weaponentities[slot];
+
+ if (player.(weaponentity).m_weapon == WEP_OVERKILL_VORTEX && WEP_CVAR(okvortex, charge) && WEP_CVAR(okvortex, charge_velocity_rate) && vdist(vec2(player.velocity), >, WEP_CVAR(okvortex, charge_minspeed)))
+ {
+ float xyspeed = vlen(vec2(player.velocity));
+ // add a maximum of charge_velocity_rate when going fast (f = 1), gradually increasing from minspeed (f = 0) to maxspeed
+ xyspeed = min(xyspeed, WEP_CVAR(okvortex, charge_maxspeed));
+ float f = (xyspeed - WEP_CVAR(okvortex, charge_minspeed)) / (WEP_CVAR(okvortex, charge_maxspeed) - WEP_CVAR(okvortex, charge_minspeed));
+ // add the extra charge
+ player.(weaponentity).okvortex_charge = min(1, player.(weaponentity).okvortex_charge + WEP_CVAR(okvortex, charge_velocity_rate) * f * PHYS_INPUT_TIMELENGTH);
+ }
+ }
+}
+
+void W_OverkillVortex_Attack(Weapon thiswep, entity actor, .entity weaponentity, float issecondary)
+{
+ float mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, myammo, charge;
+
+ mydmg = WEP_CVAR_BOTH(okvortex, !issecondary, damage);
+ myforce = WEP_CVAR_BOTH(okvortex, !issecondary, force);
+ mymindist = WEP_CVAR_BOTH(okvortex, !issecondary, damagefalloff_mindist);
+ mymaxdist = WEP_CVAR_BOTH(okvortex, !issecondary, damagefalloff_maxdist);
+ myhalflife = WEP_CVAR_BOTH(okvortex, !issecondary, damagefalloff_halflife);
+ myforcehalflife = WEP_CVAR_BOTH(okvortex, !issecondary, damagefalloff_forcehalflife);
+ myammo = WEP_CVAR_BOTH(okvortex, !issecondary, ammo);
+
+ float flying;
+ flying = IsFlying(actor); // do this BEFORE to make the trace values from FireRailgunBullet last
+
+ if (WEP_CVAR(okvortex, charge))
+ {
+ charge = WEP_CVAR(okvortex, charge_mindmg) / mydmg + (1 - WEP_CVAR(okvortex, charge_mindmg) / mydmg) * actor.(weaponentity).okvortex_charge;
+ actor.(weaponentity).okvortex_charge *= WEP_CVAR(okvortex, charge_shot_multiplier); // do this AFTER setting mydmg/myforce
+ // O RLY? -- divVerent
+ // YA RLY -- FruitieX
+ }
+ else
+ {
+ charge = 1;
+ }
+ mydmg *= charge;
+ myforce *= charge;
+
+ W_SetupShot(actor, weaponentity, true, 5, SND_NEXFIRE, CH_WEAPON_A, mydmg);
+ if(charge > WEP_CVAR(okvortex, charge_animlimit) && WEP_CVAR(okvortex, charge_animlimit)) // if the OverkillVortex is overcharged, we play an extra sound
+ {
+ sound(actor, CH_WEAPON_B, SND_NEXCHARGE, VOL_BASE * (charge - 0.5 * WEP_CVAR(okvortex, charge_animlimit)) / (1 - 0.5 * WEP_CVAR(okvortex, charge_animlimit)), ATTN_NORM);
+ }
+
+ yoda = 0;
+ damage_goodhits = 0;
+ FireRailgunBullet(actor, weaponentity, w_shotorg, w_shotorg + w_shotdir * max_shot_distance, mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, WEP_OVERKILL_VORTEX.m_id);
+
+ if(yoda && flying)
+ Send_Notification(NOTIF_ONE, actor, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA);
+ if(damage_goodhits && actor.okvortex_lasthit)
+ {
+ Send_Notification(NOTIF_ONE, actor, MSG_ANNCE, ANNCE_ACHIEVEMENT_IMPRESSIVE);
+ damage_goodhits = 0; // only every second time
+ }
+
+ actor.okvortex_lasthit = damage_goodhits;
+
+ //beam and muzzle flash done on client
+ SendCSQCVortexBeamParticle(charge);
+
+ W_DecreaseAmmo(thiswep, actor, myammo, weaponentity);
+}
+
+.float okvortex_chargepool_pauseregen_finished;
+
+METHOD(OverkillVortex, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
+{
+ if(bot_aim(actor, weaponentity, 1000000, 0, 1, false))
+ PHYS_INPUT_BUTTON_ATCK(actor) = true;
+ else
+ {
+ if(WEP_CVAR(okvortex, charge))
+ PHYS_INPUT_BUTTON_ATCK2(actor) = true;
+ }
+}
+
+METHOD(OverkillVortex, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
+{
+ if (WEP_CVAR(okvortex, charge) && actor.(weaponentity).okvortex_charge < WEP_CVAR(okvortex, charge_limit))
+ {
+ actor.(weaponentity).okvortex_charge = min(1, actor.(weaponentity).okvortex_charge + WEP_CVAR(okvortex, charge_rate) * frametime / W_TICSPERFRAME);
+ }
+
+ if (weaponslot(weaponentity) == 0)
+ {
+ actor.okvortex_charge = actor.(weaponentity).okvortex_charge;
+ }
+
+ if (WEP_CVAR_SEC(okvortex, chargepool))
+ if (actor.(weaponentity).okvortex_chargepool_ammo < 1)
+ {
+ if (actor.okvortex_chargepool_pauseregen_finished < time)
+ actor.(weaponentity).okvortex_chargepool_ammo = min(1, actor.(weaponentity).okvortex_chargepool_ammo + WEP_CVAR_SEC(okvortex, chargepool_regen) * frametime / W_TICSPERFRAME);
+ actor.pauseregen_finished = max(actor.pauseregen_finished, time + WEP_CVAR_SEC(okvortex, chargepool_pause_regen));
+ }
+
+ if(weaponslot(weaponentity) == 0)
+ actor.okvortex_chargepool_ammo = actor.(weaponentity).okvortex_chargepool_ammo;
+
+ if ((WEP_CVAR_SEC(okvortex, refire_type) == 1) && (fire & 2) && (time >= actor.jump_interval))
+ {
+ // Secondary uses it's own refire timer if refire_type is 1.
+ actor.jump_interval = time + WEP_CVAR_SEC(okvortex, refire) * W_WeaponRateFactor(actor);
+ // Ugly hack to reuse the fire mode of the blaster.
+ makevectors(actor.v_angle);
+ Weapon oldwep = actor.(weaponentity).m_weapon; // we can't avoid this hack
+ actor.(weaponentity).m_weapon = WEP_BLASTER;
+ W_Blaster_Attack(
+ actor,
+ weaponentity,
+ WEP_BLASTER.m_id | HITTYPE_SECONDARY,
+ WEP_CVAR_SEC(okvortex, shotangle),
+ WEP_CVAR_SEC(okvortex, damage),
+ WEP_CVAR_SEC(okvortex, edgedamage),
+ WEP_CVAR_SEC(okvortex, radius),
+ WEP_CVAR_SEC(okvortex, force),
+ WEP_CVAR_SEC(okvortex, speed),
+ WEP_CVAR_SEC(okvortex, spread),
+ WEP_CVAR_SEC(okvortex, delay),
+ WEP_CVAR_SEC(okvortex, lifetime)
+ );
+ actor.(weaponentity).m_weapon = oldwep;
+ if ((actor.(weaponentity).wframe == WFRAME_IDLE) ||
+ (actor.(weaponentity).wframe == WFRAME_FIRE2))
+ {
+ // Set secondary fire animation.
+ vector a = '0 0 0';
+ actor.(weaponentity).wframe = WFRAME_FIRE2;
+ a = actor.(weaponentity).anim_fire2;
+ a.z *= g_weaponratefactor;
+ FOREACH_CLIENT(true, LAMBDA(
+ if (it == actor || (IS_SPEC(it) && it.enemy == actor))
+ {
+ wframe_send(it, actor.(weaponentity), a, true);
+ }
+ ));
+ animdecide_setaction(actor, ANIMACTION_SHOOT, true);
+ }
+ }
+
+ if (autocvar_g_balance_okvortex_reload_ammo && actor.(weaponentity).clip_load < WEP_CVAR_PRI(okvortex, ammo))
+ {
+ // Rorced reload
+ thiswep.wr_reload(thiswep, actor, weaponentity);
+ return;
+ }
+ if (fire & 1) // Primary attack
+ {
+ if (!weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(okvortex, refire)))
+ {
+ return;
+ }
+ W_OverkillVortex_Attack(thiswep, actor, weaponentity, 0);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(okvortex, animtime), w_ready);
+ return;
+ }
+ if ((fire & 2) && (WEP_CVAR(okvortex, secondary) == 2) && (WEP_CVAR_SEC(okvortex, refire_type) == 0))
+ {
+ // Secondary attack
+ if (!weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(okshotgun, refire)))
+ {
+ return;
+ }
+ // ugly instagib hack to reuse the fire mode of the laser
+ makevectors(actor.v_angle);
+ Weapon oldwep = actor.(weaponentity).m_weapon; // we can't avoid this hack
+ actor.(weaponentity).m_weapon = WEP_BLASTER;
+ W_Blaster_Attack(
+ actor,
+ weaponentity,
+ WEP_BLASTER.m_id | HITTYPE_SECONDARY,
+ WEP_CVAR_SEC(okvortex, shotangle),
+ WEP_CVAR_SEC(okvortex, damage),
+ WEP_CVAR_SEC(okvortex, edgedamage),
+ WEP_CVAR_SEC(okvortex, radius),
+ WEP_CVAR_SEC(okvortex, force),
+ WEP_CVAR_SEC(okvortex, speed),
+ WEP_CVAR_SEC(okvortex, spread),
+ WEP_CVAR_SEC(okvortex, delay),
+ WEP_CVAR_SEC(okvortex, lifetime)
+ );
+ actor.(weaponentity).m_weapon = oldwep;
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(okvortex, animtime), w_ready);
+ return;
+ }
+ //if ((WEP_CVAR(okvortex, charge) && (WEP_CVAR(okvortex, secondary) == 1)) ? (PHYS_INPUT_BUTTON_ZOOM(actor) | PHYS_INPUT_BUTTON_ZOOMSCRIPT(actor)) : (fire & 2))
+ //{
+ // if(WEP_CVAR(okvortex, charge))
+ // {
+ // actor.(weaponentity).okvortex_charge_rottime = time + WEP_CVAR(okvortex, charge_rot_pause);
+ // float dt = frametime / W_TICSPERFRAME;
+ //
+ // if(actor.(weaponentity).okvortex_charge < 1)
+ // {
+ // if(WEP_CVAR_SEC(okvortex, chargepool))
+ // {
+ // if(WEP_CVAR_SEC(okvortex, ammo))
+ // {
+ // // always deplete if secondary is held
+ // actor.okvortex_chargepool_ammo = max(0, actor.okvortex_chargepool_ammo - WEP_CVAR_SEC(okvortex, ammo) * dt);
+
+ // dt = min(dt, (1 - actor.(weaponentity).okvortex_charge) / WEP_CVAR(okvortex, charge_rate));
+ // actor.okvortex_chargepool_pauseregen_finished = time + WEP_CVAR_SEC(okvortex, chargepool_pause_regen);
+ // dt = min(dt, actor.okvortex_chargepool_ammo);
+ // dt = max(0, dt);
+
+ // actor.(weaponentity).okvortex_charge += dt * WEP_CVAR(okvortex, charge_rate);
+ // }
+ // }
+
+ // else if(WEP_CVAR_SEC(okvortex, ammo))
+ // {
+ // if(fire & 2) // only eat ammo when the button is pressed
+ // {
+ // dt = min(dt, (1 - actor.(weaponentity).okvortex_charge) / WEP_CVAR(okvortex, charge_rate));
+ // if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
+ // {
+ // // if this weapon is reloadable, decrease its load. Else decrease the player's ammo
+ // if(autocvar_g_balance_vortex_reload_ammo)
+ // {
+ // dt = min(dt, (actor.(weaponentity).clip_load - WEP_CVAR_PRI(okvortex, ammo)) / WEP_CVAR_SEC(okvortex, ammo));
+ // dt = max(0, dt);
+ // if(dt > 0)
+ // {
+ // actor.(weaponentity).clip_load = max(WEP_CVAR_SEC(okvortex, ammo), actor.(weaponentity).clip_load - WEP_CVAR_SEC(okvortex, ammo) * dt);
+ // }
+ // actor.(weaponentity).(weapon_load[WEP_OVERKILL_VORTEX.m_id]) = actor.(weaponentity).clip_load;
+ // }
+ // else
+ // {
+ // dt = min(dt, (actor.(thiswep.ammo_field) - WEP_CVAR_PRI(okvortex, ammo)) / WEP_CVAR_SEC(okvortex, ammo));
+ // dt = max(0, dt);
+ // if(dt > 0)
+ // {
+ // actor.(thiswep.ammo_field) = max(WEP_CVAR_SEC(okvortex, ammo), actor.(thiswep.ammo_field) - WEP_CVAR_SEC(okvortex, ammo) * dt);
+ // }
+ // }
+ // }
+ // actor.(weaponentity).okvortex_charge += dt * WEP_CVAR(okvortex, charge_rate);
+ // }
+ // }
+
+ // else
+ // {
+ // dt = min(dt, (1 - actor.(weaponentity).okvortex_charge) / WEP_CVAR(okvortex, charge_rate));
+ // actor.(weaponentity).okvortex_charge += dt * WEP_CVAR(okvortex, charge_rate);
+ // }
+ // }
+ // }
+ // else if(WEP_CVAR(okvortex, secondary))
+ // {
+ // if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(okvortex, refire)))
+ // {
+ // W_OverkillVortex_Attack(thiswep, actor, weaponentity, 1);
+ // weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(okvortex, animtime), w_ready);
+ // }
+ // }
+ //}
+}
+
+METHOD(OverkillVortex, wr_setup, void(entity thiswep, entity actor, .entity weaponentity))
+{
+ actor.okvortex_lasthit = 0;
+}
+
+METHOD(OverkillVortex, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
+{
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(okvortex, ammo);
+ ammo_amount += (autocvar_g_balance_okvortex_reload_ammo && actor.(weaponentity).(weapon_load[WEP_OVERKILL_VORTEX.m_id]) >= WEP_CVAR_PRI(okvortex, ammo));
+ return ammo_amount;
+}
+
+METHOD(OverkillVortex, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
+{
+ if (WEP_CVAR(okvortex, secondary))
+ {
+ // don't allow charging if we don't have enough ammo
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(okvortex, ammo);
+ ammo_amount += actor.(weaponentity).(weapon_load[WEP_OVERKILL_VORTEX.m_id]) >= WEP_CVAR_SEC(okvortex, ammo);
+ return ammo_amount;
+ }
+ else
+ {
+ return false; // zoom is not a fire mode
+ }
+}
+
+METHOD(OverkillVortex, wr_resetplayer, void(entity thiswep, entity actor))
+{
+ if (WEP_CVAR(okvortex, charge)) {
+ if (WEP_CVAR_SEC(okvortex, chargepool)) {
+ actor.okvortex_chargepool_ammo = 1;
+ }
+ actor.okvortex_charge = WEP_CVAR(okvortex, charge_start);
+ for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+ {
+ .entity weaponentity = weaponentities[slot];
+ actor.(weaponentity).okvortex_charge = WEP_CVAR(okvortex, charge_start);
+
+ if (WEP_CVAR_SEC(okvortex, chargepool))
+ actor.(weaponentity).okvortex_chargepool_ammo = 1;
+ }
+ }
+ actor.okvortex_lasthit = 0;
+}
+
+METHOD(OverkillVortex, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
+{
+ W_Reload(actor, weaponentity, WEP_CVAR_PRI(okvortex, ammo), SND_RELOAD);
+}
+
+METHOD(OverkillVortex, wr_suicidemessage, Notification(entity thiswep))
+{
+ return WEAPON_THINKING_WITH_PORTALS;
+}
+
+METHOD(OverkillVortex, wr_killmessage, Notification(entity thiswep))
+{
+ return WEAPON_VORTEX_MURDER;
+}
+
+METHOD(OverkillVortex, wr_zoom, bool(entity thiswep, entity actor))
+{
+ return PHYS_INPUT_BUTTON_ATCK2(actor) && !WEP_CVAR(okvortex, secondary);
+}
+
+#endif
+#ifdef CSQC
+
+METHOD(OverkillVortex, wr_impacteffect, void(entity thiswep, entity actor))
+{
+ entity this = actor;
+ vector org2 = w_org + w_backoff * 6;
+ pointparticles(EFFECT_VORTEX_IMPACT, org2, '0 0 0', 1);
+ if(!w_issilent)
+ sound(this, CH_SHOTS, SND_NEXIMPACT, VOL_BASE, ATTN_NORM);
+}
+
+METHOD(OverkillVortex, wr_init, void(entity thiswep))
+{
+ if(autocvar_cl_reticle && autocvar_cl_reticle_weapon)
+ {
+ precache_pic("gfx/reticle_nex");
+ }
+}
+
+METHOD(OverkillVortex, wr_zoom, bool(entity thiswep, entity actor))
+{
+ if(button_zoom || zoomscript_caught || (!WEP_CVAR(okvortex, secondary) && button_attack2))
+ {
+ return true;
+ }
+ else
+ {
+ // no weapon specific image for this weapon
+ return false;
+ }
+}
+
+#endif
--- /dev/null
+#pragma once
+
+CLASS(OverkillVortex, Weapon)
+/* ammotype */ ATTRIB(OverkillVortex, ammo_type, int, RESOURCE_CELLS);
+/* impulse */ ATTRIB(OverkillVortex, impulse, int, 7);
+/* flags */ ATTRIB(OverkillVortex, spawnflags, int, WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN);
+/* rating */ ATTRIB(OverkillVortex, bot_pickupbasevalue, float, 8000);
+/* color */ ATTRIB(OverkillVortex, wpcolor, vector, '0.5 1 1');
+/* modelname */ ATTRIB(OverkillVortex, mdl, string, "ok_sniper");
+#ifdef GAMEQC
+/* model */ ATTRIB(OverkillVortex, m_model, Model, MDL_OK_SNIPER_ITEM);
+#endif
+/* crosshair */ ATTRIB(OverkillVortex, w_crosshair, string, "gfx/crosshairnex");
+/* crosshair */ ATTRIB(OverkillVortex, w_crosshair_size, float, 0.65);
+/* reticle */ ATTRIB(OverkillVortex, w_reticle, string, "gfx/reticle_nex");
+/* wepimg */ ATTRIB(OverkillVortex, model2, string, "weaponnex");
+/* refname */ ATTRIB(OverkillVortex, netname, string, "okvortex");
+/* wepname */ ATTRIB(OverkillVortex, m_name, string, _("Overkill Vortex"));
+
+#define X(BEGIN, P, END, class, prefix) \
+ BEGIN(class) \
+ P(class, prefix, ammo, float, PRI) \
+ P(class, prefix, animtime, float, PRI) \
+ P(class, prefix, chargepool, float, SEC) \
+ P(class, prefix, chargepool_pause_regen, float, SEC) \
+ P(class, prefix, chargepool_regen, float, SEC) \
+ P(class, prefix, charge, float, NONE) \
+ P(class, prefix, charge_animlimit, float, NONE) \
+ P(class, prefix, charge_limit, float, NONE) \
+ P(class, prefix, charge_maxspeed, float, NONE) \
+ P(class, prefix, charge_mindmg, float, NONE) \
+ P(class, prefix, charge_minspeed, float, NONE) \
+ P(class, prefix, charge_rate, float, NONE) \
+ P(class, prefix, charge_rot_pause, float, NONE) \
+ P(class, prefix, charge_rot_rate, float, NONE) \
+ P(class, prefix, charge_shot_multiplier, float, NONE) \
+ P(class, prefix, charge_start, float, NONE) \
+ P(class, prefix, charge_velocity_rate, float, NONE) \
+ P(class, prefix, damagefalloff_forcehalflife, float, BOTH) \
+ P(class, prefix, damagefalloff_halflife, float, BOTH) \
+ P(class, prefix, damagefalloff_maxdist, float, BOTH) \
+ P(class, prefix, damagefalloff_mindist, float, BOTH) \
+ P(class, prefix, damage, float, PRI) \
+ P(class, prefix, force, float, PRI) \
+ P(class, prefix, refire, float, PRI) \
+ P(class, prefix, secondary, float, NONE) \
+ P(class, prefix, reload_ammo, float, NONE) \
+ P(class, prefix, reload_time, float, NONE) \
+ P(class, prefix, switchdelay_raise, float, NONE) \
+ P(class, prefix, switchdelay_drop, float, NONE) \
+ P(class, prefix, weaponreplace, string, NONE) \
+ P(class, prefix, weaponstart, float, NONE) \
+ P(class, prefix, weaponstartoverride, float, NONE) \
+ P(class, prefix, weaponthrowable, float, NONE) \
+ P(class, prefix, ammo, float, SEC) \
+ P(class, prefix, animtime, float, SEC) \
+ P(class, prefix, damage, float, SEC) \
+ P(class, prefix, delay, float, SEC) \
+ P(class, prefix, edgedamage, float, SEC) \
+ P(class, prefix, force, float, SEC) \
+ P(class, prefix, lifetime, float, SEC) \
+ P(class, prefix, radius, float, SEC) \
+ P(class, prefix, refire, float, SEC) \
+ P(class, prefix, refire_type, float, SEC) \
+ P(class, prefix, shotangle, float, SEC) \
+ P(class, prefix, speed, float, SEC) \
+ P(class, prefix, spread, float, SEC) \
+ END()
+ W_PROPS(X, OverkillVortex, okvortex)
+#undef X
+
+ENDCLASS(OverkillVortex)
+REGISTER_WEAPON(OVERKILL_VORTEX, okvortex, NEW(OverkillVortex));
+
this.event_damage = func_null;
this.takedamage = DAMAGE_NO;
- RadiusDamage (this, this.realowner, WEP_CVAR(rpc, damage), WEP_CVAR(rpc, edgedamage), WEP_CVAR(rpc, radius), NULL, NULL, WEP_CVAR(rpc, force), this.projectiledeathtype, directhitentity);
+ RadiusDamage (this, this.realowner, WEP_CVAR_PRI(rpc, damage), WEP_CVAR_PRI(rpc, edgedamage), WEP_CVAR_PRI(rpc, radius), NULL, NULL, WEP_CVAR_PRI(rpc, force), this.projectiledeathtype, directhitentity);
delete(this);
}
tracebox(this.origin, this.mins, this.maxs, this.origin + this.pos1 * (2 * this.wait), MOVE_NORMAL, this);
if(IS_PLAYER(trace_ent))
- Damage (trace_ent, this, this.realowner, WEP_CVAR(rpc, damage2), this.projectiledeathtype, this.origin, normalize(this.origin - trace_ent.origin) * WEP_CVAR(rpc, force));
+ Damage (trace_ent, this, this.realowner, WEP_CVAR_PRI(rpc, damage2), this.projectiledeathtype, this.origin, normalize(this.origin - other.origin) * WEP_CVAR_PRI(rpc, force));
- this.velocity = this.pos1 * (this.cnt + (WEP_CVAR(rpc, speedaccel) * sys_frametime));
+ this.velocity = this.pos1 * (this.cnt + (WEP_CVAR_PRI(rpc, speedaccel) * sys_frametime));
UpdateCSQCProjectile(this);
this.nextthink = time;
entity missile = spawn(); //WarpZone_RefSys_SpawnSameRefSys(actor);
entity flash = spawn ();
- W_DecreaseAmmo(thiswep, actor, WEP_CVAR(rpc, ammo), weaponentity);
- W_SetupShot_ProjectileSize (actor, weaponentity, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR(rpc, damage));
+ W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(rpc, ammo), weaponentity);
+ W_SetupShot_ProjectileSize (actor, weaponentity, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(rpc, damage));
Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1);
PROJECTILE_MAKETRIGGER(missile);
missile.owner = missile.realowner = actor;
missile.bot_dodge = true;
- missile.bot_dodgerating = WEP_CVAR(rpc, damage) * 2;
+ missile.bot_dodgerating = WEP_CVAR_PRI(rpc, damage) * 2;
missile.takedamage = DAMAGE_YES;
- missile.damageforcescale = WEP_CVAR(rpc, damageforcescale);
- missile.health = WEP_CVAR(rpc, health);
+ missile.damageforcescale = WEP_CVAR_PRI(rpc, damageforcescale);
+ missile.health = WEP_CVAR_PRI(rpc, health);
missile.event_damage = W_RocketPropelledChainsaw_Damage;
missile.damagedbycontents = true;
IL_PUSH(g_damagedbycontents, missile);
setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
setorigin(missile, w_shotorg - v_forward * 3); // move it back so it hits the wall at the right point
- W_SetupProjVelocity_Basic(missile, WEP_CVAR(rpc, speed), 0);
+ W_SetupProjVelocity_Basic(missile, WEP_CVAR_PRI(rpc, speed), 0);
settouch(missile, W_RocketPropelledChainsaw_Touch);
setthink(missile, W_RocketPropelledChainsaw_Think);
- missile.cnt = time + WEP_CVAR(rpc, lifetime);
+ missile.cnt = time + WEP_CVAR_PRI(rpc, lifetime);
missile.nextthink = time;
missile.flags = FL_PROJECTILE;
IL_PUSH(g_projectiles, missile);
METHOD(RocketPropelledChainsaw, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
{
- PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR(rpc, speed), 0, WEP_CVAR(rpc, lifetime), false);
+ PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR_PRI(rpc, speed), 0, WEP_CVAR_PRI(rpc, lifetime), false);
}
METHOD(RocketPropelledChainsaw, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
- if(WEP_CVAR(rpc, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR(rpc, ammo)) {
- thiswep.wr_reload(thiswep, actor, weaponentity);
- } else
- {
- if (fire & 1)
- {
- if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(rpc, refire)))
- {
- W_RocketPropelledChainsaw_Attack(thiswep, actor, weaponentity);
- weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(rpc, animtime), w_ready);
- }
- }
-
- if (fire & 2)
- {
- // to-do
- }
- }
+ if ((WEP_CVAR_SEC(rpc, refire_type) == 1) && (fire & 2) && (time >= actor.jump_interval))
+ {
+ // Secondary uses it's own refire timer if refire_type is 1.
+ actor.jump_interval = time + WEP_CVAR_SEC(rpc, refire) * W_WeaponRateFactor(actor);
+ // Ugly hack to reuse the fire mode of the blaster.
+ makevectors(actor.v_angle);
+ Weapon oldwep = actor.(weaponentity).m_weapon; // we can't avoid this hack
+ actor.(weaponentity).m_weapon = WEP_BLASTER;
+ W_Blaster_Attack(
+ actor,
+ weaponentity,
+ WEP_BLASTER.m_id | HITTYPE_SECONDARY,
+ WEP_CVAR_SEC(rpc, shotangle),
+ WEP_CVAR_SEC(rpc, damage),
+ WEP_CVAR_SEC(rpc, edgedamage),
+ WEP_CVAR_SEC(rpc, radius),
+ WEP_CVAR_SEC(rpc, force),
+ WEP_CVAR_SEC(rpc, speed),
+ WEP_CVAR_SEC(rpc, spread),
+ WEP_CVAR_SEC(rpc, delay),
+ WEP_CVAR_SEC(rpc, lifetime)
+ );
+ actor.(weaponentity).m_weapon = oldwep;
+ if ((actor.(weaponentity).wframe == WFRAME_IDLE) ||
+ (actor.(weaponentity).wframe == WFRAME_FIRE2))
+ {
+ // Set secondary fire animation.
+ vector a = '0 0 0';
+ actor.(weaponentity).wframe = WFRAME_FIRE2;
+ a = actor.(weaponentity).anim_fire2;
+ a.z *= g_weaponratefactor;
+ FOREACH_CLIENT(true, LAMBDA(
+ if (it == actor || (IS_SPEC(it) && it.enemy == actor))
+ {
+ wframe_send(it, actor.(weaponentity), a, true);
+ }
+ ));
+ animdecide_setaction(actor, ANIMACTION_SHOOT, true);
+ }
+ }
+ if (WEP_CVAR(rpc, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR_PRI(rpc, ammo))
+ {
+ // Forced reload
+ thiswep.wr_reload(thiswep, actor, weaponentity);
+ return;
+ }
+ if (fire & 1) // Primary attack
+ {
+ if (!weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(rpc, refire)))
+ {
+ return;
+ }
+ W_RocketPropelledChainsaw_Attack(thiswep, actor, weaponentity);
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(rpc, animtime), w_ready);
+ return;
+ }
+ if ((fire & 2) && (WEP_CVAR_SEC(rpc, refire_type) == 0)) // Secondary attack
+ {
+ if (!weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(rpc, refire)))
+ {
+ return;
+ }
+ // ugly instagib hack to reuse the fire mode of the laser
+ makevectors(actor.v_angle);
+ Weapon oldwep = actor.(weaponentity).m_weapon; // we can't avoid this hack
+ actor.(weaponentity).m_weapon = WEP_BLASTER;
+ W_Blaster_Attack(
+ actor,
+ weaponentity,
+ WEP_BLASTER.m_id | HITTYPE_SECONDARY,
+ WEP_CVAR_SEC(rpc, shotangle),
+ WEP_CVAR_SEC(rpc, damage),
+ WEP_CVAR_SEC(rpc, edgedamage),
+ WEP_CVAR_SEC(rpc, radius),
+ WEP_CVAR_SEC(rpc, force),
+ WEP_CVAR_SEC(rpc, speed),
+ WEP_CVAR_SEC(rpc, spread),
+ WEP_CVAR_SEC(rpc, delay),
+ WEP_CVAR_SEC(rpc, lifetime)
+ );
+ actor.(weaponentity).m_weapon = oldwep;
+ weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(rpc, animtime), w_ready);
+ }
}
METHOD(RocketPropelledChainsaw, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(rpc, ammo);
- ammo_amount += actor.(weaponentity).(weapon_load[WEP_RPC.m_id]) >= WEP_CVAR(rpc, ammo);
- return ammo_amount;
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(rpc, ammo);
+ ammo_amount += actor.(weaponentity).(weapon_load[WEP_RPC.m_id]) >= WEP_CVAR_PRI(rpc, ammo);
+ return ammo_amount;
}
METHOD(RocketPropelledChainsaw, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
{
- return false;
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(rpc, ammo);
+ ammo_amount += actor.(weaponentity).(weapon_load[WEP_RPC.m_id]) >= WEP_CVAR_SEC(rpc, ammo);
+ return ammo_amount;
}
METHOD(RocketPropelledChainsaw, wr_reload, void(entity thiswep, entity actor, .entity weaponentity))
{
- W_Reload(actor, weaponentity, WEP_CVAR(rpc, ammo), SND_RELOAD);
+ W_Reload(actor, weaponentity, WEP_CVAR_PRI(rpc, ammo), SND_RELOAD);
}
METHOD(RocketPropelledChainsaw, wr_suicidemessage, Notification(entity thiswep))
#define X(BEGIN, P, END, class, prefix) \
BEGIN(class) \
- P(class, prefix, ammo, float, NONE) \
- P(class, prefix, animtime, float, NONE) \
- P(class, prefix, damage2, float, NONE) \
- P(class, prefix, damageforcescale, float, NONE) \
- P(class, prefix, damage, float, NONE) \
- P(class, prefix, edgedamage, float, NONE) \
- P(class, prefix, force, float, NONE) \
- P(class, prefix, health, float, NONE) \
- P(class, prefix, lifetime, float, NONE) \
- P(class, prefix, radius, float, NONE) \
- P(class, prefix, refire, float, NONE) \
+ P(class, prefix, ammo, float, PRI) \
+ P(class, prefix, animtime, float, PRI) \
+ P(class, prefix, damage, float, PRI) \
+ P(class, prefix, damage2, float, PRI) \
+ P(class, prefix, damageforcescale, float, PRI) \
+ P(class, prefix, edgedamage, float, PRI) \
+ P(class, prefix, force, float, PRI) \
+ P(class, prefix, health, float, PRI) \
+ P(class, prefix, lifetime, float, PRI) \
+ P(class, prefix, radius, float, PRI) \
+ P(class, prefix, refire, float, PRI) \
+ P(class, prefix, speedaccel, float, PRI) \
+ P(class, prefix, speed, float, PRI) \
+ P(class, prefix, ammo, float, SEC) \
+ P(class, prefix, animtime, float, SEC) \
+ P(class, prefix, damage, float, SEC) \
+ P(class, prefix, delay, float, SEC) \
+ P(class, prefix, edgedamage, float, SEC) \
+ P(class, prefix, force, float, SEC) \
+ P(class, prefix, lifetime, float, SEC) \
+ P(class, prefix, radius, float, SEC) \
+ P(class, prefix, refire, float, SEC) \
+ P(class, prefix, refire_type, float, SEC) \
+ P(class, prefix, shotangle, float, SEC) \
+ P(class, prefix, speed, float, SEC) \
+ P(class, prefix, spread, float, SEC) \
P(class, prefix, reload_ammo, float, NONE) \
P(class, prefix, reload_time, float, NONE) \
- P(class, prefix, speedaccel, float, NONE) \
- P(class, prefix, speed, float, NONE) \
P(class, prefix, switchdelay_drop, float, NONE) \
P(class, prefix, switchdelay_raise, float, NONE) \
P(class, prefix, weaponreplace, string, NONE) \
return true;
}
-MUTATOR_HOOKFUNCTION(ok, PlayerPreThink)
-{
- if(game_stopped)
- return;
-
- entity player = M_ARGV(0, entity);
-
- if(IS_DEAD(player) || !IS_PLAYER(player) || STAT(FROZEN, player))
- return;
-
- if(PHYS_INPUT_BUTTON_ATCK2(player) && time >= player.jump_interval)
- if( !forbidWeaponUse(player)
- || (round_handler_IsActive() && !round_handler_IsRoundStarted()) )
- {
- player.jump_interval = time + WEP_CVAR_PRI(blaster, refire) * W_WeaponRateFactor(player);
- makevectors(player.v_angle);
-
- for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
- {
- .entity weaponentity = weaponentities[slot];
-
- if(player.(weaponentity).m_weapon == WEP_Null && slot != 0)
- continue;
-
- Weapon oldwep = player.(weaponentity).m_weapon;
- player.(weaponentity).m_weapon = WEP_BLASTER;
- W_Blaster_Attack(
- player,
- weaponentity,
- WEP_BLASTER.m_id | HITTYPE_SECONDARY,
- WEP_CVAR_SEC(vaporizer, shotangle),
- WEP_CVAR_SEC(vaporizer, damage),
- WEP_CVAR_SEC(vaporizer, edgedamage),
- WEP_CVAR_SEC(vaporizer, radius),
- WEP_CVAR_SEC(vaporizer, force),
- WEP_CVAR_SEC(vaporizer, speed),
- WEP_CVAR_SEC(vaporizer, spread),
- WEP_CVAR_SEC(vaporizer, delay),
- WEP_CVAR_SEC(vaporizer, lifetime)
- );
- player.(weaponentity).m_weapon = oldwep;
- }
- }
-
- PHYS_INPUT_BUTTON_ATCK2(player) = false;
-}
-
MUTATOR_HOOKFUNCTION(ok, PlayerWeaponSelect)
{
entity player = M_ARGV(0, entity);
MUTATOR_HOOKFUNCTION(ok, SetStartItems, CBC_ORDER_LAST)
{
- WepSet ok_start_items = (WEPSET(MACHINEGUN) | WEPSET(VORTEX) | WEPSET(SHOTGUN));
+ WepSet ok_start_items = (WEPSET(OVERKILL_MACHINEGUN) | WEPSET(OVERKILL_VORTEX) | WEPSET(OVERKILL_SHOTGUN));
if(WEP_RPC.weaponstart > 0) { ok_start_items |= WEPSET(RPC); }
if(WEP_HMG.weaponstart > 0) { ok_start_items |= WEPSET(HMG); }
WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
WEP_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
- WEP_SHOTGUN.mdl = "ok_shotgun";
- WEP_MACHINEGUN.mdl = "ok_mg";
- WEP_VORTEX.mdl = "ok_sniper";
+ //WEP_SHOTGUN.mdl = "ok_shotgun";
+ //WEP_MACHINEGUN.mdl = "ok_mg";
+ //WEP_VORTEX.mdl = "ok_sniper";
}
REGISTER_WAYPOINT(OnsGen, _("Generator"), '1 0.5 0', 1);
REGISTER_WAYPOINT(OnsGenShielded, _("Generator"), '1 0.5 0', 1);
+REGISTER_WAYPOINT(SurvivalKill, _("Kill"), '0.8 0.8 0', 1);
+REGISTER_WAYPOINT(SurvivalDefend, _("Defend"), '0 1 0', 1);
+
REGISTER_WAYPOINT(Weapon, _("Weapon"), '0 0 0', 1);
REGISTER_WAYPOINT(Monster, _("Monster"), '1 0 0', 1);
MSG_INFO_NOTIF(SUPERWEAPON_PICKUP, N_CONSOLE, 1, 0, "s1", "s1", "superweapons", _("^BG%s^K1 picked up a Superweapon"), "")
+ MSG_INFO_NOTIF(SURVIVAL_1ST_ROUND_ATTACKER, N_CONSOLE, 0, 0, "", "", "", _("^BGFirst round. Eliminate the enemy team ^F1as fast as you can^BG."), "")
+ MSG_INFO_NOTIF(SURVIVAL_1ST_ROUND_DEFENDER, N_CONSOLE, 0, 0, "", "", "", _("^BGFirst round. Defend yourself ^F1as long as you can^BG."), "")
+ MSG_INFO_NOTIF(SURVIVAL_2ND_ROUND_ATTACKER, N_CONSOLE, 0, 1, "f1time", "", "", _("^BGSecond round. Eliminate the enemy team in less than ^F1%s^BG."), "")
+ MSG_INFO_NOTIF(SURVIVAL_2ND_ROUND_DEFENDER, N_CONSOLE, 0, 1, "f1time", "", "", _("^BGSecond round. Defend yourself for ^F1%s^BG."), "")
+ MSG_INFO_NOTIF(SURVIVAL_COOP_DEFENDER, N_CONSOLE, 0, 0, "", "", "", _("^BGDefend yourself ^F1as long as you can^BG."), "")
+ MSG_INFO_NOTIF(SURVIVAL_DEFENDERS_ELIMINATED, N_CONSOLE, 0, 0, "", "", "", _("^BGDefenders were ^F1eliminated^BG."), "")
+ MSG_INFO_NOTIF(SURVIVAL_DEFENDERS_ELIMINATED_IN, N_CONSOLE, 0, 1, "f1time", "", "", _("^BGDefenders were eliminated in ^F1%s^BG."), "")
+ MSG_INFO_NOTIF(SURVIVAL_DEFENDERS_SURVIVED, N_CONSOLE, 0, 0, "", "", "", _("^BGDefenders have ^F1survived^BG."), "")
+
MSG_INFO_NOTIF(TEAMCHANGE_LARGERTEAM, N_CONSOLE, 0, 0, "", "", "", _("^BGYou cannot change to a larger team"), "")
MSG_INFO_NOTIF(TEAMCHANGE_NOTALLOWED, N_CONSOLE, 0, 0, "", "", "", _("^BGYou are not allowed to change teams"), "")
MSG_CENTER_NOTIF(SUPERWEAPON_LOST, N_ENABLE, 0, 0, "", CPID_POWERUP, "0 0", _("^F2Superweapons have been lost"), "")
MSG_CENTER_NOTIF(SUPERWEAPON_PICKUP, N_ENABLE, 0, 0, "", CPID_POWERUP, "0 0", _("^F2You now have a superweapon"), "")
+ MSG_CENTER_NOTIF(SURVIVAL_1ST_ROUND_ATTACKER, N_ENABLE, 0, 0, "", CPID_Null, "0 0", _("^BGFirst round. Eliminate the enemy team ^F1as fast as you can^BG."), "")
+ MSG_CENTER_NOTIF(SURVIVAL_1ST_ROUND_DEFENDER, N_ENABLE, 0, 0, "", CPID_Null, "0 0", _("^BGFirst round. Defend yourself ^F1as long as you can^BG."), "")
+ MSG_CENTER_NOTIF(SURVIVAL_2ND_ROUND_ATTACKER, N_ENABLE, 0, 1, "f1time", CPID_Null, "0 0", _("^BGSecond round. Eliminate the enemy team in less than ^F1%s^BG."), "")
+ MSG_CENTER_NOTIF(SURVIVAL_2ND_ROUND_DEFENDER, N_ENABLE, 0, 1, "f1time", CPID_Null, "0 0", _("^BGSecond round. Defend yourself for ^F1%s^BG."), "")
+ MSG_CENTER_NOTIF(SURVIVAL_COOP_DEFENDER, N_ENABLE, 0, 0, "", CPID_Null, "0 0", _("^BGDefend yourself ^F1as long as you can^BG."), "")
+ MSG_CENTER_NOTIF(SURVIVAL_DEFENDERS_ELIMINATED, N_ENABLE, 0, 0, "", CPID_Null, "0 0", _("^BGDefenders were ^F1eliminated^BG."), "")
+ MSG_CENTER_NOTIF(SURVIVAL_DEFENDERS_ELIMINATED_IN, N_ENABLE, 0, 1, "f1time", CPID_Null, "0 0", _("^BGDefenders were eliminated in ^F1%s^BG."), "")
+ MSG_CENTER_NOTIF(SURVIVAL_DEFENDERS_SURVIVED, N_ENABLE, 0, 0, "", CPID_Null, "0 0", _("^BGDefenders have ^F1survived^BG."), "")
+
MULTITEAM_CENTER(TEAMCHANGE, 4, N_ENABLE, 0, 1, "", CPID_TEAMCHANGE, "1 f1", _("^K1Changing to ^TC^TT^K1 in ^COUNT"), "", NAME)
MSG_CENTER_NOTIF(TEAMCHANGE_AUTO, N_ENABLE, 0, 1, "", CPID_TEAMCHANGE, "1 f1", _("^K1Changing team in ^COUNT"), "")
MSG_CENTER_NOTIF(TEAMCHANGE_SPECTATE, N_ENABLE, 0, 1, "", CPID_TEAMCHANGE, "1 f1", _("^K1Spectating in ^COUNT"), "")
REGISTER_STAT(WEAPON_CLIPSIZE, int)
REGISTER_STAT(VORTEX_CHARGE, float)
+REGISTER_STAT(OVERKILL_VORTEX_CHARGE, float)
REGISTER_STAT(LAST_PICKUP, float)
REGISTER_STAT(HUD, int)
REGISTER_STAT(VORTEX_CHARGEPOOL, float)
+REGISTER_STAT(OVERKILL_VORTEX_CHARGEPOOL, float)
REGISTER_STAT(HIT_TIME, float)
REGISTER_STAT(DAMAGE_DEALT_TOTAL, int)
REGISTER_STAT(TYPEHIT_TIME, float)
REGISTER_STAT(DOM_PPS_YELLOW, float)
REGISTER_STAT(DOM_PPS_PINK, float)
+// gungame
+REGISTER_STAT(GUNGAME_LEADING_WEAPON, int)
+
+// Lyberta: survival
+REGISTER_STAT(SURV_ROUND_TIME, float)
+REGISTER_STAT(SURV_DEFENDER_TEAM, int)
+REGISTER_STAT(SURV_DEFENDERS_ALIVE, int)
+REGISTER_STAT(SURV_DEFENDER_HEALTH, float)
+
REGISTER_STAT(TELEPORT_MAXSPEED, float, autocvar_g_teleport_maxspeed)
REGISTER_STAT(TELEPORT_TELEFRAG_AVOID, int, autocvar_g_telefrags_avoid)
#ifdef SVQC
spawnfunc(weapon_shotgun) { weapon_defaultspawnfunc(this, WEP_SHOTGUN); }
-void W_Shotgun_Attack(Weapon thiswep, entity actor, .entity weaponentity, float isprimary)
+void W_Shotgun_Attack(Weapon thiswep, entity actor, .entity weaponentity, float isprimary, float ammocount, float damage, float bullets, float spread, float solidpenetration, float force)
{
- W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(shotgun, ammo), weaponentity);
+ W_DecreaseAmmo(thiswep, actor, ammocount, weaponentity);
- W_SetupShot(actor, weaponentity, true, 5, SND_SHOTGUN_FIRE, ((isprimary) ? CH_WEAPON_A : CH_WEAPON_SINGLE), WEP_CVAR_PRI(shotgun, damage) * WEP_CVAR_PRI(shotgun, bullets));
- for(int sc = 0;sc < WEP_CVAR_PRI(shotgun, bullets);sc = sc + 1)
- fireBullet(actor, weaponentity, w_shotorg, w_shotdir, WEP_CVAR_PRI(shotgun, spread), WEP_CVAR_PRI(shotgun, solidpenetration), WEP_CVAR_PRI(shotgun, damage), WEP_CVAR_PRI(shotgun, force), WEP_SHOTGUN.m_id, 0);
+ W_SetupShot(actor, weaponentity, true, 5, SND_SHOTGUN_FIRE, ((isprimary) ? CH_WEAPON_A : CH_WEAPON_SINGLE), damage * bullets);
+ for(int sc = 0;sc < bullets;sc = sc + 1)
+ fireBullet(actor, weaponentity, w_shotorg, w_shotdir, spread, solidpenetration, damage, force, WEP_SHOTGUN.m_id, 0);
- Send_Effect(EFFECT_SHOTGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, WEP_CVAR_PRI(shotgun, ammo));
+ Send_Effect(EFFECT_SHOTGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, ammocount);
// casing code
if(autocvar_g_casings >= 1)
}
sound(actor, CH_WEAPON_SINGLE, SND_Null, VOL_BASE, ATTN_NORM); // kill previous sound
- W_Shotgun_Attack(WEP_SHOTGUN, actor, weaponentity, true); // actually is secondary, but we trick the last shot into playing full reload sound
+ W_Shotgun_Attack(WEP_SHOTGUN, actor, weaponentity, true,
+ WEP_CVAR_PRI(shotgun, ammo),
+ WEP_CVAR_PRI(shotgun, damage),
+ WEP_CVAR_PRI(shotgun, bullets),
+ WEP_CVAR_PRI(shotgun, spread),
+ WEP_CVAR_PRI(shotgun, solidpenetration),
+ WEP_CVAR_PRI(shotgun, force)); // actually is secondary, but we trick the last shot into playing full reload sound
weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), w_ready);
}
void W_Shotgun_Attack3_Frame1(Weapon thiswep, entity actor, .entity weaponentity, int fire)
return;
}
- W_Shotgun_Attack(WEP_SHOTGUN, actor, weaponentity, false);
+ W_Shotgun_Attack(WEP_SHOTGUN, actor, weaponentity, false,
+ WEP_CVAR_PRI(shotgun, ammo),
+ WEP_CVAR_PRI(shotgun, damage),
+ WEP_CVAR_PRI(shotgun, bullets),
+ WEP_CVAR_PRI(shotgun, spread),
+ WEP_CVAR_PRI(shotgun, solidpenetration),
+ WEP_CVAR_PRI(shotgun, force));
weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame2);
}
else
PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false);
}
+
METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire))
{
if(WEP_CVAR(shotgun, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR_PRI(shotgun, ammo)) // forced reload
{
if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(shotgun, animtime)))
{
- W_Shotgun_Attack(thiswep, actor, weaponentity, true);
+ W_Shotgun_Attack(thiswep, actor, weaponentity, true,
+ WEP_CVAR_PRI(shotgun, ammo),
+ WEP_CVAR_PRI(shotgun, damage),
+ WEP_CVAR_PRI(shotgun, bullets),
+ WEP_CVAR_PRI(shotgun, spread),
+ WEP_CVAR_PRI(shotgun, solidpenetration),
+ WEP_CVAR_PRI(shotgun, force));
actor.(weaponentity).shotgun_primarytime = time + WEP_CVAR_PRI(shotgun, refire) * W_WeaponRateFactor(actor);
weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(shotgun, animtime), w_ready);
}
{
if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(shotgun, alt_animtime)))
{
- W_Shotgun_Attack(thiswep, actor, weaponentity, false);
+ W_Shotgun_Attack(thiswep, actor, weaponentity, false,
+ WEP_CVAR_PRI(shotgun, ammo),
+ WEP_CVAR_PRI(shotgun, damage),
+ WEP_CVAR_PRI(shotgun, bullets),
+ WEP_CVAR_PRI(shotgun, spread),
+ WEP_CVAR_PRI(shotgun, solidpenetration),
+ WEP_CVAR_PRI(shotgun, force));
actor.(weaponentity).shotgun_primarytime = time + WEP_CVAR_SEC(shotgun, alt_refire) * W_WeaponRateFactor(actor);
weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame1);
}
{ WriteByte(chan, this.vortex_charge * 16); }, \
{ (viewmodels[this.m_wepent_slot]).vortex_charge = ReadByte() / 16; }) \
\
+ PROP(false, okvortex_charge, WEPENT_SET_NORMAL, \
+ { WriteByte(chan, this.okvortex_charge * 16); }, \
+ { (viewmodels[this.m_wepent_slot]).okvortex_charge = ReadByte() / 16; }) \
+ \
PROP(false, m_gunalign, WEPENT_SET_NORMAL, \
{ WriteByte(chan, this.m_gunalign); }, \
{ (viewmodels[this.m_wepent_slot]).m_gunalign = ReadByte(); }) \
REGISTER_NET_TEMP(CLIENT_WEPENT)
.float vortex_charge;
+.float okvortex_charge;
.int tuba_instrument;
#ifdef SVQC
case MAPINFO_TYPE_CTS: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); break;
case MAPINFO_TYPE_INVASION: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); break;
case MAPINFO_TYPE_TEAM_DEATHMATCH: GameType_ConfigureSliders(me, _("Point limit:"), 5, 100, 5, "g_tdm_point_limit", "g_tdm_teams_override", _("The amount of points needed before the match will end")); break;
+ case MAPINFO_TYPE_SURVIVAL: GameType_ConfigureSliders(me, _("Point limit:"), 1, 20, 1, "g_surv_point_limit", string_null, _("The amount of points needed before the match will end")); break;
default: GameType_ConfigureSliders(me, _("Frag limit:"), 5, 100, 5, "fraglimit_override", string_null, _("The amount of frags needed before the match will end")); break;
}
}
GAMETYPE(MAPINFO_TYPE_NEXBALL) \
GAMETYPE(MAPINFO_TYPE_ONSLAUGHT) \
GAMETYPE(MAPINFO_TYPE_ASSAULT) \
+ GAMETYPE(MAPINFO_TYPE_GUNGAME) \
+ GAMETYPE(MAPINFO_TYPE_SURVIVAL) \
/* GAMETYPE(MAPINFO_TYPE_INVASION) */ \
/**/
#include <server/miscfunctions.qc>
#include <server/player.qc>
#include <server/playerdemo.qc>
+#include <server/playertemplates.qc>
#include <server/portals.qc>
#include <server/race.qc>
#include <server/resources.qc>
#include <server/miscfunctions.qh>
#include <server/player.qh>
#include <server/playerdemo.qh>
+#include <server/playertemplates.qh>
#include <server/portals.qh>
#include <server/race.qh>
#include <server/resources.qh>
#include "../lib/warpzone/server.qh"
+#include <common/mutators/mutator/overkill/okvortex.qh>
+
STATIC_METHOD(Client, Add, void(Client this, int _team))
{
ClientConnect(this);
this.weapons = spectatee.weapons;
this.vortex_charge = spectatee.vortex_charge;
this.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo;
+ this.okvortex_charge = spectatee.okvortex_charge;
+ this.okvortex_chargepool_ammo = spectatee.okvortex_chargepool_ammo;
this.hagar_load = spectatee.hagar_load;
this.arc_heat_percent = spectatee.arc_heat_percent;
this.minelayer_mines = spectatee.minelayer_mines;
.float vortex_charge = _STAT(VORTEX_CHARGE);
.float vortex_charge_rottime;
.float vortex_chargepool_ammo = _STAT(VORTEX_CHARGEPOOL);
+.float okvortex_charge = _STAT(OVERKILL_VORTEX_CHARGE);
+.float okvortex_charge_rottime;
+.float okvortex_chargepool_ammo = _STAT(OVERKILL_VORTEX_CHARGEPOOL);
.float hagar_load = _STAT(HAGAR_LOAD);
.int grab; // 0 = can't grab, 1 = owner can grab, 2 = owner and team mates can grab, 3 = anyone can grab
BADCVAR("g_domination_default_teams");
BADCVAR("g_freezetag");
BADCVAR("g_freezetag_teams");
+ BADCVAR("g_gg");
BADCVAR("g_invasion_teams");
BADCVAR("g_invasion_type");
BADCVAR("g_jailbreak");
BADCVAR("g_race_qualifying_timelimit_override");
BADCVAR("g_runematch");
BADCVAR("g_snafu");
+ BADCVAR("g_surv"); // Lyberta: adding gamemode cvar
BADCVAR("g_tdm");
BADCVAR("g_tdm_teams");
BADCVAR("g_vip");
--- /dev/null
+/// \file
+/// \brief Source file that contains implementation of the player templates
+/// system.
+/// \author Lyberta
+/// \copyright GNU GPLv3 or any later version.
+
+#include <common/items/item.qh>
+#include "autocvars.qh"
+#include "resources.qh"
+#include "mutators/events.qh"
+
+const string playertemplate_cvar_prefix = "g_player_template_";
+
+string PlayerTemplate_GetFullCvarName(string template, string variable)
+{
+ return strcat(playertemplate_cvar_prefix, template, "_", variable);
+}
+
+string PlayerTemplate_GetDefaultCvarName(string variable)
+{
+ switch (variable)
+ {
+ case "start_health":
+ {
+ return "g_balance_health_start";
+ }
+ case "start_armor":
+ {
+ return "g_balance_armor_start";
+ }
+ case "start_ammo_shells":
+ {
+ return "g_start_ammo_shells";
+ }
+ case "start_ammo_bullets":
+ {
+ return "g_start_ammo_nails";
+ }
+ case "start_ammo_rockets":
+ {
+ return "g_start_ammo_rockets";
+ }
+ case "start_ammo_cells":
+ {
+ return "g_start_ammo_cells";
+ }
+ case "start_ammo_plasma":
+ {
+ return "g_start_ammo_plasma";
+ }
+ case "start_ammo_fuel":
+ {
+ return "g_start_ammo_fuel";
+ }
+ case "random_start_weapons_count":
+ {
+ return "g_random_start_weapons_count";
+ }
+ case "random_start_weapons":
+ {
+ return "g_random_start_weapons";
+ }
+ case "random_start_shells":
+ {
+ return "g_random_start_shells";
+ }
+ case "random_start_bullets":
+ {
+ return "g_random_start_bullets";
+ }
+ case "random_start_rockets":
+ {
+ return "g_random_start_rockets";
+ }
+ case "random_start_cells":
+ {
+ return "g_random_start_cells";
+ }
+ case "random_start_plasma":
+ {
+ return "g_random_start_plasma";
+ }
+ case "drop_weapons":
+ {
+ return "g_weapon_throwable";
+ }
+ case "health_regen_factor":
+ {
+ return "g_balance_health_regen";
+ }
+ case "health_regen_linear":
+ {
+ return "g_balance_health_regenlinear";
+ }
+ case "health_rot_factor":
+ {
+ return "g_balance_health_rot";
+ }
+ case "health_rot_linear":
+ {
+ return "g_balance_health_rotlinear";
+ }
+ case "health_regen_stable":
+ {
+ return "g_balance_health_regenstable";
+ }
+ case "health_rot_stable":
+ {
+ return "g_balance_health_rotstable";
+ }
+ default:
+ {
+ // TODO: Report error.
+ return "";
+ }
+ }
+}
+
+float PlayerTemplate_GetDefaultFloatValue(string variable)
+{
+ switch (variable)
+ {
+ case "start_health":
+ case "start_armor":
+ case "start_ammo_shells":
+ case "start_ammo_bullets":
+ case "start_ammo_rockets":
+ case "start_ammo_cells":
+ case "start_ammo_plasma":
+ case "start_ammo_fuel":
+ case "random_start_weapons_count":
+ case "random_start_shells":
+ case "random_start_bullets":
+ case "random_start_rockets":
+ case "random_start_cells":
+ case "random_start_plasma":
+ case "drop_weapons":
+ case "health_regen_factor":
+ case "health_regen_linear":
+ case "health_rot_factor":
+ case "health_rot_linear":
+ case "health_regen_stable":
+ case "health_rot_stable":
+ {
+ return cvar(PlayerTemplate_GetDefaultCvarName(variable));
+ }
+ case "unlimited_ammo":
+ {
+ return !autocvar_g_use_ammunition;
+ }
+ case "default_start_weapons":
+ {
+ return 1;
+ }
+ case "attack_scale":
+ case "defense_scale":
+ {
+ return 1;
+ }
+ case "blaster_self_damage":
+ {
+ return 1;
+ }
+ default:
+ {
+ // TODO: Report error.
+ return 0;
+ }
+ }
+}
+
+string PlayerTemplate_GetDefaultStringValue(string variable)
+{
+ switch (variable)
+ {
+ case "random_start_weapons":
+ {
+ cvar_string(PlayerTemplate_GetDefaultCvarName(variable));
+ }
+ case "start_weapons":
+ {
+ return "";
+ }
+ default:
+ {
+ // TODO: Report error.
+ return "";
+ }
+ }
+}
+
+float PlayerTemplate_GetFloatValue(string template, string variable)
+{
+ if (template == "default")
+ {
+ return PlayerTemplate_GetDefaultFloatValue(variable);
+ }
+ string fullname = PlayerTemplate_GetFullCvarName(template, variable);
+ if (!(cvar_type(fullname) & CVAR_TYPEFLAG_EXISTS))
+ {
+ return PlayerTemplate_GetDefaultFloatValue(variable);
+ }
+ if (cvar_string(fullname) == "default")
+ {
+ return PlayerTemplate_GetDefaultFloatValue(variable);
+ }
+ return cvar(fullname);
+}
+
+string PlayerTemplate_GetStringValue(string template, string variable)
+{
+ if (template == "default")
+ {
+ return PlayerTemplate_GetDefaultStringValue(variable);
+ }
+ string fullname = PlayerTemplate_GetFullCvarName(template, variable);
+ if (!(cvar_type(fullname) & CVAR_TYPEFLAG_EXISTS))
+ {
+ return PlayerTemplate_GetDefaultStringValue(variable);
+ }
+ if (cvar_string(fullname) == "default")
+ {
+ return PlayerTemplate_GetDefaultStringValue(variable);
+ }
+ return cvar_string(fullname);
+}
+
+float PlayerTemplate_GivePlayerItem(entity player, string template,
+ string variable)
+{
+ string value = PlayerTemplate_GetStringValue(template, variable);
+ if (value == "default")
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ int numfields = tokenize_console(PlayerTemplate_GetStringValue(template,
+ variable));
+ if (numfields == 0)
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ for (int i = 1; i < numfields; ++i)
+ {
+ switch (i)
+ {
+ case 1:
+ {
+ GiveResource(player, RESOURCE_HEALTH, stof(argv(i)));
+ break;
+ }
+ case 2:
+ {
+ GiveResource(player, RESOURCE_ARMOR, stof(argv(i)));
+ break;
+ }
+ case 3:
+ {
+ GiveResource(player, RESOURCE_SHELLS, stof(argv(i)));
+ break;
+ }
+ case 4:
+ {
+ GiveResource(player, RESOURCE_BULLETS, stof(argv(i)));
+ break;
+ }
+ case 5:
+ {
+ GiveResource(player, RESOURCE_ROCKETS, stof(argv(i)));
+ break;
+ }
+ case 6:
+ {
+ GiveResource(player, RESOURCE_CELLS, stof(argv(i)));
+ break;
+ }
+ case 7:
+ {
+ GiveResource(player, RESOURCE_PLASMA, stof(argv(i)));
+ break;
+ }
+ case 8:
+ {
+ GiveResource(player, RESOURCE_FUEL, stof(argv(i)));
+ break;
+ }
+ }
+ }
+ switch (argv(0))
+ {
+ case "add":
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case "override":
+ {
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+}
+
+// =========================== Hook handlers =================================
+
+void PlayerTemplateHook_PlayerSpawn(entity player, string template)
+{
+ if (template == "default")
+ {
+ return;
+ }
+ // Give health, armor and ammo.
+ SetResourceAmount(player, RESOURCE_HEALTH,
+ PlayerTemplate_GetFloatValue(template, "start_health"));
+ SetResourceAmount(player, RESOURCE_ARMOR,
+ PlayerTemplate_GetFloatValue(template, "start_armor"));
+ if (PlayerTemplate_GetFloatValue(template, "unlimited_ammo"))
+ {
+ player.items |= IT_UNLIMITED_AMMO;
+ }
+ else
+ {
+ SetResourceAmount(player, RESOURCE_SHELLS,
+ PlayerTemplate_GetFloatValue(template, "start_ammo_shells"));
+ SetResourceAmount(player, RESOURCE_BULLETS,
+ PlayerTemplate_GetFloatValue(template, "start_ammo_bullets"));
+ SetResourceAmount(player, RESOURCE_ROCKETS,
+ PlayerTemplate_GetFloatValue(template, "start_ammo_rockets"));
+ SetResourceAmount(player, RESOURCE_CELLS,
+ PlayerTemplate_GetFloatValue(template, "start_ammo_cells"));
+ SetResourceAmount(player, RESOURCE_PLASMA,
+ PlayerTemplate_GetFloatValue(template, "start_ammo_plasma"));
+ SetResourceAmount(player, RESOURCE_FUEL,
+ PlayerTemplate_GetFloatValue(template, "start_ammo_fuel"));
+ }
+ if (autocvar_g_instagib == 1)
+ {
+ return;
+ }
+ // Give weapons.
+ if (PlayerTemplate_GetFloatValue(template, "default_start_weapons"))
+ {
+ FOREACH(Weapons, it != WEP_Null,
+ {
+ if (it.weaponstart)
+ {
+ player.weapons |= it.m_wepset;
+ }
+ });
+ }
+ int numweapons = tokenize_console(PlayerTemplate_GetStringValue(template,
+ "start_weapons"));
+ for (int i = 0; i < numweapons; ++i)
+ {
+ string weapon = argv(i);
+ FOREACH(Weapons, it != WEP_Null,
+ {
+ if (it.netname == weapon)
+ {
+ player.weapons |= it.m_wepset;
+ break;
+ }
+ });
+ }
+ if (!warmup_stage)
+ {
+ entity ammo_entity = spawn();
+ SetResourceAmount(ammo_entity, RESOURCE_SHELLS,
+ PlayerTemplate_GetFloatValue(template, "random_start_shells"));
+ SetResourceAmount(ammo_entity, RESOURCE_BULLETS,
+ PlayerTemplate_GetFloatValue(template, "random_start_bullets"));
+ SetResourceAmount(ammo_entity, RESOURCE_ROCKETS,
+ PlayerTemplate_GetFloatValue(template, "random_start_rockets"));
+ SetResourceAmount(ammo_entity, RESOURCE_CELLS,
+ PlayerTemplate_GetFloatValue(template, "random_start_cells"));
+ SetResourceAmount(ammo_entity, RESOURCE_PLASMA,
+ PlayerTemplate_GetFloatValue(template, "random_start_plasma"));
+ GiveRandomWeapons(player, PlayerTemplate_GetFloatValue(template,
+ "random_start_weapons_count"),
+ PlayerTemplate_GetStringValue(template, "random_start_weapons"),
+ ammo_entity);
+ remove(ammo_entity);
+ return;
+ }
+ // Give random weapons.
+ numweapons = tokenize_console(PlayerTemplate_GetStringValue(template,
+ "random_start_weapons"));
+ // Give all weapons during warmup stage.
+ for (int i = 0; i < numweapons; ++i)
+ {
+ string weapon = argv(i);
+ FOREACH(Weapons, it != WEP_Null,
+ {
+ if (it.netname == weapon)
+ {
+ player.weapons |= it.m_wepset;
+ break;
+ }
+ });
+ }
+}
+
+bool PlayerTemplateHook_ForbidThrowCurrentWeapon(string template)
+{
+ return !PlayerTemplate_GetFloatValue(template, "drop_weapons");
+}
+
+float PlayerTemplateHook_PlayerRegen(entity player, string template)
+{
+ if (template == "default")
+ {
+ return false;
+ }
+ M_ARGV(5, float) = PlayerTemplate_GetFloatValue(template,
+ "health_regen_factor");
+ M_ARGV(6, float) = PlayerTemplate_GetFloatValue(template,
+ "health_regen_linear");
+ M_ARGV(7, float) = PlayerTemplate_GetFloatValue(template,
+ "health_rot_factor");
+ M_ARGV(8, float) = PlayerTemplate_GetFloatValue(template,
+ "health_rot_linear");
+ M_ARGV(9, float) = PlayerTemplate_GetFloatValue(template,
+ "health_regen_stable");
+ M_ARGV(10, float) = PlayerTemplate_GetFloatValue(template,
+ "health_rot_stable");
+ return false;
+}
+
+float PlayerTemplateHook_ItemTouch(entity player, entity item, string template)
+{
+ if (template == "default")
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ switch (item.classname)
+ {
+ case "item_health_small":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_health_small");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_HealthSmall, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "item_health_medium":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_health_medium");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_HealthMedium, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "item_health_big":
+ case "item_health_large":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_health_big");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_HealthBig, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "item_health_mega":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_health_mega");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_HealthMega, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "item_armor_small":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_armor_small");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_ArmorSmall, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "item_armor_medium":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_armor_medium");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_ArmorMedium, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "item_armor_big":
+ case "item_armor_large":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_armor_big");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_ArmorBig, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "item_armor_mega":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_armor_mega");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_ArmorMega, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "item_shells":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_item_shells");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_ITEMPICKUP, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "item_bullets":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_item_bullets");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_ITEMPICKUP, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "item_rockets":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_item_rockets");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_ITEMPICKUP, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "item_cells":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_item_cells");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_ITEMPICKUP, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "weapon_machinegun":
+ case "weapon_uzi":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_machinegun");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_WEAPONPICKUP, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "weapon_grenadelauncher":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_mortar");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_WEAPONPICKUP, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "weapon_electro":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_electro");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_WEAPONPICKUP, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "weapon_crylink":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_crylink");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_WEAPONPICKUP, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "weapon_nex":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_vortex");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_WEAPONPICKUP, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "weapon_hagar":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_hagar");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_WEAPONPICKUP, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "weapon_rocketlauncher":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_devastator");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_WEAPONPICKUP, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "droppedweapon":
+ {
+ float result;
+ switch (item.weapon)
+ {
+ case WEP_SHOTGUN.m_id:
+ {
+ result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_dropped_shotgun");
+ break;
+ }
+ case WEP_MACHINEGUN.m_id:
+ {
+ result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_dropped_machinegun");
+ break;
+ }
+ case WEP_MORTAR.m_id:
+ {
+ result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_dropped_mortar");
+ break;
+ }
+ case WEP_ELECTRO.m_id:
+ {
+ result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_dropped_electro");
+ break;
+ }
+ case WEP_CRYLINK.m_id:
+ {
+ result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_dropped_crylink");
+ break;
+ }
+ case WEP_VORTEX.m_id:
+ {
+ result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_dropped_vortex");
+ break;
+ }
+ case WEP_HAGAR.m_id:
+ {
+ result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_dropped_hagar");
+ break;
+ }
+ case WEP_DEVASTATOR.m_id:
+ {
+ result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_dropped_devastator");
+ break;
+ }
+ case WEP_MINE_LAYER.m_id:
+ {
+ result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_dropped_minelayer");
+ break;
+ }
+ case WEP_HLAC.m_id:
+ {
+ result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_dropped_hlac");
+ break;
+ }
+ case WEP_RIFLE.m_id:
+ {
+ result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_dropped_rifle");
+ break;
+ }
+ case WEP_SEEKER.m_id:
+ {
+ result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_dropped_seeker");
+ break;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ delete(item);
+ sound(player, CH_TRIGGER, SND_WEAPONPICKUP, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "replacedweapon":
+ {
+ float result;
+ switch (item.weapon)
+ {
+ case WEP_MINE_LAYER.m_id:
+ {
+ result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_minelayer");
+ break;
+ }
+ case WEP_HLAC.m_id:
+ {
+ result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_hlac");
+ break;
+ }
+ case WEP_RIFLE.m_id:
+ {
+ result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_rifle");
+ break;
+ }
+ case WEP_SEEKER.m_id:
+ {
+ result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_weapon_seeker");
+ break;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_WEAPONPICKUP, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "item_strength":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_item_strength");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_Strength, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ case "item_invincible":
+ {
+ float result = PlayerTemplate_GivePlayerItem(player, template,
+ "pickup_item_shield");
+ switch (result)
+ {
+ case MUT_ITEMTOUCH_CONTINUE:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ case MUT_ITEMTOUCH_RETURN:
+ {
+ Item_ScheduleRespawn(item);
+ sound(player, CH_TRIGGER, SND_Shield, VOL_BASE,
+ ATTEN_NORM);
+ return MUT_ITEMTOUCH_RETURN;
+ }
+ default:
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ default:
+ {
+ switch (item.netname)
+ {
+ case "Vaporizer Ammo":
+ case "Extra life":
+ case "Invisibility":
+ case "Speed":
+ {
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ default:
+ {
+ PrintToChatAll(strcat("Unrecognized item, classname: ",
+ item.classname, " netname: ", item.netname));
+ return MUT_ITEMTOUCH_CONTINUE;
+ }
+ }
+ }
+ }
+ return MUT_ITEMTOUCH_CONTINUE;
+}
+
+float PlayerTemplateHook_Damage_Calculate(entity attacker,
+ string attackertemplate, entity victim, string victimtemplate,
+ float deathtype, float damage)
+{
+ if (autocvar_g_instagib == 1)
+ {
+ return damage;
+ }
+ if ((attacker == victim) && (DEATH_ISWEAPON(deathtype, WEP_BLASTER)) &&
+ (PlayerTemplate_GetFloatValue(victimtemplate, "blaster_self_damage") ==
+ 0))
+ {
+ return 0;
+ }
+ damage *= PlayerTemplate_GetFloatValue(attackertemplate, "attack_scale");
+ damage /= PlayerTemplate_GetFloatValue(victimtemplate, "defense_scale");
+ return damage;
+}
+
+void PlayerTemplateHook_PlayerDies(entity player, string template)
+{
+ if (template == "default")
+ {
+ return;
+ }
+ if (PlayerTemplate_GetFloatValue(template, "drop_weapons"))
+ {
+ return;
+ }
+ player.weapons = WEPSET(Null);
+}
--- /dev/null
+/// \file
+/// \brief Header file that describes the player templates system.
+/// \author Lyberta
+/// \copyright GNU GPLv3 or any later version.
+
+#pragma once
+
+/// \brief Returns the full name of the player template cvar.
+/// \param[in] template Name of the template.
+/// \param[in] variable Name of the variable.
+/// \return Full name of the variable.
+string PlayerTemplate_GetFullCvarName(string template, string variable);
+
+/// \brief Returns the name of the cvar that is used for default value.
+/// \param[in] Name of the player template variable.
+/// \return Name of the cvar that is used for default value.
+string PlayerTemplate_GetDefaultCvarName(string variable);
+
+/// \brief Returns the default floating point value of the player template
+/// variable.
+/// \param[in] variable Name of the variable.
+/// \return Default floating point value of the player template variable.
+float PlayerTemplate_GetDefaultFloatValue(string variable);
+
+/// \brief Returns the default string value of the player template variable.
+/// \param[in] variable Name of the variable.
+/// \return Default string value of the player template variable.
+string PlayerTemplate_GetDefaultStringValue(string variable);
+
+/// \brief Gets the floating point value of the variable from the given
+/// template.
+/// \param[in] template Name of the template.
+/// \param[in] variable Name of the variable.
+/// \return Value of the variable.
+float PlayerTemplate_GetFloatValue(string template, string variable);
+
+/// \brief Gets the string value of the variable from the given template.
+/// \param[in] template Name of the template.
+/// \param[in] variable Name of the variable.
+/// \return Value of the variable.
+string PlayerTemplate_GetStringValue(string template, string variable);
+
+/// \brief Gives player items according to the given template's variable.
+/// \param[in] player Player to give items to.
+/// \param[in] template Name of the template.
+/// \param[in] variable Name of the variable.
+/// \return Enum value to pass to mutator hook.
+float PlayerTemplate_GivePlayerItem(entity player, string template,
+ string variable);
+
+// =========================== Hook handlers =================================
+
+/// \brief Setups the player during spawn according to the given template.
+/// \param[in,out] player Player to setup.
+/// \param[in] template Name of the template.
+/// \return No return.
+void PlayerTemplateHook_PlayerSpawn(entity player, string template);
+
+/// \brief Forbids weapon dropping according to the given template.
+/// \param[in] template Name of the template.
+/// \return Value to pass to mutator hook.
+bool PlayerTemplateHook_ForbidThrowCurrentWeapon(string template);
+
+/// \brief Regenerates player health according to the given template.
+/// \param[in] player Player to regenerate.
+/// \param[in] template Name of the template.
+/// \return Value to pass to mutator hook.
+float PlayerTemplateHook_PlayerRegen(entity player, string template);
+
+/// \brief Gives player items according to the given template.
+/// \param[in,out] player Player to give items to.
+/// \param[in] item Item which player has picked up.
+/// \param[in] template Name of the template.
+/// \return Enum value to pass to mutator hook.
+float PlayerTemplateHook_ItemTouch(entity player, entity item, string template);
+
+/// \brief Changes the damage done using templates' attack and defense scales.
+/// \param[in] attacke Attacker entity.
+/// \param[in] attackertemplate Template of the attacker.
+/// \param[in] victim Victim entity.
+/// \param[in] victimtemplate Template of the victim.
+/// \param[in] deathtype Type of the damage.
+/// \param[in] damage Damage to adjust.
+/// \return Adjusted damage.
+float PlayerTemplateHook_Damage_Calculate(entity attacker,
+ string attackertemplate, entity victim, string victimtemplate,
+ float deathtype, float damage);
+
+/// \brief Strips the player of their weapons if the player is not allowed to
+/// drop them.
+/// \param[in,out] player Player to work with.
+/// \param[in] template Name of the template.
+/// \return No return.
+/// \note You must hook with CBC_ORDER_FIRST in order for this to be effective.
+void PlayerTemplateHook_PlayerDies(entity player, string template);
--- /dev/null
+// Cvars for survival gamemode.
+
+alias cl_hook_gamestart_surv
+alias sv_hook_gamestart_surv
+alias sv_vote_gametype_hook_surv
+
+set g_surv_respawn_delay_small 0
+set g_surv_respawn_delay_small_count 0
+set g_surv_respawn_delay_large 0
+set g_surv_respawn_delay_large_count 0
+set g_surv_respawn_delay_max 0
+set g_surv_respawn_waves 0
+set g_surv_weapon_stay 0
+
+set g_surv 0 "Survival: survive as long as you can"
+set g_surv_type "versus" "Type of the survival gametype"
+set g_surv_warmup 10 "How long the players will have time to run around the map before the round starts"
+set g_surv_round_timelimit 300 "Round time limit in seconds"
+seta g_surv_point_limit -1 "Survival point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_surv_point_leadlimit -1 "Survival point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+set g_surv_team_size 4 "How much players are allowed in teams (excluding cannon fodder)"
+set g_surv_stealth 0 "If set, defenders will not be shown on the radar"
+set g_surv_spectate_enemies 0 "Whether to allow spectating enemy players while dead"
+
+set g_surv_attacker_template "surv_attacker" "Player template of attackers"
+set g_surv_defender_template "surv_defender" "Player template of defenders"
+set g_surv_cannon_fodder_template "surv_cannon_fodder" "Player template of cannon fodder"
+
+set g_surv_attacker_force_overkill_models 0 "Whether to force overkill player models for attackers"
+set g_surv_defender_force_overkill_models 1 "Whether to force overkill player models for defenders"
+set g_surv_cannon_fodder_force_overkill_models 0 "Whether to force overkill player models for cannon fodder"
+
+set g_player_template_surv_attacker_unlimited_ammo 1 "Whether to give attackers unlimited ammo"
+set g_player_template_surv_attacker_random_start_weapons_count 2 "Number of weapons that can be randomly given to attackers during spawn"
+set g_player_template_surv_attacker_random_start_weapons "machinegun mortar electro crylink vortex hagar devastator" "Weapons that can be randomly given to attackers during spawn"
+
+set g_player_template_surv_defender_start_health 200 "How much health do defenders get during spawn"
+set g_player_template_surv_defender_start_armor 200 "How much armor do defenders get during spawn"
+set g_player_template_surv_defender_start_ammo_shells 15 "How many shells do defenders get during spawn"
+set g_player_template_surv_defender_start_ammo_bullets 15 "How many bullets do defenders get during spawn"
+set g_player_template_surv_defender_start_ammo_rockets 0 "How many rockets do defenders get during spawn"
+set g_player_template_surv_defender_start_ammo_cells 30 "How many cells do defenders get during spawn"
+set g_player_template_surv_defender_default_start_weapons 0 "Whether to give defenders default start weapons"
+set g_player_template_surv_defender_start_weapons "okshotgun okmachinegun okvortex" "Which weapons do defenders get during spawn"
+
+set g_player_template_surv_cannon_fodder_unlimited_ammo 1 "Whether to give cannon fodder unlimited ammo"
+set g_player_template_surv_cannon_fodder_random_start_weapons_count 2 "Number of weapons that can be randomly given to cannon fodder during spawn"
+set g_player_template_surv_cannon_fodder_random_start_weapons "machinegun mortar electro crylink vortex hagar devastator" "Weapons that can be randomly given to cannon fodder during spawn"
+
+set g_player_template_surv_defender_drop_weapons 0 "Whether defenders can drop weapons by throwing them or by dying"
+
+set g_player_template_surv_attacker_pickup_item_shells "add 25 25" "What items do attackers get when they pickup shells"
+set g_player_template_surv_attacker_pickup_item_bullets "add 25 25" "What items do attackers get when they pickup bullets"
+set g_player_template_surv_attacker_pickup_item_rockets "add 25 25" "What items do attackers get when they pickup rockets"
+set g_player_template_surv_attacker_pickup_item_cells "add 25 25" "What items do attackers get when they pickup cells"
+set g_player_template_surv_attacker_pickup_weapon_dropped_shotgun "add 25 25" "What items do attackers get when they pickup a dropped shotgun"
+set g_player_template_surv_attacker_pickup_weapon_dropped_machinegun "add 25 25" "What items do attackers get when they pickup a dropped machinegun"
+set g_player_template_surv_attacker_pickup_weapon_dropped_mortar "add 25 25" "What items do attackers get when they pickup a dropped mortar"
+set g_player_template_surv_attacker_pickup_weapon_dropped_electro "add 25 25" "What items do attackers get when they pickup a dropped electro"
+set g_player_template_surv_attacker_pickup_weapon_dropped_crylink "add 25 25" "What items do attackers get when they pickup a dropped crylink"
+set g_player_template_surv_attacker_pickup_weapon_dropped_vortex "add 25 25" "What items do attackers get when they pickup a dropped vortex"
+set g_player_template_surv_attacker_pickup_weapon_dropped_hagar "add 25 25" "What items do attackers get when they pickup a dropped hagar"
+set g_player_template_surv_attacker_pickup_weapon_dropped_devastator "add 25 25" "What items do attackers get when they pickup a dropped devastator"
+set g_player_template_surv_attacker_pickup_weapon_dropped_minelayer "add 25 25" "What items do attackers get when they pickup a dropped mine layer"
+set g_player_template_surv_attacker_pickup_weapon_dropped_hlac "add 25 25" "What items do attackers get when they pickup a dropped HLAC"
+set g_player_template_surv_attacker_pickup_weapon_dropped_rifle "add 25 25" "What items do attackers get when they pickup a dropped rifle"
+set g_player_template_surv_attacker_pickup_weapon_dropped_seeker "add 25 25" "What items do attackers get when they pickup a dropped TAG seeker"
+
+set g_player_template_surv_defender_pickup_health_small "override 0 0 5 5 10 10" "What items do defenders get when they pickup small health"
+set g_player_template_surv_defender_pickup_health_medium "override 0 0 10 10 20 20" "What items do defenders get when they pickup medium health"
+set g_player_template_surv_defender_pickup_health_big "override 0 0 20 15 40 40" "What items do defenders get when they pickup big health"
+set g_player_template_surv_defender_pickup_health_mega "override 0 0 30 30 50 50" "What items do defenders get when they pickup mega health"
+set g_player_template_surv_defender_pickup_armor_small "override 0 0 5 5 10 10" "What items do defenders get when they pickup small armor"
+set g_player_template_surv_defender_pickup_armor_medium "override 0 0 10 10 20 20" "What items do defenders get when they pickup medium armor"
+set g_player_template_surv_defender_pickup_armor_big "override 0 0 20 15 40 40" "What items do defenders get when they pickup big armor"
+set g_player_template_surv_defender_pickup_armor_mega "override 0 0 30 30 50 50" "What items do defenders get when they pickup mega armor"
+set g_player_template_surv_defender_pickup_weapon_machinegun "override 0 0 0 80" "What items do defenders get when they pickup a machinegun"
+set g_player_template_surv_defender_pickup_weapon_mortar "override 0 0 0 0 40" "What items do defenders get when they pickup a mortar"
+set g_player_template_surv_defender_pickup_weapon_electro "override 0 0 0 0 0 30" "What items do defenders get when they pickup an electro"
+set g_player_template_surv_defender_pickup_weapon_crylink "override 0 0 0 0 0 30" "What items do defenders get when they pickup a crylink"
+set g_player_template_surv_defender_pickup_weapon_vortex "override 0 0 0 0 0 30" "What items do defenders get when they pickup a vortex"
+set g_player_template_surv_defender_pickup_weapon_hagar "override 0 0 0 0 40" "What items do defenders get when they pickup a hagar"
+set g_player_template_surv_defender_pickup_weapon_devastator "override 0 0 0 0 40" "What items do defenders get when they pickup a devastator"
+set g_player_template_surv_defender_pickup_weapon_minelayer "override 0 0 0 0 40" "What items do defenders get when they pickup a mine layer"
+set g_player_template_surv_defender_pickup_weapon_hlac "override 0 0 0 0 0 30" "What items do defenders get when they pickup a HLAC"
+set g_player_template_surv_defender_pickup_weapon_rifle "override 0 0 0 80" "What items do defenders get when they pickup a rifle"
+set g_player_template_surv_defender_pickup_weapon_seeker "override 0 0 0 0 40" "What items do defenders get when they pickup a TAG seeker"
+set g_player_template_surv_defender_pickup_weapon_dropped_shotgun "override 0 0 15" "What items do defenders get when they pickup a dropped shotgun"
+set g_player_template_surv_defender_pickup_weapon_dropped_machinegun "override 0 0 0 80" "What items do defenders get when they pickup a dropped machinegun"
+set g_player_template_surv_defender_pickup_weapon_dropped_mortar "override 0 0 0 0 40" "What items do defenders get when they pickup a dropped mortar"
+set g_player_template_surv_defender_pickup_weapon_dropped_electro "override 0 0 0 0 0 30" "What items do defenders get when they pickup a dropped electro"
+set g_player_template_surv_defender_pickup_weapon_dropped_crylink "override 0 0 0 0 0 30" "What items do defenders get when they pickup a dropped crylink"
+set g_player_template_surv_defender_pickup_weapon_dropped_vortex "override 0 0 0 0 0 30" "What items do defenders get when they pickup a dropped vortex"
+set g_player_template_surv_defender_pickup_weapon_dropped_hagar "override 0 0 0 0 40" "What items do defenders get when they pickup a dropped hagar"
+set g_player_template_surv_defender_pickup_weapon_dropped_devastator "override 0 0 0 0 40" "What items do defenders get when they pickup a dropped devastator"
+set g_player_template_surv_defender_pickup_weapon_dropped_minelayer "override 0 0 0 0 40" "What items do defenders get when they pickup a dropped mine layer"
+set g_player_template_surv_defender_pickup_weapon_dropped_hlac "override 0 0 0 0 0 30" "What items do defenders get when they pickup a dropped HLAC"
+set g_player_template_surv_defender_pickup_weapon_dropped_rifle "override 0 0 0 80" "What items do defenders get when they pickup a dropped rifle"
+set g_player_template_surv_defender_pickup_weapon_dropped_seeker "override 0 0 0 0 40" "What items do defenders get when they pickup a dropped TAG seeker"
+
+set g_surv_attacker_damage_score 0.025 "How much score attackers gain per 1 point of damage"
+
+set g_player_template_surv_attacker_blaster_self_damage 0 "Whether attackers get damaged with their own blaster"
+
+set g_player_template_surv_defender_defense_scale 2 "How much defenders get damaged. Higher values mean less damage"
+set g_player_template_surv_defender_blaster_self_damage 0 "Whether defenders get damaged with their own blaster"
+
+set g_player_template_surv_cannon_fodder_blaster_self_damage 0 "Whether cannon fodder gets damaged with their own blaster"
+
+set g_surv_attacker_frag_score 10 "How much score attackers get for fragging defenders"
+
+set g_surv_defender_attacker_frag_health 0 "How much health do defenders get when they frag an attacker"
+set g_surv_defender_attacker_frag_armor 0 "How much armor do defenders get when they frag an attacker"
+set g_surv_defender_attacker_frag_shells 0 "How many shells do defenders get when they frag an attacker"
+set g_surv_defender_attacker_frag_bullets 0 "How many bullets do defenders get when they frag an attacker"
+set g_surv_defender_attacker_frag_rockets 0 "How many rockets do defenders get when they frag an attacker"
+set g_surv_defender_attacker_frag_cells 0 "How many cells do defenders get when they frag an attacker"
+set g_surv_defender_attacker_frag_plasma 0 "How much plasma do defenders get when they frag an attacker"
+set g_surv_defender_attacker_frag_fuel 0 "How much fuel do defenders get when they frag an attacker"
+set g_surv_defender_cannon_fodder_frag_health 0 "How much health do defenders get when they frag cannon fodder"
+set g_surv_defender_cannon_fodder_frag_armor 0 "How much armor do defenders get when they frag cannon fodder"
+set g_surv_defender_cannon_fodder_frag_shells 0 "How many shells do defenders get when they frag cannon fodder"
+set g_surv_defender_cannon_fodder_frag_bullets 0 "How many bullets do defenders get when they frag cannon fodder"
+set g_surv_defender_cannon_fodder_frag_rockets 0 "How many rockets do defenders get when they frag cannon fodder"
+set g_surv_defender_cannon_fodder_frag_cells 0 "How many cells do defenders get when they frag cannon fodder"
+set g_surv_defender_cannon_fodder_frag_plasma 0 "How much plasma do defenders get when they frag cannon fodder"
+set g_surv_defender_cannon_fodder_frag_fuel 0 "How much fuel do defenders get when they frag cannon fodder"