From: Mario Date: Sat, 13 Dec 2014 05:29:48 +0000 (+1100) Subject: Merge branch 'master' into terencehill/insta_notifications X-Git-Tag: xonotic-v0.8.0~113^2 X-Git-Url: http://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=commitdiff_plain;h=dc891780036706390bcaa27975dcb71e499deeae;hp=083132643679f9fa1c85b232929685461f4a15df Merge branch 'master' into terencehill/insta_notifications Conflicts: qcsrc/common/notifications.qh qcsrc/server/mutators/mutator_instagib.qc --- diff --git a/_hud_common.cfg b/_hud_common.cfg index 4a49997069..5aa1bbfeea 100644 --- a/_hud_common.cfg +++ b/_hud_common.cfg @@ -23,6 +23,7 @@ seta hud_colorset_background "7" "neutral/unimportant text" seta hud_panel_weapons_ammo_full_shells 60 "show 100% of the status bar at this ammo count" seta hud_panel_weapons_ammo_full_nails 320 "show 100% of the status bar at this ammo count" seta hud_panel_weapons_ammo_full_cells 180 "show 100% of the status bar at this ammo count" +seta hud_panel_weapons_ammo_full_plasma 180 "show 100% of the status bar at this ammo count" seta hud_panel_weapons_ammo_full_rockets 160 "show 100% of the status bar at this ammo count" seta hud_panel_weapons_ammo_full_fuel 100 "show 100% of the status bar at this ammo count" diff --git a/bal-wep-nexuiz25.cfg b/bal-wep-nexuiz25.cfg new file mode 100644 index 0000000000..b2427eccd0 --- /dev/null +++ b/bal-wep-nexuiz25.cfg @@ -0,0 +1,763 @@ +// {{{ #1: Blaster +set g_balance_blaster_primary_animtime 0.3 +set g_balance_blaster_primary_damage 35 +set g_balance_blaster_primary_delay 0 +set g_balance_blaster_primary_edgedamage 10 +set g_balance_blaster_primary_force 400 +set g_balance_blaster_primary_force_zscale 1 +set g_balance_blaster_primary_lifetime 30 +set g_balance_blaster_primary_radius 70 +set g_balance_blaster_primary_refire 0.7 +set g_balance_blaster_primary_shotangle 0 +set g_balance_blaster_primary_speed 9000 +set g_balance_blaster_primary_spread 0 +set g_balance_blaster_secondary 0 +set g_balance_blaster_secondary_animtime 0.3 +set g_balance_blaster_secondary_damage 35 +set g_balance_blaster_secondary_delay 0 +set g_balance_blaster_secondary_edgedamage 10 +set g_balance_blaster_secondary_force 400 +set g_balance_blaster_secondary_force_zscale 1 +set g_balance_blaster_secondary_lifetime 30 +set g_balance_blaster_secondary_radius 70 +set g_balance_blaster_secondary_refire 0.7 +set g_balance_blaster_secondary_shotangle 0 +set g_balance_blaster_secondary_speed 9000 +set g_balance_blaster_secondary_spread 0 +set g_balance_blaster_switchdelay_drop 0.15 +set g_balance_blaster_switchdelay_raise 0.15 +set g_balance_blaster_weaponreplace "" +set g_balance_blaster_weaponstart 1 +set g_balance_blaster_weaponstartoverride -1 +set g_balance_blaster_weaponthrowable 0 +// }}} +// {{{ #2: Shotgun +set g_balance_shotgun_primary_ammo 1 +set g_balance_shotgun_primary_animtime 0.2 +set g_balance_shotgun_primary_bullets 6 +set g_balance_shotgun_primary_damage 9 +set g_balance_shotgun_primary_force 60 +set g_balance_shotgun_primary_refire 0.5 +set g_balance_shotgun_primary_solidpenetration 3.8 +set g_balance_shotgun_primary_spread 0.07 +set g_balance_shotgun_reload_ammo 0 +set g_balance_shotgun_reload_time 2 +set g_balance_shotgun_secondary 2 +set g_balance_shotgun_secondary_animtime 1 +set g_balance_shotgun_secondary_damage 80 +set g_balance_shotgun_secondary_force 200 +set g_balance_shotgun_secondary_melee_delay 0.25 +set g_balance_shotgun_secondary_melee_multihit 1 +set g_balance_shotgun_secondary_melee_no_doubleslap 1 +set g_balance_shotgun_secondary_melee_nonplayerdamage 40 +set g_balance_shotgun_secondary_melee_range 120 +set g_balance_shotgun_secondary_melee_swing_side 120 +set g_balance_shotgun_secondary_melee_swing_up 30 +set g_balance_shotgun_secondary_melee_time 0.15 +set g_balance_shotgun_secondary_melee_traces 10 +set g_balance_shotgun_secondary_refire 1.25 +set g_balance_shotgun_secondary_alt_animtime 0.2 +set g_balance_shotgun_secondary_alt_refire 1.2 +set g_balance_shotgun_switchdelay_drop 0.15 +set g_balance_shotgun_switchdelay_raise 0.15 +set g_balance_shotgun_weaponreplace "" +set g_balance_shotgun_weaponstart 1 +set g_balance_shotgun_weaponstartoverride -1 +set g_balance_shotgun_weaponthrowable 1 +// }}} +// {{{ #3: Machine Gun +set g_balance_machinegun_burst 0 +set g_balance_machinegun_burst_ammo 3 +set g_balance_machinegun_burst_animtime 0.75 +set g_balance_machinegun_burst_refire 0.05 +set g_balance_machinegun_burst_refire2 0.75 +set g_balance_machinegun_burst_speed 0 +set g_balance_machinegun_first 1 +set g_balance_machinegun_first_ammo 1 +set g_balance_machinegun_first_damage 30 +set g_balance_machinegun_first_force 50 +set g_balance_machinegun_first_refire 0.2 +set g_balance_machinegun_first_spread 0.015 +set g_balance_machinegun_mode 0 +set g_balance_machinegun_reload_ammo 0 +set g_balance_machinegun_reload_time 2 +set g_balance_machinegun_solidpenetration 13.1 +set g_balance_machinegun_spread_add 0.02 +set g_balance_machinegun_spread_max 0.6 +set g_balance_machinegun_spread_min 0.02 +set g_balance_machinegun_sustained_ammo 1 +set g_balance_machinegun_sustained_damage 15 +set g_balance_machinegun_sustained_force 27 +set g_balance_machinegun_sustained_refire 0.1 +set g_balance_machinegun_sustained_spread 0.05 +set g_balance_machinegun_switchdelay_drop 0.15 +set g_balance_machinegun_switchdelay_raise 0.15 +set g_balance_machinegun_weaponreplace "" +set g_balance_machinegun_weaponstart 0 +set g_balance_machinegun_weaponstartoverride -1 +set g_balance_machinegun_weaponthrowable 1 +// }}} +// {{{ #4: Mortar +set g_balance_mortar_bouncefactor 0.5 +set g_balance_mortar_bouncestop 0.075 +set g_balance_mortar_primary_ammo 2 +set g_balance_mortar_primary_animtime 0.3 +set g_balance_mortar_primary_damage 70 +set g_balance_mortar_primary_damageforcescale 4 +set g_balance_mortar_primary_edgedamage 25 +set g_balance_mortar_primary_force 400 +set g_balance_mortar_primary_health 25 +set g_balance_mortar_primary_lifetime 30 +set g_balance_mortar_primary_lifetime_stick 1 +set g_balance_mortar_primary_radius 140 +set g_balance_mortar_primary_refire 0.8 +set g_balance_mortar_primary_remote_minbouncecnt 0 +set g_balance_mortar_primary_speed 2000 +set g_balance_mortar_primary_speed_up 200 +set g_balance_mortar_primary_speed_z 0 +set g_balance_mortar_primary_spread 0 +set g_balance_mortar_primary_type 0 +set g_balance_mortar_reload_ammo 0 +set g_balance_mortar_reload_time 2 +set g_balance_mortar_secondary_ammo 2 +set g_balance_mortar_secondary_animtime 0.3 +set g_balance_mortar_secondary_damage 70 +set g_balance_mortar_secondary_damageforcescale 4 +set g_balance_mortar_secondary_edgedamage 38 +set g_balance_mortar_secondary_force 400 +set g_balance_mortar_secondary_health 10 +set g_balance_mortar_secondary_lifetime 2.5 +set g_balance_mortar_secondary_lifetime_bounce 0 +set g_balance_mortar_secondary_lifetime_stick 0 +set g_balance_mortar_secondary_radius 140 +set g_balance_mortar_secondary_refire 0.7 +set g_balance_mortar_secondary_remote_detonateprimary 0 +set g_balance_mortar_secondary_speed 1400 +set g_balance_mortar_secondary_speed_up 200 +set g_balance_mortar_secondary_speed_z 0 +set g_balance_mortar_secondary_spread 0 +set g_balance_mortar_secondary_type 1 +set g_balance_mortar_switchdelay_drop 0.15 +set g_balance_mortar_switchdelay_raise 0.15 +set g_balance_mortar_weaponreplace "" +set g_balance_mortar_weaponstart 0 +set g_balance_mortar_weaponstartoverride -1 +set g_balance_mortar_weaponthrowable 1 +// }}} +// {{{ #5: Mine Layer (MUTATOR WEAPON) +set g_balance_minelayer_ammo 4 +set g_balance_minelayer_animtime 0.4 +set g_balance_minelayer_damage 40 +set g_balance_minelayer_damageforcescale 0 +set g_balance_minelayer_detonatedelay -1 +set g_balance_minelayer_edgedamage 20 +set g_balance_minelayer_force 250 +set g_balance_minelayer_health 15 +set g_balance_minelayer_lifetime 10 +set g_balance_minelayer_lifetime_countdown 0.5 +set g_balance_minelayer_limit 3 +set g_balance_minelayer_protection 0 +set g_balance_minelayer_proximityradius 150 +set g_balance_minelayer_radius 175 +set g_balance_minelayer_refire 1.5 +set g_balance_minelayer_reload_ammo 0 +set g_balance_minelayer_reload_time 2 +set g_balance_minelayer_remote_damage 45 +set g_balance_minelayer_remote_edgedamage 40 +set g_balance_minelayer_remote_force 300 +set g_balance_minelayer_remote_radius 200 +set g_balance_minelayer_speed 1000 +set g_balance_minelayer_switchdelay_drop 0.15 +set g_balance_minelayer_switchdelay_raise 0.15 +set g_balance_minelayer_time 0.5 +set g_balance_minelayer_weaponreplace "" +set g_balance_minelayer_weaponstart 0 +set g_balance_minelayer_weaponstartoverride -1 +set g_balance_minelayer_weaponthrowable 1 +// }}} +// {{{ #6: Electro +set g_balance_electro_combo_comboradius 0 +set g_balance_electro_combo_comboradius_thruwall 0 +set g_balance_electro_combo_damage 80 +set g_balance_electro_combo_edgedamage 0 +set g_balance_electro_combo_force 200 +set g_balance_electro_combo_radius 250 +set g_balance_electro_combo_safeammocheck 0 +set g_balance_electro_combo_speed 2000 +set g_balance_electro_primary_ammo 2 +set g_balance_electro_primary_animtime 0.3 +set g_balance_electro_primary_comboradius 0 +set g_balance_electro_primary_damage 65 +set g_balance_electro_primary_edgedamage 0 +set g_balance_electro_primary_force 200 +set g_balance_electro_primary_lifetime 30 +set g_balance_electro_primary_midaircombo_explode 0 +set g_balance_electro_primary_midaircombo_interval 0 +set g_balance_electro_primary_midaircombo_radius 0 +set g_balance_electro_primary_radius 150 +set g_balance_electro_primary_refire 0.6 +set g_balance_electro_primary_speed 2000 +set g_balance_electro_primary_spread 0 +set g_balance_electro_reload_ammo 0 +set g_balance_electro_reload_time 2 +set g_balance_electro_secondary_ammo 2 +set g_balance_electro_secondary_animtime 0.3 +set g_balance_electro_secondary_bouncefactor 0.5 +set g_balance_electro_secondary_bouncestop 0.075 +set g_balance_electro_secondary_count 1 +set g_balance_electro_secondary_damage 50 +set g_balance_electro_secondary_damagedbycontents 0 +set g_balance_electro_secondary_damageforcescale 4 +set g_balance_electro_secondary_edgedamage 0 +set g_balance_electro_secondary_force 200 +set g_balance_electro_secondary_health 5 +set g_balance_electro_secondary_lifetime 5 +set g_balance_electro_secondary_radius 150 +set g_balance_electro_secondary_refire 0.3 +set g_balance_electro_secondary_refire2 0 +set g_balance_electro_secondary_speed 900 +set g_balance_electro_secondary_speed_up 200 +set g_balance_electro_secondary_speed_z 0 +set g_balance_electro_secondary_spread 0.04 +set g_balance_electro_secondary_touchexplode 0 +set g_balance_electro_switchdelay_drop 0.15 +set g_balance_electro_switchdelay_raise 0.15 +set g_balance_electro_weaponreplace "" +set g_balance_electro_weaponstart 0 +set g_balance_electro_weaponstartoverride -1 +set g_balance_electro_weaponthrowable 1 +// }}} +// {{{ #7: Crylink +set g_balance_crylink_primary_ammo 2 +set g_balance_crylink_primary_animtime 0.3 +set g_balance_crylink_primary_bouncedamagefactor 0.5 +set g_balance_crylink_primary_bounces 1 +set g_balance_crylink_primary_damage 18 +set g_balance_crylink_primary_edgedamage 0 +set g_balance_crylink_primary_force -55 +set g_balance_crylink_primary_joindelay 0 +set g_balance_crylink_primary_joinexplode 0 +set g_balance_crylink_primary_joinexplode_damage 0 +set g_balance_crylink_primary_joinexplode_edgedamage 0 +set g_balance_crylink_primary_joinexplode_force 0 +set g_balance_crylink_primary_joinexplode_radius 0 +set g_balance_crylink_primary_joinspread 0 +set g_balance_crylink_primary_linkexplode 1 +set g_balance_crylink_primary_middle_fadetime 5 +set g_balance_crylink_primary_middle_lifetime 5 +set g_balance_crylink_primary_other_fadetime 0.25 +set g_balance_crylink_primary_other_lifetime 0.1 +set g_balance_crylink_primary_radius 80 +set g_balance_crylink_primary_refire 0.4 +set g_balance_crylink_primary_shots 4 +set g_balance_crylink_primary_speed 7000 +set g_balance_crylink_primary_spread 0.03 +set g_balance_crylink_reload_ammo 0 +set g_balance_crylink_reload_time 2 +set g_balance_crylink_secondary 1 +set g_balance_crylink_secondary_ammo 2 +set g_balance_crylink_secondary_animtime 0.2 +set g_balance_crylink_secondary_bouncedamagefactor 0.5 +set g_balance_crylink_secondary_bounces 0 +set g_balance_crylink_secondary_damage 18 +set g_balance_crylink_secondary_edgedamage 5 +set g_balance_crylink_secondary_force -55 +set g_balance_crylink_secondary_joindelay 0 +set g_balance_crylink_secondary_joinexplode 0 +set g_balance_crylink_secondary_joinexplode_damage 0 +set g_balance_crylink_secondary_joinexplode_edgedamage 0 +set g_balance_crylink_secondary_joinexplode_force 0 +set g_balance_crylink_secondary_joinexplode_radius 0 +set g_balance_crylink_secondary_joinspread 0 +set g_balance_crylink_secondary_linkexplode 1 +set g_balance_crylink_secondary_middle_fadetime 5 +set g_balance_crylink_secondary_middle_lifetime 5 +set g_balance_crylink_secondary_other_fadetime 2 +set g_balance_crylink_secondary_other_lifetime 2 +set g_balance_crylink_secondary_radius 100 +set g_balance_crylink_secondary_refire 0.5 +set g_balance_crylink_secondary_shots 7 +set g_balance_crylink_secondary_speed 7000 +set g_balance_crylink_secondary_spread 0.08 +set g_balance_crylink_secondary_spreadtype 0 +set g_balance_crylink_switchdelay_drop 0.15 +set g_balance_crylink_switchdelay_raise 0.15 +set g_balance_crylink_weaponreplace "" +set g_balance_crylink_weaponstart 0 +set g_balance_crylink_weaponstartoverride -1 +set g_balance_crylink_weaponthrowable 1 +// }}} +// {{{ #8: Vortex +set g_balance_vortex_charge 0 +set g_balance_vortex_charge_animlimit 0.5 +set g_balance_vortex_charge_limit 1 +set g_balance_vortex_charge_maxspeed 800 +set g_balance_vortex_charge_mindmg 40 +set g_balance_vortex_charge_minspeed 400 +set g_balance_vortex_charge_rate 0.4 +set g_balance_vortex_charge_rot_pause 0 +set g_balance_vortex_charge_rot_rate 0 +set g_balance_vortex_charge_shot_multiplier 0 +set g_balance_vortex_charge_start 0.5 +set g_balance_vortex_charge_velocity_rate 0 +set g_balance_vortex_primary_ammo 5 +set g_balance_vortex_primary_animtime 0.3 +set g_balance_vortex_primary_damage 100 +set g_balance_vortex_primary_damagefalloff_forcehalflife 0 +set g_balance_vortex_primary_damagefalloff_halflife 0 +set g_balance_vortex_primary_damagefalloff_maxdist 0 +set g_balance_vortex_primary_damagefalloff_mindist 0 +set g_balance_vortex_primary_force 600 +set g_balance_vortex_primary_refire 1.5 +set g_balance_vortex_reload_ammo 0 +set g_balance_vortex_reload_time 2 +set g_balance_vortex_secondary 0 +set g_balance_vortex_secondary_ammo 2 +set g_balance_vortex_secondary_animtime 0 +set g_balance_vortex_secondary_chargepool 0 +set g_balance_vortex_secondary_chargepool_pause_regen 1 +set g_balance_vortex_secondary_chargepool_regen 0.15 +set g_balance_vortex_secondary_damage 0 +set g_balance_vortex_secondary_damagefalloff_forcehalflife 0 +set g_balance_vortex_secondary_damagefalloff_halflife 0 +set g_balance_vortex_secondary_damagefalloff_maxdist 0 +set g_balance_vortex_secondary_damagefalloff_mindist 0 +set g_balance_vortex_secondary_force 0 +set g_balance_vortex_secondary_refire 0 +set g_balance_vortex_switchdelay_drop 0.15 +set g_balance_vortex_switchdelay_raise 0.15 +set g_balance_vortex_weaponreplace "" +set g_balance_vortex_weaponstart 0 +set g_balance_vortex_weaponstartoverride -1 +set g_balance_vortex_weaponthrowable 1 +// }}} +// {{{ #9: Hagar +set g_balance_hagar_primary_ammo 1 +set g_balance_hagar_primary_damage 37 +set g_balance_hagar_primary_damageforcescale 0 +set g_balance_hagar_primary_edgedamage 15 +set g_balance_hagar_primary_force 100 +set g_balance_hagar_primary_health 0 +set g_balance_hagar_primary_lifetime 30 +set g_balance_hagar_primary_radius 65 +set g_balance_hagar_primary_refire 0.15 +set g_balance_hagar_primary_speed 3000 +set g_balance_hagar_primary_spread 0.010 +set g_balance_hagar_reload_ammo 0 +set g_balance_hagar_reload_time 2 +set g_balance_hagar_secondary 1 +set g_balance_hagar_secondary_ammo 1 +set g_balance_hagar_secondary_damage 37 +set g_balance_hagar_secondary_damageforcescale 0 +set g_balance_hagar_secondary_edgedamage 15 +set g_balance_hagar_secondary_force 100 +set g_balance_hagar_secondary_health 0 +set g_balance_hagar_secondary_lifetime_min 30 +set g_balance_hagar_secondary_lifetime_rand 0 +set g_balance_hagar_secondary_load 0 +set g_balance_hagar_secondary_load_abort 1 +set g_balance_hagar_secondary_load_animtime 0.2 +set g_balance_hagar_secondary_load_hold 4 +set g_balance_hagar_secondary_load_linkexplode 0 +set g_balance_hagar_secondary_load_max 4 +set g_balance_hagar_secondary_load_releasedeath 0 +set g_balance_hagar_secondary_load_speed 0.5 +set g_balance_hagar_secondary_load_spread 0.075 +set g_balance_hagar_secondary_load_spread_bias 0.5 +set g_balance_hagar_secondary_radius 80 +set g_balance_hagar_secondary_refire 0.15 +set g_balance_hagar_secondary_speed 1400 +set g_balance_hagar_secondary_spread 0.015 +set g_balance_hagar_switchdelay_drop 0.15 +set g_balance_hagar_switchdelay_raise 0.15 +set g_balance_hagar_weaponreplace "" +set g_balance_hagar_weaponstart 0 +set g_balance_hagar_weaponstartoverride -1 +set g_balance_hagar_weaponthrowable 1 +// }}} +// {{{ #10: Devastator +set g_balance_devastator_ammo 3 +set g_balance_devastator_animtime 0.3 +set g_balance_devastator_damage 105 +set g_balance_devastator_damageforcescale 4 +set g_balance_devastator_detonatedelay 0.2 +set g_balance_devastator_edgedamage 40 +set g_balance_devastator_force 600 +set g_balance_devastator_guidedelay 0.15 +set g_balance_devastator_guidegoal 512 +set g_balance_devastator_guiderate 90 +set g_balance_devastator_guideratedelay 0.01 +set g_balance_devastator_guidestop 0 +set g_balance_devastator_health 40 +set g_balance_devastator_lifetime 30 +set g_balance_devastator_radius 150 +set g_balance_devastator_refire 1 +set g_balance_devastator_reload_ammo 0 +set g_balance_devastator_reload_time 2 +set g_balance_devastator_remote_damage 105 +set g_balance_devastator_remote_edgedamage 40 +set g_balance_devastator_remote_force 600 +set g_balance_devastator_remote_jump_damage 70 +set g_balance_devastator_remote_jump_radius 0 +set g_balance_devastator_remote_jump_velocity_z_add 400 +set g_balance_devastator_remote_jump_velocity_z_max 1500 +set g_balance_devastator_remote_jump_velocity_z_min 400 +set g_balance_devastator_remote_radius 150 +set g_balance_devastator_speed 850 +set g_balance_devastator_speedaccel 0 +set g_balance_devastator_speedstart 850 +set g_balance_devastator_switchdelay_drop 0.15 +set g_balance_devastator_switchdelay_raise 0.15 +set g_balance_devastator_weaponreplace "" +set g_balance_devastator_weaponstart 0 +set g_balance_devastator_weaponstartoverride -1 +set g_balance_devastator_weaponthrowable 1 +// }}} +// {{{ #11: Port-O-Launch +set g_balance_porto_primary_animtime 0.3 +set g_balance_porto_primary_lifetime 30 +set g_balance_porto_primary_refire 1.5 +set g_balance_porto_primary_speed 2000 +set g_balance_porto_secondary 1 +set g_balance_porto_secondary_animtime 0.3 +set g_balance_porto_secondary_lifetime 30 +set g_balance_porto_secondary_refire 1.5 +set g_balance_porto_secondary_speed 2000 +set g_balance_porto_switchdelay_drop 0.15 +set g_balance_porto_switchdelay_raise 0.15 +set g_balance_porto_weaponreplace "" +set g_balance_porto_weaponstart 0 +set g_balance_porto_weaponstartoverride -1 +set g_balance_porto_weaponthrowable 1 +// }}} +// {{{ #12: Vaporizer +set g_balance_vaporizer_primary_ammo 10 +set g_balance_vaporizer_primary_animtime 0.3 +set g_balance_vaporizer_primary_refire 1 +set g_balance_vaporizer_reload_ammo 0 +set g_balance_vaporizer_reload_time 0 +set g_balance_vaporizer_secondary_ammo 0 +set g_balance_vaporizer_secondary_animtime 0.2 +set g_balance_vaporizer_secondary_damage 25 +set g_balance_vaporizer_secondary_delay 0 +set g_balance_vaporizer_secondary_edgedamage 12.5 +set g_balance_vaporizer_secondary_force 400 +set g_balance_vaporizer_secondary_lifetime 5 +set g_balance_vaporizer_secondary_radius 70 +set g_balance_vaporizer_secondary_refire 0.7 +set g_balance_vaporizer_secondary_shotangle 0 +set g_balance_vaporizer_secondary_speed 6000 +set g_balance_vaporizer_secondary_spread 0 +set g_balance_vaporizer_switchdelay_drop 0.15 +set g_balance_vaporizer_switchdelay_raise 0.15 +set g_balance_vaporizer_weaponreplace "" +set g_balance_vaporizer_weaponstart 0 +set g_balance_vaporizer_weaponstartoverride -1 +set g_balance_vaporizer_weaponthrowable 0 +// }}} +// {{{ #13: Grappling Hook +set g_balance_hook_primary_ammo 5 +set g_balance_hook_primary_animtime 0.3 +set g_balance_hook_primary_hooked_ammo 5 +set g_balance_hook_primary_hooked_time_free 2 +set g_balance_hook_primary_hooked_time_max 0 +set g_balance_hook_primary_refire 0.2 +set g_balance_hook_secondary_animtime 0.3 +set g_balance_hook_secondary_damage 25 +set g_balance_hook_secondary_damageforcescale 0 +set g_balance_hook_secondary_duration 1.5 +set g_balance_hook_secondary_edgedamage 5 +set g_balance_hook_secondary_force -2000 +set g_balance_hook_secondary_gravity 5 +set g_balance_hook_secondary_health 15 +set g_balance_hook_secondary_lifetime 5 +set g_balance_hook_secondary_power 3 +set g_balance_hook_secondary_radius 500 +set g_balance_hook_secondary_refire 3 +set g_balance_hook_secondary_speed 0 +set g_balance_hook_switchdelay_drop 0.15 +set g_balance_hook_switchdelay_raise 0.15 +set g_balance_hook_weaponreplace "" +set g_balance_hook_weaponstart 0 +set g_balance_hook_weaponstartoverride -1 +set g_balance_hook_weaponthrowable 1 +// }}} +// {{{ #14: Heavy Laser Assault Cannon (MUTATOR WEAPON) +set g_balance_hlac_primary_ammo 1 +set g_balance_hlac_primary_animtime 0.4 +set g_balance_hlac_primary_damage 18 +set g_balance_hlac_primary_edgedamage 9 +set g_balance_hlac_primary_force 90 +set g_balance_hlac_primary_lifetime 5 +set g_balance_hlac_primary_radius 70 +set g_balance_hlac_primary_refire 0.15 +set g_balance_hlac_primary_speed 9000 +set g_balance_hlac_primary_spread_add 0.0045 +set g_balance_hlac_primary_spread_crouchmod 0.25 +set g_balance_hlac_primary_spread_max 0.25 +set g_balance_hlac_primary_spread_min 0.01 +set g_balance_hlac_reload_ammo 0 +set g_balance_hlac_reload_time 2 +set g_balance_hlac_secondary 1 +set g_balance_hlac_secondary_ammo 10 +set g_balance_hlac_secondary_animtime 0.3 +set g_balance_hlac_secondary_damage 15 +set g_balance_hlac_secondary_edgedamage 7.5 +set g_balance_hlac_secondary_force 90 +set g_balance_hlac_secondary_lifetime 5 +set g_balance_hlac_secondary_radius 70 +set g_balance_hlac_secondary_refire 1 +set g_balance_hlac_secondary_shots 6 +set g_balance_hlac_secondary_speed 9000 +set g_balance_hlac_secondary_spread 0.15 +set g_balance_hlac_secondary_spread_crouchmod 0.5 +set g_balance_hlac_switchdelay_drop 0.15 +set g_balance_hlac_switchdelay_raise 0.15 +set g_balance_hlac_weaponreplace "" +set g_balance_hlac_weaponstart 0 +set g_balance_hlac_weaponstartoverride -1 +set g_balance_hlac_weaponthrowable 1 +// }}} +// {{{ #15: @!#%'n Tuba +set g_balance_tuba_animtime 0.05 +set g_balance_tuba_attenuation 0.5 +set g_balance_tuba_damage 5 +set g_balance_tuba_edgedamage 0 +set g_balance_tuba_fadetime 0.25 +set g_balance_tuba_force 40 +set g_balance_tuba_pitchstep 6 +set g_balance_tuba_radius 200 +set g_balance_tuba_refire 0.05 +set g_balance_tuba_switchdelay_drop 0.15 +set g_balance_tuba_switchdelay_raise 0.15 +set g_balance_tuba_volume 1 +set g_balance_tuba_weaponreplace "" +set g_balance_tuba_weaponstart 0 +set g_balance_tuba_weaponstartoverride -1 +set g_balance_tuba_weaponthrowable 1 +// }}} +// {{{ #16: Rifle (MUTATOR WEAPON) +set g_balance_rifle_bursttime 0 +set g_balance_rifle_primary_ammo 10 +set g_balance_rifle_primary_animtime 0.4 +set g_balance_rifle_primary_bullethail 0 +set g_balance_rifle_primary_burstcost 0 +set g_balance_rifle_primary_damage 80 +set g_balance_rifle_primary_force 100 +set g_balance_rifle_primary_refire 1.2 +set g_balance_rifle_primary_shots 1 +set g_balance_rifle_primary_solidpenetration 62.2 +set g_balance_rifle_primary_spread 0 +set g_balance_rifle_primary_tracer 1 +set g_balance_rifle_reload_ammo 80 +set g_balance_rifle_reload_time 2 +set g_balance_rifle_secondary 1 +set g_balance_rifle_secondary_ammo 10 +set g_balance_rifle_secondary_animtime 0.3 +set g_balance_rifle_secondary_bullethail 0 +set g_balance_rifle_secondary_burstcost 0 +set g_balance_rifle_secondary_damage 20 +set g_balance_rifle_secondary_force 50 +set g_balance_rifle_secondary_refire 0.9 +set g_balance_rifle_secondary_reload 0 +set g_balance_rifle_secondary_shots 4 +set g_balance_rifle_secondary_solidpenetration 15.5 +set g_balance_rifle_secondary_spread 0.04 +set g_balance_rifle_secondary_tracer 0 +set g_balance_rifle_switchdelay_drop 0.15 +set g_balance_rifle_switchdelay_raise 0.15 +set g_balance_rifle_weaponreplace "" +set g_balance_rifle_weaponstart 0 +set g_balance_rifle_weaponstartoverride -1 +set g_balance_rifle_weaponthrowable 1 +// }}} +// {{{ #17: Fireball +set g_balance_fireball_primary_animtime 0.4 +set g_balance_fireball_primary_bfgdamage 100 +set g_balance_fireball_primary_bfgforce 0 +set g_balance_fireball_primary_bfgradius 1000 +set g_balance_fireball_primary_damage 200 +set g_balance_fireball_primary_damageforcescale 0 +set g_balance_fireball_primary_edgedamage 50 +set g_balance_fireball_primary_force 600 +set g_balance_fireball_primary_health 0 +set g_balance_fireball_primary_laserburntime 0.5 +set g_balance_fireball_primary_laserdamage 80 +set g_balance_fireball_primary_laseredgedamage 20 +set g_balance_fireball_primary_laserradius 256 +set g_balance_fireball_primary_lifetime 15 +set g_balance_fireball_primary_radius 200 +set g_balance_fireball_primary_refire 2 +set g_balance_fireball_primary_refire2 0 +set g_balance_fireball_primary_speed 1200 +set g_balance_fireball_primary_spread 0 +set g_balance_fireball_secondary_animtime 0.3 +set g_balance_fireball_secondary_damage 40 +set g_balance_fireball_secondary_damageforcescale 4 +set g_balance_fireball_secondary_damagetime 5 +set g_balance_fireball_secondary_laserburntime 0.5 +set g_balance_fireball_secondary_laserdamage 50 +set g_balance_fireball_secondary_laseredgedamage 20 +set g_balance_fireball_secondary_laserradius 110 +set g_balance_fireball_secondary_lifetime 7 +set g_balance_fireball_secondary_refire 1.5 +set g_balance_fireball_secondary_speed 900 +set g_balance_fireball_secondary_speed_up 100 +set g_balance_fireball_secondary_speed_z 0 +set g_balance_fireball_secondary_spread 0 +set g_balance_fireball_switchdelay_drop 0.15 +set g_balance_fireball_switchdelay_raise 0.15 +set g_balance_fireball_weaponreplace "" +set g_balance_fireball_weaponstart 0 +set g_balance_fireball_weaponstartoverride -1 +set g_balance_fireball_weaponthrowable 0 +// }}} +// {{{ #18: T.A.G. Seeker (MUTATOR WEAPON) +set g_balance_seeker_flac_ammo 1 +set g_balance_seeker_flac_animtime 0.1 +set g_balance_seeker_flac_damage 15 +set g_balance_seeker_flac_edgedamage 10 +set g_balance_seeker_flac_force 50 +set g_balance_seeker_flac_lifetime 0.1 +set g_balance_seeker_flac_lifetime_rand 0.05 +set g_balance_seeker_flac_radius 100 +set g_balance_seeker_flac_refire 0.1 +set g_balance_seeker_flac_speed 3000 +set g_balance_seeker_flac_speed_up 1000 +set g_balance_seeker_flac_speed_z 0 +set g_balance_seeker_flac_spread 0.4 +set g_balance_seeker_missile_accel 1400 +set g_balance_seeker_missile_ammo 2 +set g_balance_seeker_missile_animtime 0.2 +set g_balance_seeker_missile_count 3 +set g_balance_seeker_missile_damage 30 +set g_balance_seeker_missile_damageforcescale 4 +set g_balance_seeker_missile_decel 1400 +set g_balance_seeker_missile_delay 0.25 +set g_balance_seeker_missile_edgedamage 10 +set g_balance_seeker_missile_force 150 +set g_balance_seeker_missile_health 5 +set g_balance_seeker_missile_lifetime 15 +set g_balance_seeker_missile_proxy 0 +set g_balance_seeker_missile_proxy_delay 0.2 +set g_balance_seeker_missile_proxy_maxrange 45 +set g_balance_seeker_missile_radius 80 +set g_balance_seeker_missile_refire 0.5 +set g_balance_seeker_missile_smart 1 +set g_balance_seeker_missile_smart_mindist 800 +set g_balance_seeker_missile_smart_trace_max 2500 +set g_balance_seeker_missile_smart_trace_min 1000 +set g_balance_seeker_missile_speed 700 +set g_balance_seeker_missile_speed_max 1300 +set g_balance_seeker_missile_speed_up 300 +set g_balance_seeker_missile_speed_z 0 +set g_balance_seeker_missile_spread 0 +set g_balance_seeker_missile_turnrate 0.65 +set g_balance_seeker_reload_ammo 0 +set g_balance_seeker_reload_time 2 +set g_balance_seeker_switchdelay_drop 0.15 +set g_balance_seeker_switchdelay_raise 0.15 +set g_balance_seeker_tag_ammo 1 +set g_balance_seeker_tag_animtime 0.2 +set g_balance_seeker_tag_damageforcescale 4 +set g_balance_seeker_tag_health 5 +set g_balance_seeker_tag_lifetime 15 +set g_balance_seeker_tag_refire 0.75 +set g_balance_seeker_tag_speed 5000 +set g_balance_seeker_tag_spread 0 +set g_balance_seeker_tag_tracker_lifetime 10 +set g_balance_seeker_type 0 +set g_balance_seeker_weaponreplace "" +set g_balance_seeker_weaponstart 0 +set g_balance_seeker_weaponstartoverride -1 +set g_balance_seeker_weaponthrowable 1 +// }}} +// {{{ #19: Shockwave (MUTATOR WEAPON) +set g_balance_shockwave_blast_animtime 0.3 +set g_balance_shockwave_blast_damage 20 +set g_balance_shockwave_blast_distance 1000 +set g_balance_shockwave_blast_edgedamage 0 +set g_balance_shockwave_blast_force 200 +set g_balance_shockwave_blast_force_forwardbias 50 +set g_balance_shockwave_blast_force_zscale 2 +set g_balance_shockwave_blast_jump_damage 20 +set g_balance_shockwave_blast_jump_edgedamage 0 +set g_balance_shockwave_blast_jump_force 300 +set g_balance_shockwave_blast_jump_force_velocitybias 0 +set g_balance_shockwave_blast_jump_force_zscale 1.25 +set g_balance_shockwave_blast_jump_multiplier_accuracy 0.5 +set g_balance_shockwave_blast_jump_multiplier_distance 0.5 +set g_balance_shockwave_blast_jump_multiplier_min 0 +set g_balance_shockwave_blast_jump_radius 150 +set g_balance_shockwave_blast_multiplier_accuracy 0.5 +set g_balance_shockwave_blast_multiplier_distance 0.5 +set g_balance_shockwave_blast_multiplier_min 0 +set g_balance_shockwave_blast_refire 0.75 +set g_balance_shockwave_blast_splash_damage 15 +set g_balance_shockwave_blast_splash_edgedamage 0 +set g_balance_shockwave_blast_splash_force 100 +set g_balance_shockwave_blast_splash_force_forwardbias 50 +set g_balance_shockwave_blast_splash_multiplier_accuracy 0.5 +set g_balance_shockwave_blast_splash_multiplier_distance 0.5 +set g_balance_shockwave_blast_splash_multiplier_min 0 +set g_balance_shockwave_blast_splash_radius 70 +set g_balance_shockwave_blast_spread_max 120 +set g_balance_shockwave_blast_spread_min 25 +set g_balance_shockwave_melee_animtime 1.3 +set g_balance_shockwave_melee_damage 80 +set g_balance_shockwave_melee_delay 0.25 +set g_balance_shockwave_melee_force 200 +set g_balance_shockwave_melee_multihit 1 +set g_balance_shockwave_melee_no_doubleslap 1 +set g_balance_shockwave_melee_nonplayerdamage 40 +set g_balance_shockwave_melee_range 120 +set g_balance_shockwave_melee_refire 1.25 +set g_balance_shockwave_melee_swing_side 120 +set g_balance_shockwave_melee_swing_up 30 +set g_balance_shockwave_melee_time 0.15 +set g_balance_shockwave_melee_traces 10 +set g_balance_shockwave_switchdelay_drop 0.15 +set g_balance_shockwave_switchdelay_raise 0.15 +set g_balance_shockwave_weaponreplace "" +set g_balance_shockwave_weaponstart 1 +set g_balance_shockwave_weaponstartoverride -1 +set g_balance_shockwave_weaponthrowable 0 +// }}} +// {{{ #20: Arc +set g_balance_arc_beam_ammo 4 +set g_balance_arc_beam_animtime 0.2 +set g_balance_arc_beam_botaimlifetime 0 +set g_balance_arc_beam_botaimspeed 0 +set g_balance_arc_beam_damage 115 +set g_balance_arc_beam_degreespersegment 1 +set g_balance_arc_beam_distancepersegment 0 +set g_balance_arc_beam_falloff_halflifedist 0 +set g_balance_arc_beam_falloff_maxdist 0 +set g_balance_arc_beam_falloff_mindist 0 +set g_balance_arc_beam_force 900 +set g_balance_arc_beam_healing_amax 100 +set g_balance_arc_beam_healing_aps 50 +set g_balance_arc_beam_healing_hmax 150 +set g_balance_arc_beam_healing_hps 50 +set g_balance_arc_cooldown 2.5 +set g_balance_arc_overheat_max 5 +set g_balance_arc_overheat_min 3 +set g_balance_arc_beam_heat 1 +set g_balance_arc_burst_heat 3 +set g_balance_arc_beam_maxangle 10 +set g_balance_arc_beam_nonplayerdamage 80 +set g_balance_arc_beam_range 1000 +set g_balance_arc_beam_refire 0.5 +set g_balance_arc_beam_returnspeed 8 +set g_balance_arc_beam_tightness 0.5 +set g_balance_arc_burst_ammo 15 +set g_balance_arc_burst_damage 150 +set g_balance_arc_burst_healing_aps 100 +set g_balance_arc_burst_healing_hps 100 +set g_balance_arc_switchdelay_drop 0.3 +set g_balance_arc_switchdelay_raise 0.3 +set g_balance_arc_weaponreplace "" +set g_balance_arc_weaponstart 0 +set g_balance_arc_weaponstartoverride -1 +set g_balance_arc_weaponthrowable 1 +// }}} diff --git a/bal-wep-samual.cfg b/bal-wep-samual.cfg new file mode 100644 index 0000000000..2ec59d610c --- /dev/null +++ b/bal-wep-samual.cfg @@ -0,0 +1,761 @@ +// {{{ #1: Blaster +set g_balance_blaster_primary_animtime 0.2 +set g_balance_blaster_primary_damage 25 +set g_balance_blaster_primary_delay 0 +set g_balance_blaster_primary_edgedamage 12.5 +set g_balance_blaster_primary_force 300 +set g_balance_blaster_primary_lifetime 5 +set g_balance_blaster_primary_radius 70 +set g_balance_blaster_primary_refire 0.7 +set g_balance_blaster_primary_shotangle 0 +set g_balance_blaster_primary_speed 6000 +set g_balance_blaster_primary_spread 0 +set g_balance_blaster_secondary 0 +set g_balance_blaster_secondary_animtime 0.2 +set g_balance_blaster_secondary_damage 25 +set g_balance_blaster_secondary_delay 0 +set g_balance_blaster_secondary_edgedamage 12.5 +set g_balance_blaster_secondary_force 300 +set g_balance_blaster_secondary_lifetime 5 +set g_balance_blaster_secondary_radius 70 +set g_balance_blaster_secondary_refire 0.7 +set g_balance_blaster_secondary_shotangle 0 +set g_balance_blaster_secondary_speed 6000 +set g_balance_blaster_secondary_spread 0 +set g_balance_blaster_switchdelay_drop 0.15 +set g_balance_blaster_switchdelay_raise 0.15 +set g_balance_blaster_weaponreplace "" +set g_balance_blaster_weaponstart 1 +set g_balance_blaster_weaponstartoverride -1 +set g_balance_blaster_weaponthrowable 0 +// }}} +// {{{ #2: Shockwave +set g_balance_shockwave_blast_animtime 0.3 +set g_balance_shockwave_blast_damage 20 +set g_balance_shockwave_blast_distance 1000 +set g_balance_shockwave_blast_edgedamage 0 +set g_balance_shockwave_blast_force 200 +set g_balance_shockwave_blast_force_forwardbias 50 +set g_balance_shockwave_blast_force_zscale 2 +set g_balance_shockwave_blast_jump_damage 20 +set g_balance_shockwave_blast_jump_edgedamage 0 +set g_balance_shockwave_blast_jump_force 300 +set g_balance_shockwave_blast_jump_force_velocitybias 0 +set g_balance_shockwave_blast_jump_force_zscale 1.25 +set g_balance_shockwave_blast_jump_multiplier_accuracy 0.5 +set g_balance_shockwave_blast_jump_multiplier_distance 0.5 +set g_balance_shockwave_blast_jump_multiplier_min 0 +set g_balance_shockwave_blast_jump_radius 150 +set g_balance_shockwave_blast_multiplier_accuracy 0.5 +set g_balance_shockwave_blast_multiplier_distance 0.5 +set g_balance_shockwave_blast_multiplier_min 0 +set g_balance_shockwave_blast_refire 0.75 +set g_balance_shockwave_blast_splash_damage 15 +set g_balance_shockwave_blast_splash_edgedamage 0 +set g_balance_shockwave_blast_splash_force 100 +set g_balance_shockwave_blast_splash_force_forwardbias 50 +set g_balance_shockwave_blast_splash_multiplier_accuracy 0.5 +set g_balance_shockwave_blast_splash_multiplier_distance 0.5 +set g_balance_shockwave_blast_splash_multiplier_min 0 +set g_balance_shockwave_blast_splash_radius 70 +set g_balance_shockwave_blast_spread_max 120 +set g_balance_shockwave_blast_spread_min 25 +set g_balance_shockwave_melee_animtime 1.3 +set g_balance_shockwave_melee_damage 80 +set g_balance_shockwave_melee_delay 0.25 +set g_balance_shockwave_melee_force 200 +set g_balance_shockwave_melee_multihit 1 +set g_balance_shockwave_melee_no_doubleslap 1 +set g_balance_shockwave_melee_nonplayerdamage 40 +set g_balance_shockwave_melee_range 120 +set g_balance_shockwave_melee_refire 1.25 +set g_balance_shockwave_melee_swing_side 120 +set g_balance_shockwave_melee_swing_up 30 +set g_balance_shockwave_melee_time 0.15 +set g_balance_shockwave_melee_traces 10 +set g_balance_shockwave_switchdelay_drop 0.2 +set g_balance_shockwave_switchdelay_raise 0.2 +set g_balance_shockwave_weaponreplace "" +set g_balance_shockwave_weaponstart 1 +set g_balance_shockwave_weaponstartoverride -1 +set g_balance_shockwave_weaponthrowable 0 +// }}} +// {{{ #3: Machine Gun +set g_balance_machinegun_burst 3 +set g_balance_machinegun_burst_ammo 3 +set g_balance_machinegun_burst_animtime 0.3 +set g_balance_machinegun_burst_refire 0.06 +set g_balance_machinegun_burst_refire2 0.45 +set g_balance_machinegun_burst_speed 0 +set g_balance_machinegun_first 1 +set g_balance_machinegun_first_ammo 1 +set g_balance_machinegun_first_damage 14 +set g_balance_machinegun_first_force 5 +set g_balance_machinegun_first_refire 0.125 +set g_balance_machinegun_first_spread 0.03 +set g_balance_machinegun_mode 1 +set g_balance_machinegun_reload_ammo 60 +set g_balance_machinegun_reload_time 2 +set g_balance_machinegun_solidpenetration 13.1 +set g_balance_machinegun_spread_add 0.012 +set g_balance_machinegun_spread_max 0.05 +set g_balance_machinegun_spread_min 0.02 +set g_balance_machinegun_sustained_ammo 1 +set g_balance_machinegun_sustained_damage 10 +set g_balance_machinegun_sustained_force 5 +set g_balance_machinegun_sustained_refire 0.1 +set g_balance_machinegun_sustained_spread 0.03 +set g_balance_machinegun_switchdelay_drop 0.2 +set g_balance_machinegun_switchdelay_raise 0.2 +set g_balance_machinegun_weaponreplace "arc" +set g_balance_machinegun_weaponstart 0 +set g_balance_machinegun_weaponstartoverride -1 +set g_balance_machinegun_weaponthrowable 1 +// }}} +// {{{ #4: Mortar +set g_balance_mortar_bouncefactor 0.5 +set g_balance_mortar_bouncestop 0.075 +set g_balance_mortar_primary_ammo 2 +set g_balance_mortar_primary_animtime 0.3 +set g_balance_mortar_primary_damage 50 +set g_balance_mortar_primary_damageforcescale 0 +set g_balance_mortar_primary_edgedamage 25 +set g_balance_mortar_primary_force 250 +set g_balance_mortar_primary_health 15 +set g_balance_mortar_primary_lifetime 5 +set g_balance_mortar_primary_lifetime_stick 0 +set g_balance_mortar_primary_radius 120 +set g_balance_mortar_primary_refire 0.8 +set g_balance_mortar_primary_remote_minbouncecnt 0 +set g_balance_mortar_primary_speed 1900 +set g_balance_mortar_primary_speed_up 225 +set g_balance_mortar_primary_speed_z 0 +set g_balance_mortar_primary_spread 0 +set g_balance_mortar_primary_type 0 +set g_balance_mortar_reload_ammo 0 +set g_balance_mortar_reload_time 2 +set g_balance_mortar_secondary_ammo 2 +set g_balance_mortar_secondary_animtime 0.3 +set g_balance_mortar_secondary_damage 60 +set g_balance_mortar_secondary_damageforcescale 4 +set g_balance_mortar_secondary_edgedamage 30 +set g_balance_mortar_secondary_force 250 +set g_balance_mortar_secondary_health 30 +set g_balance_mortar_secondary_lifetime 5 +set g_balance_mortar_secondary_lifetime_bounce 0.5 +set g_balance_mortar_secondary_lifetime_stick 0 +set g_balance_mortar_secondary_radius 120 +set g_balance_mortar_secondary_refire 0.7 +set g_balance_mortar_secondary_remote_detonateprimary 0 +set g_balance_mortar_secondary_speed 1400 +set g_balance_mortar_secondary_speed_up 150 +set g_balance_mortar_secondary_speed_z 0 +set g_balance_mortar_secondary_spread 0 +set g_balance_mortar_secondary_type 1 +set g_balance_mortar_switchdelay_drop 0.2 +set g_balance_mortar_switchdelay_raise 0.2 +set g_balance_mortar_weaponreplace "" +set g_balance_mortar_weaponstart 0 +set g_balance_mortar_weaponstartoverride -1 +set g_balance_mortar_weaponthrowable 1 +// }}} +// {{{ #5: Mine Layer (MUTATOR WEAPON) +set g_balance_minelayer_ammo 4 +set g_balance_minelayer_animtime 0.4 +set g_balance_minelayer_damage 40 +set g_balance_minelayer_damageforcescale 0 +set g_balance_minelayer_detonatedelay -1 +set g_balance_minelayer_edgedamage 20 +set g_balance_minelayer_force 250 +set g_balance_minelayer_health 15 +set g_balance_minelayer_lifetime 10 +set g_balance_minelayer_lifetime_countdown 0.5 +set g_balance_minelayer_limit 3 +set g_balance_minelayer_protection 0 +set g_balance_minelayer_proximityradius 150 +set g_balance_minelayer_radius 175 +set g_balance_minelayer_refire 1.5 +set g_balance_minelayer_reload_ammo 0 +set g_balance_minelayer_reload_time 2 +set g_balance_minelayer_remote_damage 45 +set g_balance_minelayer_remote_edgedamage 40 +set g_balance_minelayer_remote_force 300 +set g_balance_minelayer_remote_radius 200 +set g_balance_minelayer_speed 1000 +set g_balance_minelayer_switchdelay_drop 0.2 +set g_balance_minelayer_switchdelay_raise 0.2 +set g_balance_minelayer_time 0.5 +set g_balance_minelayer_weaponreplace "" +set g_balance_minelayer_weaponstart 0 +set g_balance_minelayer_weaponstartoverride -1 +set g_balance_minelayer_weaponthrowable 1 +// }}} +// {{{ #6: Electro +set g_balance_electro_combo_comboradius 300 +set g_balance_electro_combo_comboradius_thruwall 200 +set g_balance_electro_combo_damage 50 +set g_balance_electro_combo_edgedamage 25 +set g_balance_electro_combo_force 120 +set g_balance_electro_combo_radius 150 +set g_balance_electro_combo_safeammocheck 1 +set g_balance_electro_combo_speed 2000 +set g_balance_electro_primary_ammo 4 +set g_balance_electro_primary_animtime 0.3 +set g_balance_electro_primary_comboradius 300 +set g_balance_electro_primary_damage 40 +set g_balance_electro_primary_edgedamage 20 +set g_balance_electro_primary_force 200 +set g_balance_electro_primary_lifetime 5 +set g_balance_electro_primary_midaircombo_explode 1 +set g_balance_electro_primary_midaircombo_interval 0.1 +set g_balance_electro_primary_midaircombo_radius 100 +set g_balance_electro_primary_radius 100 +set g_balance_electro_primary_refire 0.6 +set g_balance_electro_primary_speed 2500 +set g_balance_electro_primary_spread 0 +set g_balance_electro_reload_ammo 0 +set g_balance_electro_reload_time 2 +set g_balance_electro_secondary_ammo 2 +set g_balance_electro_secondary_animtime 0.2 +set g_balance_electro_secondary_bouncefactor 0.3 +set g_balance_electro_secondary_bouncestop 0.05 +set g_balance_electro_secondary_count 3 +set g_balance_electro_secondary_damage 40 +set g_balance_electro_secondary_damagedbycontents 1 +set g_balance_electro_secondary_damageforcescale 4 +set g_balance_electro_secondary_edgedamage 20 +set g_balance_electro_secondary_force 50 +set g_balance_electro_secondary_health 5 +set g_balance_electro_secondary_lifetime 4 +set g_balance_electro_secondary_radius 150 +set g_balance_electro_secondary_refire 0.2 +set g_balance_electro_secondary_refire2 1.6 +set g_balance_electro_secondary_speed 1000 +set g_balance_electro_secondary_speed_up 200 +set g_balance_electro_secondary_speed_z 0 +set g_balance_electro_secondary_spread 0.04 +set g_balance_electro_secondary_touchexplode 0 +set g_balance_electro_switchdelay_drop 0.2 +set g_balance_electro_switchdelay_raise 0.2 +set g_balance_electro_weaponreplace "" +set g_balance_electro_weaponstart 0 +set g_balance_electro_weaponstartoverride -1 +set g_balance_electro_weaponthrowable 1 +// }}} +// {{{ #7: Arc +set g_balance_arc_beam_ammo 0 +set g_balance_arc_beam_animtime 0.2 +set g_balance_arc_beam_botaimlifetime 0 +set g_balance_arc_beam_botaimspeed 0 +set g_balance_arc_beam_damage 150 +set g_balance_arc_beam_degreespersegment 1 +set g_balance_arc_beam_distancepersegment 0 +set g_balance_arc_beam_falloff_halflifedist 0 +set g_balance_arc_beam_falloff_maxdist 0 +set g_balance_arc_beam_falloff_mindist 0 +set g_balance_arc_beam_force 2000 +set g_balance_arc_beam_healing_amax 200 +set g_balance_arc_beam_healing_aps 50 +set g_balance_arc_beam_healing_hmax 200 +set g_balance_arc_beam_healing_hps 50 +set g_balance_arc_beam_maxangle 10 +set g_balance_arc_beam_nonplayerdamage 80 +set g_balance_arc_beam_range 1000 +set g_balance_arc_beam_refire 0.5 +set g_balance_arc_beam_returnspeed 8 +set g_balance_arc_beam_tightness 0.5 +set g_balance_arc_burst_ammo 0 +set g_balance_arc_burst_damage 500 +set g_balance_arc_burst_healing_aps 100 +set g_balance_arc_burst_healing_hps 100 +set g_balance_arc_cooldown 2.5 +set g_balance_arc_overheat_max 5 +set g_balance_arc_overheat_min 3 +set g_balance_arc_beam_heat 1 +set g_balance_arc_burst_heat 5 +set g_balance_arc_switchdelay_drop 0.3 +set g_balance_arc_switchdelay_raise 0.3 +set g_balance_arc_weaponreplace "" +set g_balance_arc_weaponstart 0 +set g_balance_arc_weaponstartoverride -1 +set g_balance_arc_weaponthrowable 1 +// }}} +// {{{ #8: Crylink +set g_balance_crylink_primary_ammo 3 +set g_balance_crylink_primary_animtime 0.3 +set g_balance_crylink_primary_bouncedamagefactor 0.5 +set g_balance_crylink_primary_bounces 1 +set g_balance_crylink_primary_damage 12 +set g_balance_crylink_primary_edgedamage 6 +set g_balance_crylink_primary_force -50 +set g_balance_crylink_primary_joindelay 0.1 +set g_balance_crylink_primary_joinexplode 1 +set g_balance_crylink_primary_joinexplode_damage 0 +set g_balance_crylink_primary_joinexplode_edgedamage 0 +set g_balance_crylink_primary_joinexplode_force 0 +set g_balance_crylink_primary_joinexplode_radius 0 +set g_balance_crylink_primary_joinspread 0.2 +set g_balance_crylink_primary_linkexplode 1 +set g_balance_crylink_primary_middle_fadetime 5 +set g_balance_crylink_primary_middle_lifetime 5 +set g_balance_crylink_primary_other_fadetime 5 +set g_balance_crylink_primary_other_lifetime 5 +set g_balance_crylink_primary_radius 80 +set g_balance_crylink_primary_refire 0.7 +set g_balance_crylink_primary_shots 6 +set g_balance_crylink_primary_speed 2000 +set g_balance_crylink_primary_spread 0.08 +set g_balance_crylink_reload_ammo 0 +set g_balance_crylink_reload_time 2 +set g_balance_crylink_secondary 1 +set g_balance_crylink_secondary_ammo 2 +set g_balance_crylink_secondary_animtime 0.2 +set g_balance_crylink_secondary_bouncedamagefactor 0.5 +set g_balance_crylink_secondary_bounces 0 +set g_balance_crylink_secondary_damage 10 +set g_balance_crylink_secondary_edgedamage 5 +set g_balance_crylink_secondary_force -250 +set g_balance_crylink_secondary_joindelay 0 +set g_balance_crylink_secondary_joinexplode 0 +set g_balance_crylink_secondary_joinexplode_damage 0 +set g_balance_crylink_secondary_joinexplode_edgedamage 0 +set g_balance_crylink_secondary_joinexplode_force 0 +set g_balance_crylink_secondary_joinexplode_radius 0 +set g_balance_crylink_secondary_joinspread 0 +set g_balance_crylink_secondary_linkexplode 1 +set g_balance_crylink_secondary_middle_fadetime 5 +set g_balance_crylink_secondary_middle_lifetime 5 +set g_balance_crylink_secondary_other_fadetime 5 +set g_balance_crylink_secondary_other_lifetime 5 +set g_balance_crylink_secondary_radius 100 +set g_balance_crylink_secondary_refire 0.7 +set g_balance_crylink_secondary_shots 5 +set g_balance_crylink_secondary_speed 3000 +set g_balance_crylink_secondary_spread 0.01 +set g_balance_crylink_secondary_spreadtype 1 +set g_balance_crylink_switchdelay_drop 0.2 +set g_balance_crylink_switchdelay_raise 0.2 +set g_balance_crylink_weaponreplace "" +set g_balance_crylink_weaponstart 0 +set g_balance_crylink_weaponstartoverride -1 +set g_balance_crylink_weaponthrowable 1 +// }}} +// {{{ #9: Vortex +set g_balance_vortex_charge 1 +set g_balance_vortex_charge_animlimit 0.5 +set g_balance_vortex_charge_limit 1 +set g_balance_vortex_charge_maxspeed 800 +set g_balance_vortex_charge_mindmg 40 +set g_balance_vortex_charge_minspeed 400 +set g_balance_vortex_charge_rate 0.4 +set g_balance_vortex_charge_rot_pause 0 +set g_balance_vortex_charge_rot_rate 0 +set g_balance_vortex_charge_shot_multiplier 0 +set g_balance_vortex_charge_start 0.5 +set g_balance_vortex_charge_velocity_rate 0 +set g_balance_vortex_primary_ammo 6 +set g_balance_vortex_primary_animtime 0.6 +set g_balance_vortex_primary_damage 80 +set g_balance_vortex_primary_damagefalloff_forcehalflife 0 +set g_balance_vortex_primary_damagefalloff_halflife 0 +set g_balance_vortex_primary_damagefalloff_maxdist 0 +set g_balance_vortex_primary_damagefalloff_mindist 0 +set g_balance_vortex_primary_force 400 +set g_balance_vortex_primary_refire 1.5 +set g_balance_vortex_reload_ammo 0 +set g_balance_vortex_reload_time 2 +set g_balance_vortex_secondary 0 +set g_balance_vortex_secondary_ammo 2 +set g_balance_vortex_secondary_animtime 0 +set g_balance_vortex_secondary_chargepool 0 +set g_balance_vortex_secondary_chargepool_pause_regen 1 +set g_balance_vortex_secondary_chargepool_regen 0.15 +set g_balance_vortex_secondary_damage 0 +set g_balance_vortex_secondary_damagefalloff_forcehalflife 0 +set g_balance_vortex_secondary_damagefalloff_halflife 0 +set g_balance_vortex_secondary_damagefalloff_maxdist 0 +set g_balance_vortex_secondary_damagefalloff_mindist 0 +set g_balance_vortex_secondary_force 0 +set g_balance_vortex_secondary_refire 0 +set g_balance_vortex_switchdelay_drop 0.3 +set g_balance_vortex_switchdelay_raise 0.25 +set g_balance_vortex_weaponreplace "" +set g_balance_vortex_weaponstart 0 +set g_balance_vortex_weaponstartoverride -1 +set g_balance_vortex_weaponthrowable 1 +// }}} +// {{{ #10: Hagar +set g_balance_hagar_primary_ammo 1 +set g_balance_hagar_primary_damage 25 +set g_balance_hagar_primary_damageforcescale 0 +set g_balance_hagar_primary_edgedamage 12.5 +set g_balance_hagar_primary_force 100 +set g_balance_hagar_primary_health 15 +set g_balance_hagar_primary_lifetime 5 +set g_balance_hagar_primary_radius 65 +set g_balance_hagar_primary_refire 0.16667 +set g_balance_hagar_primary_speed 2500 +set g_balance_hagar_primary_spread 0.03 +set g_balance_hagar_reload_ammo 0 +set g_balance_hagar_reload_time 2 +set g_balance_hagar_secondary 1 +set g_balance_hagar_secondary_ammo 1 +set g_balance_hagar_secondary_damage 40 +set g_balance_hagar_secondary_damageforcescale 0 +set g_balance_hagar_secondary_edgedamage 20 +set g_balance_hagar_secondary_force 75 +set g_balance_hagar_secondary_health 15 +set g_balance_hagar_secondary_lifetime_min 10 +set g_balance_hagar_secondary_lifetime_rand 0 +set g_balance_hagar_secondary_load 1 +set g_balance_hagar_secondary_load_abort 0 +set g_balance_hagar_secondary_load_animtime 0.2 +set g_balance_hagar_secondary_load_hold 4 +set g_balance_hagar_secondary_load_linkexplode 0 +set g_balance_hagar_secondary_load_max 4 +set g_balance_hagar_secondary_load_releasedeath 0 +set g_balance_hagar_secondary_load_speed 0.5 +set g_balance_hagar_secondary_load_spread 0.075 +set g_balance_hagar_secondary_load_spread_bias 0.5 +set g_balance_hagar_secondary_radius 80 +set g_balance_hagar_secondary_refire 0.5 +set g_balance_hagar_secondary_speed 2500 +set g_balance_hagar_secondary_spread 0.05 +set g_balance_hagar_switchdelay_drop 0.2 +set g_balance_hagar_switchdelay_raise 0.2 +set g_balance_hagar_weaponreplace "" +set g_balance_hagar_weaponstart 0 +set g_balance_hagar_weaponstartoverride -1 +set g_balance_hagar_weaponthrowable 1 +// }}} +// {{{ #11: Devastator +set g_balance_devastator_ammo 4 +set g_balance_devastator_animtime 0.4 +set g_balance_devastator_damage 70 +set g_balance_devastator_damageforcescale 1 +set g_balance_devastator_detonatedelay 0.02 +set g_balance_devastator_edgedamage 35 +set g_balance_devastator_force 450 +set g_balance_devastator_guidedelay 0.2 +set g_balance_devastator_guidegoal 512 +set g_balance_devastator_guiderate 70 +set g_balance_devastator_guideratedelay 0.01 +set g_balance_devastator_guidestop 0 +set g_balance_devastator_health 30 +set g_balance_devastator_lifetime 10 +set g_balance_devastator_radius 110 +set g_balance_devastator_refire 1.2 +set g_balance_devastator_reload_ammo 0 +set g_balance_devastator_reload_time 2 +set g_balance_devastator_remote_damage 70 +set g_balance_devastator_remote_edgedamage 35 +set g_balance_devastator_remote_force 400 +set g_balance_devastator_remote_jump_damage 40 +set g_balance_devastator_remote_jump_radius 200 +set g_balance_devastator_remote_jump_velocity_z_add 500 +set g_balance_devastator_remote_jump_velocity_z_max 1500 +set g_balance_devastator_remote_jump_velocity_z_min 500 +set g_balance_devastator_remote_radius 110 +set g_balance_devastator_speed 1300 +set g_balance_devastator_speedaccel 1300 +set g_balance_devastator_speedstart 1000 +set g_balance_devastator_switchdelay_drop 0.3 +set g_balance_devastator_switchdelay_raise 0.2 +set g_balance_devastator_weaponreplace "" +set g_balance_devastator_weaponstart 0 +set g_balance_devastator_weaponstartoverride -1 +set g_balance_devastator_weaponthrowable 1 +// }}} +// {{{ #12: Port-O-Launch +set g_balance_porto_primary_animtime 0.3 +set g_balance_porto_primary_lifetime 5 +set g_balance_porto_primary_refire 1.5 +set g_balance_porto_primary_speed 1000 +set g_balance_porto_secondary 1 +set g_balance_porto_secondary_animtime 0.3 +set g_balance_porto_secondary_lifetime 5 +set g_balance_porto_secondary_refire 1.5 +set g_balance_porto_secondary_speed 1000 +set g_balance_porto_switchdelay_drop 0.2 +set g_balance_porto_switchdelay_raise 0.2 +set g_balance_porto_weaponreplace "" +set g_balance_porto_weaponstart 0 +set g_balance_porto_weaponstartoverride -1 +set g_balance_porto_weaponthrowable 1 +// }}} +// {{{ #13: Vaporizer +set g_balance_vaporizer_primary_ammo 10 +set g_balance_vaporizer_primary_animtime 0.3 +set g_balance_vaporizer_primary_refire 1 +set g_balance_vaporizer_reload_ammo 0 +set g_balance_vaporizer_reload_time 0 +set g_balance_vaporizer_secondary_ammo 0 +set g_balance_vaporizer_secondary_animtime 0.2 +set g_balance_vaporizer_secondary_damage 25 +set g_balance_vaporizer_secondary_delay 0 +set g_balance_vaporizer_secondary_edgedamage 12.5 +set g_balance_vaporizer_secondary_force 400 +set g_balance_vaporizer_secondary_lifetime 5 +set g_balance_vaporizer_secondary_radius 70 +set g_balance_vaporizer_secondary_refire 0.7 +set g_balance_vaporizer_secondary_shotangle 0 +set g_balance_vaporizer_secondary_speed 6000 +set g_balance_vaporizer_secondary_spread 0 +set g_balance_vaporizer_switchdelay_drop 0.2 +set g_balance_vaporizer_switchdelay_raise 0.2 +set g_balance_vaporizer_weaponreplace "" +set g_balance_vaporizer_weaponstart 0 +set g_balance_vaporizer_weaponstartoverride -1 +set g_balance_vaporizer_weaponthrowable 0 +// }}} +// {{{ #14: Grappling Hook +set g_balance_hook_primary_ammo 5 +set g_balance_hook_primary_animtime 0.3 +set g_balance_hook_primary_hooked_ammo 5 +set g_balance_hook_primary_hooked_time_free 2 +set g_balance_hook_primary_hooked_time_max 0 +set g_balance_hook_primary_refire 0 +set g_balance_hook_secondary_animtime 0.3 +set g_balance_hook_secondary_damage 25 +set g_balance_hook_secondary_damageforcescale 0 +set g_balance_hook_secondary_duration 1.5 +set g_balance_hook_secondary_edgedamage 5 +set g_balance_hook_secondary_force -2000 +set g_balance_hook_secondary_gravity 5 +set g_balance_hook_secondary_health 15 +set g_balance_hook_secondary_lifetime 5 +set g_balance_hook_secondary_power 3 +set g_balance_hook_secondary_radius 500 +set g_balance_hook_secondary_refire 3 +set g_balance_hook_secondary_speed 0 +set g_balance_hook_switchdelay_drop 0.2 +set g_balance_hook_switchdelay_raise 0.2 +set g_balance_hook_weaponreplace "" +set g_balance_hook_weaponstart 0 +set g_balance_hook_weaponstartoverride -1 +set g_balance_hook_weaponthrowable 1 +// }}} +// {{{ #15: Heavy Laser Assault Cannon (MUTATOR WEAPON) +set g_balance_hlac_primary_ammo 1 +set g_balance_hlac_primary_animtime 0.4 +set g_balance_hlac_primary_damage 18 +set g_balance_hlac_primary_edgedamage 9 +set g_balance_hlac_primary_force 90 +set g_balance_hlac_primary_lifetime 5 +set g_balance_hlac_primary_radius 70 +set g_balance_hlac_primary_refire 0.15 +set g_balance_hlac_primary_speed 9000 +set g_balance_hlac_primary_spread_add 0.0045 +set g_balance_hlac_primary_spread_crouchmod 0.25 +set g_balance_hlac_primary_spread_max 0.25 +set g_balance_hlac_primary_spread_min 0.01 +set g_balance_hlac_reload_ammo 0 +set g_balance_hlac_reload_time 2 +set g_balance_hlac_secondary 1 +set g_balance_hlac_secondary_ammo 10 +set g_balance_hlac_secondary_animtime 0.3 +set g_balance_hlac_secondary_damage 15 +set g_balance_hlac_secondary_edgedamage 7.5 +set g_balance_hlac_secondary_force 90 +set g_balance_hlac_secondary_lifetime 5 +set g_balance_hlac_secondary_radius 70 +set g_balance_hlac_secondary_refire 1 +set g_balance_hlac_secondary_shots 6 +set g_balance_hlac_secondary_speed 9000 +set g_balance_hlac_secondary_spread 0.15 +set g_balance_hlac_secondary_spread_crouchmod 0.5 +set g_balance_hlac_switchdelay_drop 0.2 +set g_balance_hlac_switchdelay_raise 0.2 +set g_balance_hlac_weaponreplace "" +set g_balance_hlac_weaponstart 0 +set g_balance_hlac_weaponstartoverride -1 +set g_balance_hlac_weaponthrowable 1 +// }}} +// {{{ #16: @!#%'n Tuba +set g_balance_tuba_animtime 0.05 +set g_balance_tuba_attenuation 0.5 +set g_balance_tuba_damage 5 +set g_balance_tuba_edgedamage 0 +set g_balance_tuba_fadetime 0.25 +set g_balance_tuba_force 40 +set g_balance_tuba_pitchstep 6 +set g_balance_tuba_radius 200 +set g_balance_tuba_refire 0.05 +set g_balance_tuba_switchdelay_drop 0.2 +set g_balance_tuba_switchdelay_raise 0.2 +set g_balance_tuba_volume 1 +set g_balance_tuba_weaponreplace "" +set g_balance_tuba_weaponstart 0 +set g_balance_tuba_weaponstartoverride -1 +set g_balance_tuba_weaponthrowable 1 +// }}} +// {{{ #17: Rifle (MUTATOR WEAPON) +set g_balance_rifle_bursttime 0 +set g_balance_rifle_primary_ammo 10 +set g_balance_rifle_primary_animtime 0.4 +set g_balance_rifle_primary_bullethail 0 +set g_balance_rifle_primary_burstcost 0 +set g_balance_rifle_primary_damage 80 +set g_balance_rifle_primary_force 100 +set g_balance_rifle_primary_refire 1.2 +set g_balance_rifle_primary_shots 1 +set g_balance_rifle_primary_solidpenetration 62.2 +set g_balance_rifle_primary_spread 0 +set g_balance_rifle_primary_tracer 1 +set g_balance_rifle_reload_ammo 80 +set g_balance_rifle_reload_time 2 +set g_balance_rifle_secondary 1 +set g_balance_rifle_secondary_ammo 10 +set g_balance_rifle_secondary_animtime 0.3 +set g_balance_rifle_secondary_bullethail 0 +set g_balance_rifle_secondary_burstcost 0 +set g_balance_rifle_secondary_damage 20 +set g_balance_rifle_secondary_force 50 +set g_balance_rifle_secondary_refire 0.9 +set g_balance_rifle_secondary_reload 0 +set g_balance_rifle_secondary_shots 4 +set g_balance_rifle_secondary_solidpenetration 15.5 +set g_balance_rifle_secondary_spread 0.04 +set g_balance_rifle_secondary_tracer 0 +set g_balance_rifle_switchdelay_drop 0.2 +set g_balance_rifle_switchdelay_raise 0.2 +set g_balance_rifle_weaponreplace "" +set g_balance_rifle_weaponstart 0 +set g_balance_rifle_weaponstartoverride -1 +set g_balance_rifle_weaponthrowable 1 +// }}} +// {{{ #18: Fireball +set g_balance_fireball_primary_animtime 0.2 +set g_balance_fireball_primary_bfgdamage 100 +set g_balance_fireball_primary_bfgforce 0 +set g_balance_fireball_primary_bfgradius 1000 +set g_balance_fireball_primary_damage 200 +set g_balance_fireball_primary_damageforcescale 0 +set g_balance_fireball_primary_edgedamage 50 +set g_balance_fireball_primary_force 600 +set g_balance_fireball_primary_health 0 +set g_balance_fireball_primary_laserburntime 0.5 +set g_balance_fireball_primary_laserdamage 80 +set g_balance_fireball_primary_laseredgedamage 20 +set g_balance_fireball_primary_laserradius 256 +set g_balance_fireball_primary_lifetime 15 +set g_balance_fireball_primary_radius 200 +set g_balance_fireball_primary_refire 2 +set g_balance_fireball_primary_refire2 0 +set g_balance_fireball_primary_speed 1200 +set g_balance_fireball_primary_spread 0 +set g_balance_fireball_secondary_animtime 0.3 +set g_balance_fireball_secondary_damage 40 +set g_balance_fireball_secondary_damageforcescale 4 +set g_balance_fireball_secondary_damagetime 5 +set g_balance_fireball_secondary_laserburntime 0.5 +set g_balance_fireball_secondary_laserdamage 50 +set g_balance_fireball_secondary_laseredgedamage 20 +set g_balance_fireball_secondary_laserradius 110 +set g_balance_fireball_secondary_lifetime 7 +set g_balance_fireball_secondary_refire 1.5 +set g_balance_fireball_secondary_speed 900 +set g_balance_fireball_secondary_speed_up 100 +set g_balance_fireball_secondary_speed_z 0 +set g_balance_fireball_secondary_spread 0 +set g_balance_fireball_switchdelay_drop 0.2 +set g_balance_fireball_switchdelay_raise 0.2 +set g_balance_fireball_weaponreplace "" +set g_balance_fireball_weaponstart 0 +set g_balance_fireball_weaponstartoverride -1 +set g_balance_fireball_weaponthrowable 0 +// }}} +// {{{ #19: T.A.G. Seeker (MUTATOR WEAPON) +set g_balance_seeker_flac_ammo 1 +set g_balance_seeker_flac_animtime 0.1 +set g_balance_seeker_flac_damage 15 +set g_balance_seeker_flac_edgedamage 10 +set g_balance_seeker_flac_force 50 +set g_balance_seeker_flac_lifetime 0.1 +set g_balance_seeker_flac_lifetime_rand 0.05 +set g_balance_seeker_flac_radius 100 +set g_balance_seeker_flac_refire 0.1 +set g_balance_seeker_flac_speed 3000 +set g_balance_seeker_flac_speed_up 1000 +set g_balance_seeker_flac_speed_z 0 +set g_balance_seeker_flac_spread 0.4 +set g_balance_seeker_missile_accel 1400 +set g_balance_seeker_missile_ammo 2 +set g_balance_seeker_missile_animtime 0.2 +set g_balance_seeker_missile_count 3 +set g_balance_seeker_missile_damage 30 +set g_balance_seeker_missile_damageforcescale 4 +set g_balance_seeker_missile_decel 1400 +set g_balance_seeker_missile_delay 0.25 +set g_balance_seeker_missile_edgedamage 10 +set g_balance_seeker_missile_force 150 +set g_balance_seeker_missile_health 5 +set g_balance_seeker_missile_lifetime 15 +set g_balance_seeker_missile_proxy 0 +set g_balance_seeker_missile_proxy_delay 0.2 +set g_balance_seeker_missile_proxy_maxrange 45 +set g_balance_seeker_missile_radius 80 +set g_balance_seeker_missile_refire 0.5 +set g_balance_seeker_missile_smart 1 +set g_balance_seeker_missile_smart_mindist 800 +set g_balance_seeker_missile_smart_trace_max 2500 +set g_balance_seeker_missile_smart_trace_min 1000 +set g_balance_seeker_missile_speed 700 +set g_balance_seeker_missile_speed_max 1300 +set g_balance_seeker_missile_speed_up 300 +set g_balance_seeker_missile_speed_z 0 +set g_balance_seeker_missile_spread 0 +set g_balance_seeker_missile_turnrate 0.65 +set g_balance_seeker_reload_ammo 0 +set g_balance_seeker_reload_time 2 +set g_balance_seeker_switchdelay_drop 0.2 +set g_balance_seeker_switchdelay_raise 0.2 +set g_balance_seeker_tag_ammo 1 +set g_balance_seeker_tag_animtime 0.2 +set g_balance_seeker_tag_damageforcescale 4 +set g_balance_seeker_tag_health 5 +set g_balance_seeker_tag_lifetime 15 +set g_balance_seeker_tag_refire 0.75 +set g_balance_seeker_tag_speed 5000 +set g_balance_seeker_tag_spread 0 +set g_balance_seeker_tag_tracker_lifetime 10 +set g_balance_seeker_type 0 +set g_balance_seeker_weaponreplace "" +set g_balance_seeker_weaponstart 0 +set g_balance_seeker_weaponstartoverride -1 +set g_balance_seeker_weaponthrowable 1 +// }}} +// {{{ #20: Shotgun (MUTATOR WEAPON) +set g_balance_shotgun_primary_ammo 1 +set g_balance_shotgun_primary_animtime 0.2 +set g_balance_shotgun_primary_bullets 14 +set g_balance_shotgun_primary_damage 4 +set g_balance_shotgun_primary_force 15 +set g_balance_shotgun_primary_refire 0.75 +set g_balance_shotgun_primary_solidpenetration 3.8 +set g_balance_shotgun_primary_spread 0.12 +set g_balance_shotgun_reload_ammo 0 +set g_balance_shotgun_reload_time 2 +set g_balance_shotgun_secondary 1 +set g_balance_shotgun_secondary_animtime 1 +set g_balance_shotgun_secondary_damage 80 +set g_balance_shotgun_secondary_force 200 +set g_balance_shotgun_secondary_melee_delay 0.25 +set g_balance_shotgun_secondary_melee_multihit 1 +set g_balance_shotgun_secondary_melee_no_doubleslap 1 +set g_balance_shotgun_secondary_melee_nonplayerdamage 40 +set g_balance_shotgun_secondary_melee_range 120 +set g_balance_shotgun_secondary_melee_swing_side 120 +set g_balance_shotgun_secondary_melee_swing_up 30 +set g_balance_shotgun_secondary_melee_time 0.15 +set g_balance_shotgun_secondary_melee_traces 10 +set g_balance_shotgun_secondary_refire 1.25 +set g_balance_shotgun_secondary_alt_animtime 0.2 +set g_balance_shotgun_secondary_alt_refire 1.2 +set g_balance_shotgun_switchdelay_drop 0.2 +set g_balance_shotgun_switchdelay_raise 0.2 +set g_balance_shotgun_weaponreplace "" +set g_balance_shotgun_weaponstart 0 +set g_balance_shotgun_weaponstartoverride -1 +set g_balance_shotgun_weaponthrowable 1 +// }}} diff --git a/bal-wep-xdf.cfg b/bal-wep-xdf.cfg new file mode 100644 index 0000000000..ef8035ea72 --- /dev/null +++ b/bal-wep-xdf.cfg @@ -0,0 +1,763 @@ +// {{{ #1: Blaster +set g_balance_blaster_primary_animtime 0.3 +set g_balance_blaster_primary_damage 25 +set g_balance_blaster_primary_delay 0 +set g_balance_blaster_primary_edgedamage 12.5 +set g_balance_blaster_primary_force 250 +set g_balance_blaster_primary_force_zscale 1.5 +set g_balance_blaster_primary_lifetime 5 +set g_balance_blaster_primary_radius 60 +set g_balance_blaster_primary_refire 0.7 +set g_balance_blaster_primary_shotangle 0 +set g_balance_blaster_primary_speed 6000 +set g_balance_blaster_primary_spread 0 +set g_balance_blaster_secondary 0 +set g_balance_blaster_secondary_animtime 0.3 +set g_balance_blaster_secondary_damage 25 +set g_balance_blaster_secondary_delay 0 +set g_balance_blaster_secondary_edgedamage 12.5 +set g_balance_blaster_secondary_force 300 +set g_balance_blaster_secondary_force_zscale 1.2 +set g_balance_blaster_secondary_lifetime 5 +set g_balance_blaster_secondary_radius 70 +set g_balance_blaster_secondary_refire 0.7 +set g_balance_blaster_secondary_shotangle 0 +set g_balance_blaster_secondary_speed 6000 +set g_balance_blaster_secondary_spread 0 +set g_balance_blaster_switchdelay_drop 0 +set g_balance_blaster_switchdelay_raise 0 +set g_balance_blaster_weaponreplace "" +set g_balance_blaster_weaponstart 0 +set g_balance_blaster_weaponstartoverride -1 +set g_balance_blaster_weaponthrowable 0 +// }}} +// {{{ #2: Shotgun +set g_balance_shotgun_primary_ammo 1 +set g_balance_shotgun_primary_animtime 0.2 +set g_balance_shotgun_primary_bullets 14 +set g_balance_shotgun_primary_damage 4 +set g_balance_shotgun_primary_force 15 +set g_balance_shotgun_primary_refire 0.75 +set g_balance_shotgun_primary_solidpenetration 3.8 +set g_balance_shotgun_primary_spread 0.12 +set g_balance_shotgun_reload_ammo 0 +set g_balance_shotgun_reload_time 2 +set g_balance_shotgun_secondary 1 +set g_balance_shotgun_secondary_animtime 1 +set g_balance_shotgun_secondary_damage 80 +set g_balance_shotgun_secondary_force 200 +set g_balance_shotgun_secondary_melee_delay 0.25 +set g_balance_shotgun_secondary_melee_multihit 1 +set g_balance_shotgun_secondary_melee_no_doubleslap 1 +set g_balance_shotgun_secondary_melee_nonplayerdamage 40 +set g_balance_shotgun_secondary_melee_range 120 +set g_balance_shotgun_secondary_melee_swing_side 120 +set g_balance_shotgun_secondary_melee_swing_up 30 +set g_balance_shotgun_secondary_melee_time 0.15 +set g_balance_shotgun_secondary_melee_traces 10 +set g_balance_shotgun_secondary_refire 1.25 +set g_balance_shotgun_secondary_alt_animtime 0.2 +set g_balance_shotgun_secondary_alt_refire 1.2 +set g_balance_shotgun_switchdelay_drop 0 +set g_balance_shotgun_switchdelay_raise 0 +set g_balance_shotgun_weaponreplace "" +set g_balance_shotgun_weaponstart 0 +set g_balance_shotgun_weaponstartoverride -1 +set g_balance_shotgun_weaponthrowable 1 +// }}} +// {{{ #3: Machine Gun +set g_balance_machinegun_burst 3 +set g_balance_machinegun_burst_ammo 3 +set g_balance_machinegun_burst_animtime 0.3 +set g_balance_machinegun_burst_refire 0.06 +set g_balance_machinegun_burst_refire2 0.45 +set g_balance_machinegun_burst_speed 0 +set g_balance_machinegun_first 1 +set g_balance_machinegun_first_ammo 1 +set g_balance_machinegun_first_damage 14 +set g_balance_machinegun_first_force 5 +set g_balance_machinegun_first_refire 0.4 +set g_balance_machinegun_first_spread 0.03 +set g_balance_machinegun_mode 1 +set g_balance_machinegun_reload_ammo 0 +set g_balance_machinegun_reload_time 2 +set g_balance_machinegun_solidpenetration 13.1 +set g_balance_machinegun_spread_add 0 +set g_balance_machinegun_spread_max 0 +set g_balance_machinegun_spread_min 0 +set g_balance_machinegun_sustained_ammo 1 +set g_balance_machinegun_sustained_damage 12 +set g_balance_machinegun_sustained_force 5 +set g_balance_machinegun_sustained_refire 0.1 +set g_balance_machinegun_sustained_spread 0 +set g_balance_machinegun_switchdelay_drop 0 +set g_balance_machinegun_switchdelay_raise 0 +set g_balance_machinegun_weaponreplace "" +set g_balance_machinegun_weaponstart 1 +set g_balance_machinegun_weaponstartoverride -1 +set g_balance_machinegun_weaponthrowable 1 +// }}} +// {{{ #4: Mortar +set g_balance_mortar_bouncefactor 0.5 +set g_balance_mortar_bouncestop 0.075 +set g_balance_mortar_primary_ammo 2 +set g_balance_mortar_primary_animtime 0.3 +set g_balance_mortar_primary_damage 50 +set g_balance_mortar_primary_damageforcescale 0 +set g_balance_mortar_primary_edgedamage 25 +set g_balance_mortar_primary_force 250 +set g_balance_mortar_primary_health 0 +set g_balance_mortar_primary_lifetime 5 +set g_balance_mortar_primary_lifetime_stick 0 +set g_balance_mortar_primary_radius 100 +set g_balance_mortar_primary_refire 0.7 +set g_balance_mortar_primary_remote_minbouncecnt 0 +set g_balance_mortar_primary_speed 2000 +set g_balance_mortar_primary_speed_up 200 +set g_balance_mortar_primary_speed_z 0 +set g_balance_mortar_primary_spread 0 +set g_balance_mortar_primary_type 0 +set g_balance_mortar_reload_ammo 0 +set g_balance_mortar_reload_time 2 +set g_balance_mortar_secondary_ammo 2 +set g_balance_mortar_secondary_animtime 0.5 +set g_balance_mortar_secondary_damage 55 +set g_balance_mortar_secondary_damageforcescale 0 +set g_balance_mortar_secondary_edgedamage 30 +set g_balance_mortar_secondary_force 300 +set g_balance_mortar_secondary_health 0 +set g_balance_mortar_secondary_lifetime 8 +set g_balance_mortar_secondary_lifetime_bounce 0.5 +set g_balance_mortar_secondary_lifetime_stick 0 +set g_balance_mortar_secondary_radius 200 +set g_balance_mortar_secondary_refire 0.7 +set g_balance_mortar_secondary_remote_detonateprimary 0 +set g_balance_mortar_secondary_speed 800 +set g_balance_mortar_secondary_speed_up 0 +set g_balance_mortar_secondary_speed_z 200 +set g_balance_mortar_secondary_spread 0 +set g_balance_mortar_secondary_type 1 +set g_balance_mortar_switchdelay_drop 0 +set g_balance_mortar_switchdelay_raise 0 +set g_balance_mortar_weaponreplace "" +set g_balance_mortar_weaponstart 0 +set g_balance_mortar_weaponstartoverride -1 +set g_balance_mortar_weaponthrowable 1 +// }}} +// {{{ #5: Mine Layer (MUTATOR WEAPON) +set g_balance_minelayer_ammo 4 +set g_balance_minelayer_animtime 0.4 +set g_balance_minelayer_damage 40 +set g_balance_minelayer_damageforcescale 0 +set g_balance_minelayer_detonatedelay -1 +set g_balance_minelayer_edgedamage 20 +set g_balance_minelayer_force 250 +set g_balance_minelayer_health 15 +set g_balance_minelayer_lifetime 10 +set g_balance_minelayer_lifetime_countdown 0.5 +set g_balance_minelayer_limit 3 +set g_balance_minelayer_protection 0 +set g_balance_minelayer_proximityradius 150 +set g_balance_minelayer_radius 175 +set g_balance_minelayer_refire 1.5 +set g_balance_minelayer_reload_ammo 0 +set g_balance_minelayer_reload_time 2 +set g_balance_minelayer_remote_damage 45 +set g_balance_minelayer_remote_edgedamage 40 +set g_balance_minelayer_remote_force 300 +set g_balance_minelayer_remote_radius 200 +set g_balance_minelayer_speed 1000 +set g_balance_minelayer_switchdelay_drop 0 +set g_balance_minelayer_switchdelay_raise 0 +set g_balance_minelayer_time 0.5 +set g_balance_minelayer_weaponreplace "" +set g_balance_minelayer_weaponstart 0 +set g_balance_minelayer_weaponstartoverride -1 +set g_balance_minelayer_weaponthrowable 1 +// }}} +// {{{ #6: Electro +set g_balance_electro_combo_comboradius 275 +set g_balance_electro_combo_comboradius_thruwall 200 +set g_balance_electro_combo_damage 40 +set g_balance_electro_combo_edgedamage 20 +set g_balance_electro_combo_force 120 +set g_balance_electro_combo_radius 175 +set g_balance_electro_combo_safeammocheck 1 +set g_balance_electro_combo_speed 2000 +set g_balance_electro_primary_ammo 4 +set g_balance_electro_primary_animtime 0.1 +set g_balance_electro_primary_comboradius 150 +set g_balance_electro_primary_damage 55 +set g_balance_electro_primary_edgedamage 27.5 +set g_balance_electro_primary_force 200 +set g_balance_electro_primary_lifetime 5 +set g_balance_electro_primary_midaircombo_explode 1 +set g_balance_electro_primary_midaircombo_interval 0.1 +set g_balance_electro_primary_midaircombo_radius 100 +set g_balance_electro_primary_radius 100 +set g_balance_electro_primary_refire 0.6 +set g_balance_electro_primary_speed 2500 +set g_balance_electro_primary_spread 0 +set g_balance_electro_reload_ammo 0 +set g_balance_electro_reload_time 2 +set g_balance_electro_secondary_ammo 2 +set g_balance_electro_secondary_animtime 0.2 +set g_balance_electro_secondary_bouncefactor 0.4 +set g_balance_electro_secondary_bouncestop 0.05 +set g_balance_electro_secondary_count 3 +set g_balance_electro_secondary_damage 40 +set g_balance_electro_secondary_damagedbycontents 1 +set g_balance_electro_secondary_damageforcescale 4 +set g_balance_electro_secondary_edgedamage 20 +set g_balance_electro_secondary_force 200 +set g_balance_electro_secondary_health 5 +set g_balance_electro_secondary_lifetime 3 +set g_balance_electro_secondary_radius 150 +set g_balance_electro_secondary_refire 0.2 +set g_balance_electro_secondary_refire2 1.5 +set g_balance_electro_secondary_speed 900 +set g_balance_electro_secondary_speed_up 200 +set g_balance_electro_secondary_speed_z 0 +set g_balance_electro_secondary_spread 0.05 +set g_balance_electro_secondary_touchexplode 0 +set g_balance_electro_switchdelay_drop 0 +set g_balance_electro_switchdelay_raise 0 +set g_balance_electro_weaponreplace "" +set g_balance_electro_weaponstart 0 +set g_balance_electro_weaponstartoverride -1 +set g_balance_electro_weaponthrowable 1 +// }}} +// {{{ #7: Crylink +set g_balance_crylink_primary_ammo 3 +set g_balance_crylink_primary_animtime 0.3 +set g_balance_crylink_primary_bouncedamagefactor 0.5 +set g_balance_crylink_primary_bounces 1 +set g_balance_crylink_primary_damage 12 +set g_balance_crylink_primary_edgedamage 6 +set g_balance_crylink_primary_force -60 +set g_balance_crylink_primary_joindelay 0.1 +set g_balance_crylink_primary_joinexplode 1 +set g_balance_crylink_primary_joinexplode_damage 0 +set g_balance_crylink_primary_joinexplode_edgedamage 0 +set g_balance_crylink_primary_joinexplode_force 0 +set g_balance_crylink_primary_joinexplode_radius 0 +set g_balance_crylink_primary_joinspread 0.2 +set g_balance_crylink_primary_linkexplode 1 +set g_balance_crylink_primary_middle_fadetime 5 +set g_balance_crylink_primary_middle_lifetime 5 +set g_balance_crylink_primary_other_fadetime 5 +set g_balance_crylink_primary_other_lifetime 5 +set g_balance_crylink_primary_radius 80 +set g_balance_crylink_primary_refire 0.7 +set g_balance_crylink_primary_shots 6 +set g_balance_crylink_primary_speed 2000 +set g_balance_crylink_primary_spread 0.08 +set g_balance_crylink_reload_ammo 0 +set g_balance_crylink_reload_time 2 +set g_balance_crylink_secondary 1 +set g_balance_crylink_secondary_ammo 2 +set g_balance_crylink_secondary_animtime 0.2 +set g_balance_crylink_secondary_bouncedamagefactor 0.5 +set g_balance_crylink_secondary_bounces 0 +set g_balance_crylink_secondary_damage 10 +set g_balance_crylink_secondary_edgedamage 5 +set g_balance_crylink_secondary_force -150 +set g_balance_crylink_secondary_joindelay 0 +set g_balance_crylink_secondary_joinexplode 0 +set g_balance_crylink_secondary_joinexplode_damage 0 +set g_balance_crylink_secondary_joinexplode_edgedamage 0 +set g_balance_crylink_secondary_joinexplode_force 0 +set g_balance_crylink_secondary_joinexplode_radius 0 +set g_balance_crylink_secondary_joinspread 0 +set g_balance_crylink_secondary_linkexplode 1 +set g_balance_crylink_secondary_middle_fadetime 5 +set g_balance_crylink_secondary_middle_lifetime 5 +set g_balance_crylink_secondary_other_fadetime 5 +set g_balance_crylink_secondary_other_lifetime 5 +set g_balance_crylink_secondary_radius 100 +set g_balance_crylink_secondary_refire 0.7 +set g_balance_crylink_secondary_shots 5 +set g_balance_crylink_secondary_speed 3000 +set g_balance_crylink_secondary_spread 0.01 +set g_balance_crylink_secondary_spreadtype 1 +set g_balance_crylink_switchdelay_drop 0 +set g_balance_crylink_switchdelay_raise 0 +set g_balance_crylink_weaponreplace "" +set g_balance_crylink_weaponstart 0 +set g_balance_crylink_weaponstartoverride -1 +set g_balance_crylink_weaponthrowable 1 +// }}} +// {{{ #8: Vortex +set g_balance_vortex_charge 1 +set g_balance_vortex_charge_animlimit 0.5 +set g_balance_vortex_charge_limit 1 +set g_balance_vortex_charge_maxspeed 800 +set g_balance_vortex_charge_mindmg 40 +set g_balance_vortex_charge_minspeed 400 +set g_balance_vortex_charge_rate 0.4 +set g_balance_vortex_charge_rot_pause 0 +set g_balance_vortex_charge_rot_rate 0 +set g_balance_vortex_charge_shot_multiplier 0 +set g_balance_vortex_charge_start 0.5 +set g_balance_vortex_charge_velocity_rate 0 +set g_balance_vortex_primary_ammo 6 +set g_balance_vortex_primary_animtime 0.4 +set g_balance_vortex_primary_damage 90 +set g_balance_vortex_primary_damagefalloff_forcehalflife 0 +set g_balance_vortex_primary_damagefalloff_halflife 0 +set g_balance_vortex_primary_damagefalloff_maxdist 0 +set g_balance_vortex_primary_damagefalloff_mindist 0 +set g_balance_vortex_primary_force 400 +set g_balance_vortex_primary_refire 1.5 +set g_balance_vortex_reload_ammo 0 +set g_balance_vortex_reload_time 2 +set g_balance_vortex_secondary 0 +set g_balance_vortex_secondary_ammo 2 +set g_balance_vortex_secondary_animtime 0 +set g_balance_vortex_secondary_chargepool 0 +set g_balance_vortex_secondary_chargepool_pause_regen 1 +set g_balance_vortex_secondary_chargepool_regen 0.15 +set g_balance_vortex_secondary_damage 0 +set g_balance_vortex_secondary_damagefalloff_forcehalflife 0 +set g_balance_vortex_secondary_damagefalloff_halflife 0 +set g_balance_vortex_secondary_damagefalloff_maxdist 0 +set g_balance_vortex_secondary_damagefalloff_mindist 0 +set g_balance_vortex_secondary_force 0 +set g_balance_vortex_secondary_refire 0 +set g_balance_vortex_switchdelay_drop 0 +set g_balance_vortex_switchdelay_raise 0 +set g_balance_vortex_weaponreplace "" +set g_balance_vortex_weaponstart 0 +set g_balance_vortex_weaponstartoverride -1 +set g_balance_vortex_weaponthrowable 1 +// }}} +// {{{ #9: Hagar +set g_balance_hagar_primary_ammo 1 +set g_balance_hagar_primary_damage 25 +set g_balance_hagar_primary_damageforcescale 0 +set g_balance_hagar_primary_edgedamage 12.5 +set g_balance_hagar_primary_force 92 +set g_balance_hagar_primary_health 15 +set g_balance_hagar_primary_lifetime 5 +set g_balance_hagar_primary_radius 25 +set g_balance_hagar_primary_refire 0.11 +set g_balance_hagar_primary_speed 2000 +set g_balance_hagar_primary_spread 0.03 +set g_balance_hagar_reload_ammo 0 +set g_balance_hagar_reload_time 2 +set g_balance_hagar_secondary 0 +set g_balance_hagar_secondary_ammo 1 +set g_balance_hagar_secondary_damage 40 +set g_balance_hagar_secondary_damageforcescale 0 +set g_balance_hagar_secondary_edgedamage 20 +set g_balance_hagar_secondary_force 75 +set g_balance_hagar_secondary_health 15 +set g_balance_hagar_secondary_lifetime_min 10 +set g_balance_hagar_secondary_lifetime_rand 0 +set g_balance_hagar_secondary_load 1 +set g_balance_hagar_secondary_load_abort 1 +set g_balance_hagar_secondary_load_animtime 0.2 +set g_balance_hagar_secondary_load_hold 4 +set g_balance_hagar_secondary_load_linkexplode 0 +set g_balance_hagar_secondary_load_max 4 +set g_balance_hagar_secondary_load_releasedeath 0 +set g_balance_hagar_secondary_load_speed 0.5 +set g_balance_hagar_secondary_load_spread 0.075 +set g_balance_hagar_secondary_load_spread_bias 0.5 +set g_balance_hagar_secondary_radius 80 +set g_balance_hagar_secondary_refire 0.5 +set g_balance_hagar_secondary_speed 2000 +set g_balance_hagar_secondary_spread 0.05 +set g_balance_hagar_switchdelay_drop 0 +set g_balance_hagar_switchdelay_raise 0 +set g_balance_hagar_weaponreplace "" +set g_balance_hagar_weaponstart 0 +set g_balance_hagar_weaponstartoverride -1 +set g_balance_hagar_weaponthrowable 1 +// }}} +// {{{ #10: Devastator +set g_balance_devastator_ammo 4 +set g_balance_devastator_animtime 0.7 +set g_balance_devastator_damage 80 +set g_balance_devastator_damageforcescale 0 +set g_balance_devastator_detonatedelay 0.02 +set g_balance_devastator_edgedamage 40 +set g_balance_devastator_force 350 +set g_balance_devastator_guidedelay 0.2 +set g_balance_devastator_guidegoal 512 +set g_balance_devastator_guiderate 0 +set g_balance_devastator_guideratedelay 999 +set g_balance_devastator_guidestop 1 +set g_balance_devastator_health 0 +set g_balance_devastator_lifetime 100 +set g_balance_devastator_radius 110 +set g_balance_devastator_refire 0.9 +set g_balance_devastator_reload_ammo 0 +set g_balance_devastator_reload_time 2 +set g_balance_devastator_remote_damage 70 +set g_balance_devastator_remote_edgedamage 35 +set g_balance_devastator_remote_force 350 +set g_balance_devastator_remote_jump_damage 70 +set g_balance_devastator_remote_jump_radius 0 +set g_balance_devastator_remote_jump_velocity_z_add 400 +set g_balance_devastator_remote_jump_velocity_z_max 1500 +set g_balance_devastator_remote_jump_velocity_z_min 400 +set g_balance_devastator_remote_radius 110 +set g_balance_devastator_speed 1000 +set g_balance_devastator_speedaccel 0 +set g_balance_devastator_speedstart 1000 +set g_balance_devastator_switchdelay_drop 0 +set g_balance_devastator_switchdelay_raise 0 +set g_balance_devastator_weaponreplace "" +set g_balance_devastator_weaponstart 0 +set g_balance_devastator_weaponstartoverride -1 +set g_balance_devastator_weaponthrowable 1 +// }}} +// {{{ #11: Port-O-Launch +set g_balance_porto_primary_animtime 0.3 +set g_balance_porto_primary_lifetime 5 +set g_balance_porto_primary_refire 1.5 +set g_balance_porto_primary_speed 5000 +set g_balance_porto_secondary 1 +set g_balance_porto_secondary_animtime 0.3 +set g_balance_porto_secondary_lifetime 5 +set g_balance_porto_secondary_refire 1.5 +set g_balance_porto_secondary_speed 1000 +set g_balance_porto_switchdelay_drop 0 +set g_balance_porto_switchdelay_raise 0 +set g_balance_porto_weaponreplace "" +set g_balance_porto_weaponstart 0 +set g_balance_porto_weaponstartoverride -1 +set g_balance_porto_weaponthrowable 1 +// }}} +// {{{ #12: Vaporizer +set g_balance_vaporizer_primary_ammo 10 +set g_balance_vaporizer_primary_animtime 0.3 +set g_balance_vaporizer_primary_refire 1 +set g_balance_vaporizer_reload_ammo 0 +set g_balance_vaporizer_reload_time 0 +set g_balance_vaporizer_secondary_ammo 0 +set g_balance_vaporizer_secondary_animtime 0.2 +set g_balance_vaporizer_secondary_damage 25 +set g_balance_vaporizer_secondary_delay 0 +set g_balance_vaporizer_secondary_edgedamage 12.5 +set g_balance_vaporizer_secondary_force 400 +set g_balance_vaporizer_secondary_lifetime 5 +set g_balance_vaporizer_secondary_radius 70 +set g_balance_vaporizer_secondary_refire 0.7 +set g_balance_vaporizer_secondary_shotangle 0 +set g_balance_vaporizer_secondary_speed 6000 +set g_balance_vaporizer_secondary_spread 0 +set g_balance_vaporizer_switchdelay_drop 0 +set g_balance_vaporizer_switchdelay_raise 0 +set g_balance_vaporizer_weaponreplace "" +set g_balance_vaporizer_weaponstart 0 +set g_balance_vaporizer_weaponstartoverride -1 +set g_balance_vaporizer_weaponthrowable 0 +// }}} +// {{{ #13: Grappling Hook +set g_balance_hook_primary_ammo 5 +set g_balance_hook_primary_animtime 0.3 +set g_balance_hook_primary_hooked_ammo 5 +set g_balance_hook_primary_hooked_time_free 2 +set g_balance_hook_primary_hooked_time_max 0 +set g_balance_hook_primary_refire 0.2 +set g_balance_hook_secondary_animtime 0.3 +set g_balance_hook_secondary_damage 25 +set g_balance_hook_secondary_damageforcescale 0 +set g_balance_hook_secondary_duration 1.5 +set g_balance_hook_secondary_edgedamage 5 +set g_balance_hook_secondary_force -2000 +set g_balance_hook_secondary_gravity 5 +set g_balance_hook_secondary_health 15 +set g_balance_hook_secondary_lifetime 5 +set g_balance_hook_secondary_power 3 +set g_balance_hook_secondary_radius 500 +set g_balance_hook_secondary_refire 3 +set g_balance_hook_secondary_speed 0 +set g_balance_hook_switchdelay_drop 0 +set g_balance_hook_switchdelay_raise 0 +set g_balance_hook_weaponreplace "" +set g_balance_hook_weaponstart 0 +set g_balance_hook_weaponstartoverride -1 +set g_balance_hook_weaponthrowable 1 +// }}} +// {{{ #14: Heavy Laser Assault Cannon (MUTATOR WEAPON) +set g_balance_hlac_primary_ammo 1 +set g_balance_hlac_primary_animtime 0.4 +set g_balance_hlac_primary_damage 18 +set g_balance_hlac_primary_edgedamage 9 +set g_balance_hlac_primary_force 90 +set g_balance_hlac_primary_lifetime 5 +set g_balance_hlac_primary_radius 70 +set g_balance_hlac_primary_refire 0.15 +set g_balance_hlac_primary_speed 9000 +set g_balance_hlac_primary_spread_add 0.0045 +set g_balance_hlac_primary_spread_crouchmod 0.25 +set g_balance_hlac_primary_spread_max 0.25 +set g_balance_hlac_primary_spread_min 0.01 +set g_balance_hlac_reload_ammo 0 +set g_balance_hlac_reload_time 2 +set g_balance_hlac_secondary 1 +set g_balance_hlac_secondary_ammo 10 +set g_balance_hlac_secondary_animtime 0.3 +set g_balance_hlac_secondary_damage 15 +set g_balance_hlac_secondary_edgedamage 7.5 +set g_balance_hlac_secondary_force 90 +set g_balance_hlac_secondary_lifetime 5 +set g_balance_hlac_secondary_radius 70 +set g_balance_hlac_secondary_refire 1 +set g_balance_hlac_secondary_shots 6 +set g_balance_hlac_secondary_speed 9000 +set g_balance_hlac_secondary_spread 0.15 +set g_balance_hlac_secondary_spread_crouchmod 0.5 +set g_balance_hlac_switchdelay_drop 0 +set g_balance_hlac_switchdelay_raise 0 +set g_balance_hlac_weaponreplace "" +set g_balance_hlac_weaponstart 0 +set g_balance_hlac_weaponstartoverride -1 +set g_balance_hlac_weaponthrowable 1 +// }}} +// {{{ #15: @!#%'n Tuba +set g_balance_tuba_animtime 0.05 +set g_balance_tuba_attenuation 0.5 +set g_balance_tuba_damage 5 +set g_balance_tuba_edgedamage 0 +set g_balance_tuba_fadetime 0.25 +set g_balance_tuba_force 40 +set g_balance_tuba_pitchstep 6 +set g_balance_tuba_radius 200 +set g_balance_tuba_refire 0.05 +set g_balance_tuba_switchdelay_drop 0 +set g_balance_tuba_switchdelay_raise 0 +set g_balance_tuba_volume 1 +set g_balance_tuba_weaponreplace "" +set g_balance_tuba_weaponstart 0 +set g_balance_tuba_weaponstartoverride -1 +set g_balance_tuba_weaponthrowable 1 +// }}} +// {{{ #16: Rifle (MUTATOR WEAPON) +set g_balance_rifle_bursttime 0 +set g_balance_rifle_primary_ammo 10 +set g_balance_rifle_primary_animtime 0.4 +set g_balance_rifle_primary_bullethail 0 +set g_balance_rifle_primary_burstcost 0 +set g_balance_rifle_primary_damage 80 +set g_balance_rifle_primary_force 100 +set g_balance_rifle_primary_refire 1.2 +set g_balance_rifle_primary_shots 1 +set g_balance_rifle_primary_solidpenetration 62.2 +set g_balance_rifle_primary_spread 0 +set g_balance_rifle_primary_tracer 1 +set g_balance_rifle_reload_ammo 80 +set g_balance_rifle_reload_time 2 +set g_balance_rifle_secondary 1 +set g_balance_rifle_secondary_ammo 10 +set g_balance_rifle_secondary_animtime 0.3 +set g_balance_rifle_secondary_bullethail 0 +set g_balance_rifle_secondary_burstcost 0 +set g_balance_rifle_secondary_damage 20 +set g_balance_rifle_secondary_force 50 +set g_balance_rifle_secondary_refire 0.9 +set g_balance_rifle_secondary_reload 0 +set g_balance_rifle_secondary_shots 4 +set g_balance_rifle_secondary_solidpenetration 15.5 +set g_balance_rifle_secondary_spread 0.04 +set g_balance_rifle_secondary_tracer 0 +set g_balance_rifle_switchdelay_drop 0 +set g_balance_rifle_switchdelay_raise 0 +set g_balance_rifle_weaponreplace "" +set g_balance_rifle_weaponstart 0 +set g_balance_rifle_weaponstartoverride -1 +set g_balance_rifle_weaponthrowable 1 +// }}} +// {{{ #17: Fireball +set g_balance_fireball_primary_animtime 0.4 +set g_balance_fireball_primary_bfgdamage 100 +set g_balance_fireball_primary_bfgforce 0 +set g_balance_fireball_primary_bfgradius 1000 +set g_balance_fireball_primary_damage 200 +set g_balance_fireball_primary_damageforcescale 0 +set g_balance_fireball_primary_edgedamage 50 +set g_balance_fireball_primary_force 600 +set g_balance_fireball_primary_health 0 +set g_balance_fireball_primary_laserburntime 0.5 +set g_balance_fireball_primary_laserdamage 80 +set g_balance_fireball_primary_laseredgedamage 20 +set g_balance_fireball_primary_laserradius 256 +set g_balance_fireball_primary_lifetime 15 +set g_balance_fireball_primary_radius 200 +set g_balance_fireball_primary_refire 2 +set g_balance_fireball_primary_refire2 0 +set g_balance_fireball_primary_speed 1200 +set g_balance_fireball_primary_spread 0 +set g_balance_fireball_secondary_animtime 0.3 +set g_balance_fireball_secondary_damage 40 +set g_balance_fireball_secondary_damageforcescale 4 +set g_balance_fireball_secondary_damagetime 5 +set g_balance_fireball_secondary_laserburntime 0.5 +set g_balance_fireball_secondary_laserdamage 50 +set g_balance_fireball_secondary_laseredgedamage 20 +set g_balance_fireball_secondary_laserradius 110 +set g_balance_fireball_secondary_lifetime 7 +set g_balance_fireball_secondary_refire 1.5 +set g_balance_fireball_secondary_speed 900 +set g_balance_fireball_secondary_speed_up 100 +set g_balance_fireball_secondary_speed_z 0 +set g_balance_fireball_secondary_spread 0 +set g_balance_fireball_switchdelay_drop 0 +set g_balance_fireball_switchdelay_raise 0 +set g_balance_fireball_weaponreplace "" +set g_balance_fireball_weaponstart 0 +set g_balance_fireball_weaponstartoverride -1 +set g_balance_fireball_weaponthrowable 0 +// }}} +// {{{ #18: T.A.G. Seeker (MUTATOR WEAPON) +set g_balance_seeker_flac_ammo 1 +set g_balance_seeker_flac_animtime 0.1 +set g_balance_seeker_flac_damage 15 +set g_balance_seeker_flac_edgedamage 10 +set g_balance_seeker_flac_force 50 +set g_balance_seeker_flac_lifetime 0.1 +set g_balance_seeker_flac_lifetime_rand 0.05 +set g_balance_seeker_flac_radius 100 +set g_balance_seeker_flac_refire 0.1 +set g_balance_seeker_flac_speed 3000 +set g_balance_seeker_flac_speed_up 1000 +set g_balance_seeker_flac_speed_z 0 +set g_balance_seeker_flac_spread 0.4 +set g_balance_seeker_missile_accel 1400 +set g_balance_seeker_missile_ammo 2 +set g_balance_seeker_missile_animtime 0.2 +set g_balance_seeker_missile_count 3 +set g_balance_seeker_missile_damage 30 +set g_balance_seeker_missile_damageforcescale 4 +set g_balance_seeker_missile_decel 1400 +set g_balance_seeker_missile_delay 0.25 +set g_balance_seeker_missile_edgedamage 10 +set g_balance_seeker_missile_force 150 +set g_balance_seeker_missile_health 5 +set g_balance_seeker_missile_lifetime 15 +set g_balance_seeker_missile_proxy 0 +set g_balance_seeker_missile_proxy_delay 0.2 +set g_balance_seeker_missile_proxy_maxrange 45 +set g_balance_seeker_missile_radius 80 +set g_balance_seeker_missile_refire 0.5 +set g_balance_seeker_missile_smart 1 +set g_balance_seeker_missile_smart_mindist 800 +set g_balance_seeker_missile_smart_trace_max 2500 +set g_balance_seeker_missile_smart_trace_min 1000 +set g_balance_seeker_missile_speed 700 +set g_balance_seeker_missile_speed_max 1300 +set g_balance_seeker_missile_speed_up 300 +set g_balance_seeker_missile_speed_z 0 +set g_balance_seeker_missile_spread 0 +set g_balance_seeker_missile_turnrate 0.65 +set g_balance_seeker_reload_ammo 0 +set g_balance_seeker_reload_time 2 +set g_balance_seeker_switchdelay_drop 0 +set g_balance_seeker_switchdelay_raise 0 +set g_balance_seeker_tag_ammo 1 +set g_balance_seeker_tag_animtime 0.2 +set g_balance_seeker_tag_damageforcescale 4 +set g_balance_seeker_tag_health 5 +set g_balance_seeker_tag_lifetime 15 +set g_balance_seeker_tag_refire 0.75 +set g_balance_seeker_tag_speed 5000 +set g_balance_seeker_tag_spread 0 +set g_balance_seeker_tag_tracker_lifetime 10 +set g_balance_seeker_type 0 +set g_balance_seeker_weaponreplace "" +set g_balance_seeker_weaponstart 0 +set g_balance_seeker_weaponstartoverride -1 +set g_balance_seeker_weaponthrowable 1 +// }}} +// {{{ #19: Shockwave (MUTATOR WEAPON) +set g_balance_shockwave_blast_animtime 0.3 +set g_balance_shockwave_blast_damage 20 +set g_balance_shockwave_blast_distance 1000 +set g_balance_shockwave_blast_edgedamage 0 +set g_balance_shockwave_blast_force 200 +set g_balance_shockwave_blast_force_forwardbias 50 +set g_balance_shockwave_blast_force_zscale 2 +set g_balance_shockwave_blast_jump_damage 20 +set g_balance_shockwave_blast_jump_edgedamage 0 +set g_balance_shockwave_blast_jump_force 300 +set g_balance_shockwave_blast_jump_force_velocitybias 0 +set g_balance_shockwave_blast_jump_force_zscale 1.25 +set g_balance_shockwave_blast_jump_multiplier_accuracy 0.5 +set g_balance_shockwave_blast_jump_multiplier_distance 0.5 +set g_balance_shockwave_blast_jump_multiplier_min 0 +set g_balance_shockwave_blast_jump_radius 150 +set g_balance_shockwave_blast_multiplier_accuracy 0.5 +set g_balance_shockwave_blast_multiplier_distance 0.5 +set g_balance_shockwave_blast_multiplier_min 0 +set g_balance_shockwave_blast_refire 0.75 +set g_balance_shockwave_blast_splash_damage 15 +set g_balance_shockwave_blast_splash_edgedamage 0 +set g_balance_shockwave_blast_splash_force 100 +set g_balance_shockwave_blast_splash_force_forwardbias 50 +set g_balance_shockwave_blast_splash_multiplier_accuracy 0.5 +set g_balance_shockwave_blast_splash_multiplier_distance 0.5 +set g_balance_shockwave_blast_splash_multiplier_min 0 +set g_balance_shockwave_blast_splash_radius 70 +set g_balance_shockwave_blast_spread_max 120 +set g_balance_shockwave_blast_spread_min 25 +set g_balance_shockwave_melee_animtime 1.3 +set g_balance_shockwave_melee_damage 80 +set g_balance_shockwave_melee_delay 0.25 +set g_balance_shockwave_melee_force 200 +set g_balance_shockwave_melee_multihit 1 +set g_balance_shockwave_melee_no_doubleslap 1 +set g_balance_shockwave_melee_nonplayerdamage 40 +set g_balance_shockwave_melee_range 120 +set g_balance_shockwave_melee_refire 1.25 +set g_balance_shockwave_melee_swing_side 120 +set g_balance_shockwave_melee_swing_up 30 +set g_balance_shockwave_melee_time 0.15 +set g_balance_shockwave_melee_traces 10 +set g_balance_shockwave_switchdelay_drop 0 +set g_balance_shockwave_switchdelay_raise 0 +set g_balance_shockwave_weaponreplace "" +set g_balance_shockwave_weaponstart 0 +set g_balance_shockwave_weaponstartoverride -1 +set g_balance_shockwave_weaponthrowable 0 +// }}} +// {{{ #20: Arc +set g_balance_arc_beam_ammo 4 +set g_balance_arc_beam_animtime 0.2 +set g_balance_arc_beam_botaimlifetime 0 +set g_balance_arc_beam_botaimspeed 0 +set g_balance_arc_beam_damage 115 +set g_balance_arc_beam_degreespersegment 1 +set g_balance_arc_beam_distancepersegment 0 +set g_balance_arc_beam_falloff_halflifedist 0 +set g_balance_arc_beam_falloff_maxdist 0 +set g_balance_arc_beam_falloff_mindist 0 +set g_balance_arc_beam_force 900 +set g_balance_arc_beam_healing_amax 100 +set g_balance_arc_beam_healing_aps 50 +set g_balance_arc_beam_healing_hmax 150 +set g_balance_arc_beam_healing_hps 50 +set g_balance_arc_cooldown 2.5 +set g_balance_arc_overheat_max 5 +set g_balance_arc_overheat_min 3 +set g_balance_arc_beam_heat 1 +set g_balance_arc_burst_heat 5 +set g_balance_arc_beam_maxangle 10 +set g_balance_arc_beam_nonplayerdamage 80 +set g_balance_arc_beam_range 1000 +set g_balance_arc_beam_refire 0.5 +set g_balance_arc_beam_returnspeed 8 +set g_balance_arc_beam_tightness 0.5 +set g_balance_arc_burst_ammo 15 +set g_balance_arc_burst_damage 250 +set g_balance_arc_burst_healing_aps 100 +set g_balance_arc_burst_healing_hps 100 +set g_balance_arc_switchdelay_drop 0.3 +set g_balance_arc_switchdelay_raise 0.3 +set g_balance_arc_weaponreplace "" +set g_balance_arc_weaponstart 0 +set g_balance_arc_weaponstartoverride -1 +set g_balance_arc_weaponthrowable 1 +// }}} diff --git a/bal-wep-xonotic.cfg b/bal-wep-xonotic.cfg new file mode 100644 index 0000000000..320ce22804 --- /dev/null +++ b/bal-wep-xonotic.cfg @@ -0,0 +1,763 @@ +// {{{ #1: Blaster +set g_balance_blaster_primary_animtime 0.2 +set g_balance_blaster_primary_damage 25 +set g_balance_blaster_primary_delay 0 +set g_balance_blaster_primary_edgedamage 12.5 +set g_balance_blaster_primary_force 300 +set g_balance_blaster_primary_force_zscale 1.25 +set g_balance_blaster_primary_lifetime 5 +set g_balance_blaster_primary_radius 60 +set g_balance_blaster_primary_refire 0.7 +set g_balance_blaster_primary_shotangle 0 +set g_balance_blaster_primary_speed 6000 +set g_balance_blaster_primary_spread 0 +set g_balance_blaster_secondary 0 +set g_balance_blaster_secondary_animtime 0.2 +set g_balance_blaster_secondary_damage 25 +set g_balance_blaster_secondary_delay 0 +set g_balance_blaster_secondary_edgedamage 12.5 +set g_balance_blaster_secondary_force 300 +set g_balance_blaster_secondary_force_zscale 1.2 +set g_balance_blaster_secondary_lifetime 5 +set g_balance_blaster_secondary_radius 70 +set g_balance_blaster_secondary_refire 0.7 +set g_balance_blaster_secondary_shotangle 0 +set g_balance_blaster_secondary_speed 6000 +set g_balance_blaster_secondary_spread 0 +set g_balance_blaster_switchdelay_drop 0.2 +set g_balance_blaster_switchdelay_raise 0.2 +set g_balance_blaster_weaponreplace "" +set g_balance_blaster_weaponstart 1 +set g_balance_blaster_weaponstartoverride -1 +set g_balance_blaster_weaponthrowable 0 +// }}} +// {{{ #2: Shotgun +set g_balance_shotgun_primary_ammo 1 +set g_balance_shotgun_primary_animtime 0.2 +set g_balance_shotgun_primary_bullets 14 +set g_balance_shotgun_primary_damage 3.5 +set g_balance_shotgun_primary_force 15 +set g_balance_shotgun_primary_refire 0.75 +set g_balance_shotgun_primary_solidpenetration 3.8 +set g_balance_shotgun_primary_spread 0.11 +set g_balance_shotgun_reload_ammo 0 +set g_balance_shotgun_reload_time 2 +set g_balance_shotgun_secondary 1 +set g_balance_shotgun_secondary_animtime 1 +set g_balance_shotgun_secondary_damage 70 +set g_balance_shotgun_secondary_force 200 +set g_balance_shotgun_secondary_melee_delay 0.25 +set g_balance_shotgun_secondary_melee_multihit 1 +set g_balance_shotgun_secondary_melee_no_doubleslap 1 +set g_balance_shotgun_secondary_melee_nonplayerdamage 40 +set g_balance_shotgun_secondary_melee_range 120 +set g_balance_shotgun_secondary_melee_swing_side 120 +set g_balance_shotgun_secondary_melee_swing_up 30 +set g_balance_shotgun_secondary_melee_time 0.15 +set g_balance_shotgun_secondary_melee_traces 10 +set g_balance_shotgun_secondary_refire 1.25 +set g_balance_shotgun_secondary_alt_animtime 0.2 +set g_balance_shotgun_secondary_alt_refire 1.2 +set g_balance_shotgun_switchdelay_drop 0.2 +set g_balance_shotgun_switchdelay_raise 0.2 +set g_balance_shotgun_weaponreplace "" +set g_balance_shotgun_weaponstart 1 +set g_balance_shotgun_weaponstartoverride -1 +set g_balance_shotgun_weaponthrowable 1 +// }}} +// {{{ #3: Machine Gun +set g_balance_machinegun_burst 3 +set g_balance_machinegun_burst_ammo 3 +set g_balance_machinegun_burst_animtime 0.3 +set g_balance_machinegun_burst_refire 0.06 +set g_balance_machinegun_burst_refire2 0.45 +set g_balance_machinegun_burst_speed 0 +set g_balance_machinegun_first 1 +set g_balance_machinegun_first_ammo 1 +set g_balance_machinegun_first_damage 14 +set g_balance_machinegun_first_force 5 +set g_balance_machinegun_first_refire 0.125 +set g_balance_machinegun_first_spread 0.03 +set g_balance_machinegun_mode 1 +set g_balance_machinegun_reload_ammo 60 +set g_balance_machinegun_reload_time 2 +set g_balance_machinegun_solidpenetration 13.1 +set g_balance_machinegun_spread_add 0.012 +set g_balance_machinegun_spread_max 0.05 +set g_balance_machinegun_spread_min 0.02 +set g_balance_machinegun_sustained_ammo 1 +set g_balance_machinegun_sustained_damage 10 +set g_balance_machinegun_sustained_force 5 +set g_balance_machinegun_sustained_refire 0.1 +set g_balance_machinegun_sustained_spread 0.03 +set g_balance_machinegun_switchdelay_drop 0.2 +set g_balance_machinegun_switchdelay_raise 0.2 +set g_balance_machinegun_weaponreplace "" +set g_balance_machinegun_weaponstart 0 +set g_balance_machinegun_weaponstartoverride -1 +set g_balance_machinegun_weaponthrowable 1 +// }}} +// {{{ #4: Mortar +set g_balance_mortar_bouncefactor 0.5 +set g_balance_mortar_bouncestop 0.075 +set g_balance_mortar_primary_ammo 2 +set g_balance_mortar_primary_animtime 0.3 +set g_balance_mortar_primary_damage 55 +set g_balance_mortar_primary_damageforcescale 0 +set g_balance_mortar_primary_edgedamage 25 +set g_balance_mortar_primary_force 250 +set g_balance_mortar_primary_health 15 +set g_balance_mortar_primary_lifetime 5 +set g_balance_mortar_primary_lifetime_stick 0 +set g_balance_mortar_primary_radius 120 +set g_balance_mortar_primary_refire 0.8 +set g_balance_mortar_primary_remote_minbouncecnt 0 +set g_balance_mortar_primary_speed 1900 +set g_balance_mortar_primary_speed_up 225 +set g_balance_mortar_primary_speed_z 0 +set g_balance_mortar_primary_spread 0 +set g_balance_mortar_primary_type 0 +set g_balance_mortar_reload_ammo 0 +set g_balance_mortar_reload_time 2 +set g_balance_mortar_secondary_ammo 2 +set g_balance_mortar_secondary_animtime 0.3 +set g_balance_mortar_secondary_damage 55 +set g_balance_mortar_secondary_damageforcescale 4 +set g_balance_mortar_secondary_edgedamage 30 +set g_balance_mortar_secondary_force 250 +set g_balance_mortar_secondary_health 30 +set g_balance_mortar_secondary_lifetime 5 +set g_balance_mortar_secondary_lifetime_bounce 0.5 +set g_balance_mortar_secondary_lifetime_stick 0 +set g_balance_mortar_secondary_radius 120 +set g_balance_mortar_secondary_refire 0.7 +set g_balance_mortar_secondary_remote_detonateprimary 0 +set g_balance_mortar_secondary_speed 1400 +set g_balance_mortar_secondary_speed_up 150 +set g_balance_mortar_secondary_speed_z 0 +set g_balance_mortar_secondary_spread 0 +set g_balance_mortar_secondary_type 1 +set g_balance_mortar_switchdelay_drop 0.2 +set g_balance_mortar_switchdelay_raise 0.2 +set g_balance_mortar_weaponreplace "" +set g_balance_mortar_weaponstart 0 +set g_balance_mortar_weaponstartoverride -1 +set g_balance_mortar_weaponthrowable 1 +// }}} +// {{{ #5: Mine Layer (MUTATOR WEAPON) +set g_balance_minelayer_ammo 4 +set g_balance_minelayer_animtime 0.4 +set g_balance_minelayer_damage 40 +set g_balance_minelayer_damageforcescale 0 +set g_balance_minelayer_detonatedelay -1 +set g_balance_minelayer_edgedamage 20 +set g_balance_minelayer_force 250 +set g_balance_minelayer_health 15 +set g_balance_minelayer_lifetime 10 +set g_balance_minelayer_lifetime_countdown 0.5 +set g_balance_minelayer_limit 3 +set g_balance_minelayer_protection 0 +set g_balance_minelayer_proximityradius 150 +set g_balance_minelayer_radius 175 +set g_balance_minelayer_refire 1.5 +set g_balance_minelayer_reload_ammo 0 +set g_balance_minelayer_reload_time 2 +set g_balance_minelayer_remote_damage 45 +set g_balance_minelayer_remote_edgedamage 40 +set g_balance_minelayer_remote_force 300 +set g_balance_minelayer_remote_radius 200 +set g_balance_minelayer_speed 1000 +set g_balance_minelayer_switchdelay_drop 0.2 +set g_balance_minelayer_switchdelay_raise 0.2 +set g_balance_minelayer_time 0.5 +set g_balance_minelayer_weaponreplace "" +set g_balance_minelayer_weaponstart 0 +set g_balance_minelayer_weaponstartoverride -1 +set g_balance_minelayer_weaponthrowable 1 +// }}} +// {{{ #6: Electro +set g_balance_electro_combo_comboradius 300 +set g_balance_electro_combo_comboradius_thruwall 200 +set g_balance_electro_combo_damage 50 +set g_balance_electro_combo_edgedamage 25 +set g_balance_electro_combo_force 120 +set g_balance_electro_combo_radius 150 +set g_balance_electro_combo_safeammocheck 1 +set g_balance_electro_combo_speed 2000 +set g_balance_electro_primary_ammo 4 +set g_balance_electro_primary_animtime 0.3 +set g_balance_electro_primary_comboradius 300 +set g_balance_electro_primary_damage 40 +set g_balance_electro_primary_edgedamage 20 +set g_balance_electro_primary_force 200 +set g_balance_electro_primary_lifetime 5 +set g_balance_electro_primary_midaircombo_explode 1 +set g_balance_electro_primary_midaircombo_interval 0.1 +set g_balance_electro_primary_midaircombo_radius 150 +set g_balance_electro_primary_radius 100 +set g_balance_electro_primary_refire 0.6 +set g_balance_electro_primary_speed 2500 +set g_balance_electro_primary_spread 0 +set g_balance_electro_reload_ammo 0 +set g_balance_electro_reload_time 2 +set g_balance_electro_secondary_ammo 2 +set g_balance_electro_secondary_animtime 0.2 +set g_balance_electro_secondary_bouncefactor 0.3 +set g_balance_electro_secondary_bouncestop 0.05 +set g_balance_electro_secondary_count 3 +set g_balance_electro_secondary_damage 30 +set g_balance_electro_secondary_damagedbycontents 1 +set g_balance_electro_secondary_damageforcescale 4 +set g_balance_electro_secondary_edgedamage 15 +set g_balance_electro_secondary_force 50 +set g_balance_electro_secondary_health 5 +set g_balance_electro_secondary_lifetime 4 +set g_balance_electro_secondary_radius 150 +set g_balance_electro_secondary_refire 0.2 +set g_balance_electro_secondary_refire2 1.6 +set g_balance_electro_secondary_speed 1000 +set g_balance_electro_secondary_speed_up 200 +set g_balance_electro_secondary_speed_z 0 +set g_balance_electro_secondary_spread 0.04 +set g_balance_electro_secondary_touchexplode 0 +set g_balance_electro_switchdelay_drop 0.2 +set g_balance_electro_switchdelay_raise 0.2 +set g_balance_electro_weaponreplace "" +set g_balance_electro_weaponstart 0 +set g_balance_electro_weaponstartoverride -1 +set g_balance_electro_weaponthrowable 1 +// }}} +// {{{ #7: Crylink +set g_balance_crylink_primary_ammo 3 +set g_balance_crylink_primary_animtime 0.3 +set g_balance_crylink_primary_bouncedamagefactor 0.5 +set g_balance_crylink_primary_bounces 1 +set g_balance_crylink_primary_damage 12 +set g_balance_crylink_primary_edgedamage 6 +set g_balance_crylink_primary_force -50 +set g_balance_crylink_primary_joindelay 0.1 +set g_balance_crylink_primary_joinexplode 1 +set g_balance_crylink_primary_joinexplode_damage 0 +set g_balance_crylink_primary_joinexplode_edgedamage 0 +set g_balance_crylink_primary_joinexplode_force 0 +set g_balance_crylink_primary_joinexplode_radius 0 +set g_balance_crylink_primary_joinspread 0.2 +set g_balance_crylink_primary_linkexplode 1 +set g_balance_crylink_primary_middle_fadetime 5 +set g_balance_crylink_primary_middle_lifetime 5 +set g_balance_crylink_primary_other_fadetime 5 +set g_balance_crylink_primary_other_lifetime 5 +set g_balance_crylink_primary_radius 80 +set g_balance_crylink_primary_refire 0.7 +set g_balance_crylink_primary_shots 6 +set g_balance_crylink_primary_speed 2000 +set g_balance_crylink_primary_spread 0.08 +set g_balance_crylink_reload_ammo 0 +set g_balance_crylink_reload_time 2 +set g_balance_crylink_secondary 1 +set g_balance_crylink_secondary_ammo 2 +set g_balance_crylink_secondary_animtime 0.2 +set g_balance_crylink_secondary_bouncedamagefactor 0.5 +set g_balance_crylink_secondary_bounces 0 +set g_balance_crylink_secondary_damage 10 +set g_balance_crylink_secondary_edgedamage 5 +set g_balance_crylink_secondary_force -250 +set g_balance_crylink_secondary_joindelay 0 +set g_balance_crylink_secondary_joinexplode 0 +set g_balance_crylink_secondary_joinexplode_damage 0 +set g_balance_crylink_secondary_joinexplode_edgedamage 0 +set g_balance_crylink_secondary_joinexplode_force 0 +set g_balance_crylink_secondary_joinexplode_radius 0 +set g_balance_crylink_secondary_joinspread 0 +set g_balance_crylink_secondary_linkexplode 1 +set g_balance_crylink_secondary_middle_fadetime 5 +set g_balance_crylink_secondary_middle_lifetime 5 +set g_balance_crylink_secondary_other_fadetime 5 +set g_balance_crylink_secondary_other_lifetime 5 +set g_balance_crylink_secondary_radius 100 +set g_balance_crylink_secondary_refire 0.7 +set g_balance_crylink_secondary_shots 5 +set g_balance_crylink_secondary_speed 3000 +set g_balance_crylink_secondary_spread 0.01 +set g_balance_crylink_secondary_spreadtype 1 +set g_balance_crylink_switchdelay_drop 0.2 +set g_balance_crylink_switchdelay_raise 0.2 +set g_balance_crylink_weaponreplace "" +set g_balance_crylink_weaponstart 0 +set g_balance_crylink_weaponstartoverride -1 +set g_balance_crylink_weaponthrowable 1 +// }}} +// {{{ #8: Vortex +set g_balance_vortex_charge 1 +set g_balance_vortex_charge_animlimit 0.5 +set g_balance_vortex_charge_limit 1 +set g_balance_vortex_charge_maxspeed 800 +set g_balance_vortex_charge_mindmg 40 +set g_balance_vortex_charge_minspeed 400 +set g_balance_vortex_charge_rate 0.6 +set g_balance_vortex_charge_rot_pause 0 +set g_balance_vortex_charge_rot_rate 0 +set g_balance_vortex_charge_shot_multiplier 0 +set g_balance_vortex_charge_start 0.5 +set g_balance_vortex_charge_velocity_rate 0 +set g_balance_vortex_primary_ammo 6 +set g_balance_vortex_primary_animtime 0.4 +set g_balance_vortex_primary_damage 80 +set g_balance_vortex_primary_damagefalloff_forcehalflife 0 +set g_balance_vortex_primary_damagefalloff_halflife 0 +set g_balance_vortex_primary_damagefalloff_maxdist 0 +set g_balance_vortex_primary_damagefalloff_mindist 0 +set g_balance_vortex_primary_force 400 +set g_balance_vortex_primary_refire 1.5 +set g_balance_vortex_reload_ammo 0 +set g_balance_vortex_reload_time 2 +set g_balance_vortex_secondary 0 +set g_balance_vortex_secondary_ammo 2 +set g_balance_vortex_secondary_animtime 0 +set g_balance_vortex_secondary_chargepool 0 +set g_balance_vortex_secondary_chargepool_pause_regen 1 +set g_balance_vortex_secondary_chargepool_regen 0.15 +set g_balance_vortex_secondary_damage 0 +set g_balance_vortex_secondary_damagefalloff_forcehalflife 0 +set g_balance_vortex_secondary_damagefalloff_halflife 0 +set g_balance_vortex_secondary_damagefalloff_maxdist 0 +set g_balance_vortex_secondary_damagefalloff_mindist 0 +set g_balance_vortex_secondary_force 0 +set g_balance_vortex_secondary_refire 0 +set g_balance_vortex_switchdelay_drop 0.25 +set g_balance_vortex_switchdelay_raise 0.25 +set g_balance_vortex_weaponreplace "" +set g_balance_vortex_weaponstart 0 +set g_balance_vortex_weaponstartoverride -1 +set g_balance_vortex_weaponthrowable 1 +// }}} +// {{{ #9: Hagar +set g_balance_hagar_primary_ammo 1 +set g_balance_hagar_primary_damage 25 +set g_balance_hagar_primary_damageforcescale 0 +set g_balance_hagar_primary_edgedamage 12.5 +set g_balance_hagar_primary_force 100 +set g_balance_hagar_primary_health 15 +set g_balance_hagar_primary_lifetime 5 +set g_balance_hagar_primary_radius 65 +set g_balance_hagar_primary_refire 0.16667 +set g_balance_hagar_primary_speed 2200 +set g_balance_hagar_primary_spread 0.03 +set g_balance_hagar_reload_ammo 0 +set g_balance_hagar_reload_time 2 +set g_balance_hagar_secondary 1 +set g_balance_hagar_secondary_ammo 1 +set g_balance_hagar_secondary_damage 35 +set g_balance_hagar_secondary_damageforcescale 0 +set g_balance_hagar_secondary_edgedamage 17.5 +set g_balance_hagar_secondary_force 75 +set g_balance_hagar_secondary_health 15 +set g_balance_hagar_secondary_lifetime_min 10 +set g_balance_hagar_secondary_lifetime_rand 0 +set g_balance_hagar_secondary_load 1 +set g_balance_hagar_secondary_load_abort 1 +set g_balance_hagar_secondary_load_animtime 0.2 +set g_balance_hagar_secondary_load_hold 4 +set g_balance_hagar_secondary_load_linkexplode 0 +set g_balance_hagar_secondary_load_max 4 +set g_balance_hagar_secondary_load_releasedeath 0 +set g_balance_hagar_secondary_load_speed 0.5 +set g_balance_hagar_secondary_load_spread 0.075 +set g_balance_hagar_secondary_load_spread_bias 0.5 +set g_balance_hagar_secondary_radius 80 +set g_balance_hagar_secondary_refire 0.5 +set g_balance_hagar_secondary_speed 2000 +set g_balance_hagar_secondary_spread 0.05 +set g_balance_hagar_switchdelay_drop 0.2 +set g_balance_hagar_switchdelay_raise 0.2 +set g_balance_hagar_weaponreplace "" +set g_balance_hagar_weaponstart 0 +set g_balance_hagar_weaponstartoverride -1 +set g_balance_hagar_weaponthrowable 1 +// }}} +// {{{ #10: Devastator +set g_balance_devastator_ammo 4 +set g_balance_devastator_animtime 0.4 +set g_balance_devastator_damage 80 +set g_balance_devastator_damageforcescale 1 +set g_balance_devastator_detonatedelay 0.02 +set g_balance_devastator_edgedamage 40 +set g_balance_devastator_force 400 +set g_balance_devastator_guidedelay 0.2 +set g_balance_devastator_guidegoal 512 +set g_balance_devastator_guiderate 90 +set g_balance_devastator_guideratedelay 0.01 +set g_balance_devastator_guidestop 0 +set g_balance_devastator_health 30 +set g_balance_devastator_lifetime 10 +set g_balance_devastator_radius 110 +set g_balance_devastator_refire 1.1 +set g_balance_devastator_reload_ammo 0 +set g_balance_devastator_reload_time 2 +set g_balance_devastator_remote_damage 70 +set g_balance_devastator_remote_edgedamage 35 +set g_balance_devastator_remote_force 300 +set g_balance_devastator_remote_jump_damage 70 +set g_balance_devastator_remote_jump_radius 0 +set g_balance_devastator_remote_jump_velocity_z_add 400 +set g_balance_devastator_remote_jump_velocity_z_max 1500 +set g_balance_devastator_remote_jump_velocity_z_min 400 +set g_balance_devastator_remote_radius 110 +set g_balance_devastator_speed 1300 +set g_balance_devastator_speedaccel 1300 +set g_balance_devastator_speedstart 1000 +set g_balance_devastator_switchdelay_drop 0.2 +set g_balance_devastator_switchdelay_raise 0.2 +set g_balance_devastator_weaponreplace "" +set g_balance_devastator_weaponstart 0 +set g_balance_devastator_weaponstartoverride -1 +set g_balance_devastator_weaponthrowable 1 +// }}} +// {{{ #11: Port-O-Launch +set g_balance_porto_primary_animtime 0.3 +set g_balance_porto_primary_lifetime 5 +set g_balance_porto_primary_refire 1.5 +set g_balance_porto_primary_speed 1000 +set g_balance_porto_secondary 1 +set g_balance_porto_secondary_animtime 0.3 +set g_balance_porto_secondary_lifetime 5 +set g_balance_porto_secondary_refire 1.5 +set g_balance_porto_secondary_speed 1000 +set g_balance_porto_switchdelay_drop 0.2 +set g_balance_porto_switchdelay_raise 0.2 +set g_balance_porto_weaponreplace "" +set g_balance_porto_weaponstart 0 +set g_balance_porto_weaponstartoverride -1 +set g_balance_porto_weaponthrowable 1 +// }}} +// {{{ #12: Vaporizer +set g_balance_vaporizer_primary_ammo 10 +set g_balance_vaporizer_primary_animtime 0.3 +set g_balance_vaporizer_primary_refire 1 +set g_balance_vaporizer_reload_ammo 0 +set g_balance_vaporizer_reload_time 0 +set g_balance_vaporizer_secondary_ammo 0 +set g_balance_vaporizer_secondary_animtime 0.2 +set g_balance_vaporizer_secondary_damage 25 +set g_balance_vaporizer_secondary_delay 0 +set g_balance_vaporizer_secondary_edgedamage 12.5 +set g_balance_vaporizer_secondary_force 400 +set g_balance_vaporizer_secondary_lifetime 5 +set g_balance_vaporizer_secondary_radius 70 +set g_balance_vaporizer_secondary_refire 0.7 +set g_balance_vaporizer_secondary_shotangle 0 +set g_balance_vaporizer_secondary_speed 6000 +set g_balance_vaporizer_secondary_spread 0 +set g_balance_vaporizer_switchdelay_drop 0.2 +set g_balance_vaporizer_switchdelay_raise 0.2 +set g_balance_vaporizer_weaponreplace "" +set g_balance_vaporizer_weaponstart 0 +set g_balance_vaporizer_weaponstartoverride -1 +set g_balance_vaporizer_weaponthrowable 0 +// }}} +// {{{ #13: Grappling Hook +set g_balance_hook_primary_ammo 5 +set g_balance_hook_primary_animtime 0.3 +set g_balance_hook_primary_hooked_ammo 5 +set g_balance_hook_primary_hooked_time_free 2 +set g_balance_hook_primary_hooked_time_max 0 +set g_balance_hook_primary_refire 0.2 +set g_balance_hook_secondary_animtime 0.3 +set g_balance_hook_secondary_damage 25 +set g_balance_hook_secondary_damageforcescale 0 +set g_balance_hook_secondary_duration 1.5 +set g_balance_hook_secondary_edgedamage 5 +set g_balance_hook_secondary_force -2000 +set g_balance_hook_secondary_gravity 5 +set g_balance_hook_secondary_health 15 +set g_balance_hook_secondary_lifetime 5 +set g_balance_hook_secondary_power 3 +set g_balance_hook_secondary_radius 500 +set g_balance_hook_secondary_refire 3 +set g_balance_hook_secondary_speed 0 +set g_balance_hook_switchdelay_drop 0.2 +set g_balance_hook_switchdelay_raise 0.2 +set g_balance_hook_weaponreplace "" +set g_balance_hook_weaponstart 0 +set g_balance_hook_weaponstartoverride -1 +set g_balance_hook_weaponthrowable 1 +// }}} +// {{{ #14: Heavy Laser Assault Cannon (MUTATOR WEAPON) +set g_balance_hlac_primary_ammo 1 +set g_balance_hlac_primary_animtime 0.4 +set g_balance_hlac_primary_damage 18 +set g_balance_hlac_primary_edgedamage 9 +set g_balance_hlac_primary_force 90 +set g_balance_hlac_primary_lifetime 5 +set g_balance_hlac_primary_radius 70 +set g_balance_hlac_primary_refire 0.15 +set g_balance_hlac_primary_speed 9000 +set g_balance_hlac_primary_spread_add 0.0045 +set g_balance_hlac_primary_spread_crouchmod 0.25 +set g_balance_hlac_primary_spread_max 0.25 +set g_balance_hlac_primary_spread_min 0.01 +set g_balance_hlac_reload_ammo 0 +set g_balance_hlac_reload_time 2 +set g_balance_hlac_secondary 1 +set g_balance_hlac_secondary_ammo 10 +set g_balance_hlac_secondary_animtime 0.3 +set g_balance_hlac_secondary_damage 15 +set g_balance_hlac_secondary_edgedamage 7.5 +set g_balance_hlac_secondary_force 90 +set g_balance_hlac_secondary_lifetime 5 +set g_balance_hlac_secondary_radius 70 +set g_balance_hlac_secondary_refire 1 +set g_balance_hlac_secondary_shots 6 +set g_balance_hlac_secondary_speed 9000 +set g_balance_hlac_secondary_spread 0.15 +set g_balance_hlac_secondary_spread_crouchmod 0.5 +set g_balance_hlac_switchdelay_drop 0.2 +set g_balance_hlac_switchdelay_raise 0.2 +set g_balance_hlac_weaponreplace "" +set g_balance_hlac_weaponstart 0 +set g_balance_hlac_weaponstartoverride -1 +set g_balance_hlac_weaponthrowable 1 +// }}} +// {{{ #15: @!#%'n Tuba +set g_balance_tuba_animtime 0.05 +set g_balance_tuba_attenuation 0.5 +set g_balance_tuba_damage 5 +set g_balance_tuba_edgedamage 0 +set g_balance_tuba_fadetime 0.25 +set g_balance_tuba_force 40 +set g_balance_tuba_pitchstep 6 +set g_balance_tuba_radius 200 +set g_balance_tuba_refire 0.05 +set g_balance_tuba_switchdelay_drop 0.2 +set g_balance_tuba_switchdelay_raise 0.2 +set g_balance_tuba_volume 1 +set g_balance_tuba_weaponreplace "" +set g_balance_tuba_weaponstart 0 +set g_balance_tuba_weaponstartoverride -1 +set g_balance_tuba_weaponthrowable 1 +// }}} +// {{{ #16: Rifle (MUTATOR WEAPON) +set g_balance_rifle_bursttime 0 +set g_balance_rifle_primary_ammo 10 +set g_balance_rifle_primary_animtime 0.4 +set g_balance_rifle_primary_bullethail 0 +set g_balance_rifle_primary_burstcost 0 +set g_balance_rifle_primary_damage 80 +set g_balance_rifle_primary_force 100 +set g_balance_rifle_primary_refire 1.2 +set g_balance_rifle_primary_shots 1 +set g_balance_rifle_primary_solidpenetration 62.2 +set g_balance_rifle_primary_spread 0 +set g_balance_rifle_primary_tracer 1 +set g_balance_rifle_reload_ammo 80 +set g_balance_rifle_reload_time 2 +set g_balance_rifle_secondary 1 +set g_balance_rifle_secondary_ammo 10 +set g_balance_rifle_secondary_animtime 0.3 +set g_balance_rifle_secondary_bullethail 0 +set g_balance_rifle_secondary_burstcost 0 +set g_balance_rifle_secondary_damage 20 +set g_balance_rifle_secondary_force 50 +set g_balance_rifle_secondary_refire 0.9 +set g_balance_rifle_secondary_reload 0 +set g_balance_rifle_secondary_shots 4 +set g_balance_rifle_secondary_solidpenetration 15.5 +set g_balance_rifle_secondary_spread 0.04 +set g_balance_rifle_secondary_tracer 0 +set g_balance_rifle_switchdelay_drop 0.2 +set g_balance_rifle_switchdelay_raise 0.2 +set g_balance_rifle_weaponreplace "" +set g_balance_rifle_weaponstart 0 +set g_balance_rifle_weaponstartoverride -1 +set g_balance_rifle_weaponthrowable 1 +// }}} +// {{{ #17: Fireball +set g_balance_fireball_primary_animtime 0.4 +set g_balance_fireball_primary_bfgdamage 100 +set g_balance_fireball_primary_bfgforce 0 +set g_balance_fireball_primary_bfgradius 1000 +set g_balance_fireball_primary_damage 200 +set g_balance_fireball_primary_damageforcescale 0 +set g_balance_fireball_primary_edgedamage 50 +set g_balance_fireball_primary_force 600 +set g_balance_fireball_primary_health 0 +set g_balance_fireball_primary_laserburntime 0.5 +set g_balance_fireball_primary_laserdamage 80 +set g_balance_fireball_primary_laseredgedamage 20 +set g_balance_fireball_primary_laserradius 256 +set g_balance_fireball_primary_lifetime 15 +set g_balance_fireball_primary_radius 200 +set g_balance_fireball_primary_refire 2 +set g_balance_fireball_primary_refire2 0 +set g_balance_fireball_primary_speed 1200 +set g_balance_fireball_primary_spread 0 +set g_balance_fireball_secondary_animtime 0.3 +set g_balance_fireball_secondary_damage 40 +set g_balance_fireball_secondary_damageforcescale 4 +set g_balance_fireball_secondary_damagetime 5 +set g_balance_fireball_secondary_laserburntime 0.5 +set g_balance_fireball_secondary_laserdamage 50 +set g_balance_fireball_secondary_laseredgedamage 20 +set g_balance_fireball_secondary_laserradius 110 +set g_balance_fireball_secondary_lifetime 7 +set g_balance_fireball_secondary_refire 1.5 +set g_balance_fireball_secondary_speed 900 +set g_balance_fireball_secondary_speed_up 100 +set g_balance_fireball_secondary_speed_z 0 +set g_balance_fireball_secondary_spread 0 +set g_balance_fireball_switchdelay_drop 0.2 +set g_balance_fireball_switchdelay_raise 0.2 +set g_balance_fireball_weaponreplace "" +set g_balance_fireball_weaponstart 0 +set g_balance_fireball_weaponstartoverride -1 +set g_balance_fireball_weaponthrowable 0 +// }}} +// {{{ #18: T.A.G. Seeker (MUTATOR WEAPON) +set g_balance_seeker_flac_ammo 1 +set g_balance_seeker_flac_animtime 0.1 +set g_balance_seeker_flac_damage 15 +set g_balance_seeker_flac_edgedamage 10 +set g_balance_seeker_flac_force 50 +set g_balance_seeker_flac_lifetime 0.1 +set g_balance_seeker_flac_lifetime_rand 0.05 +set g_balance_seeker_flac_radius 100 +set g_balance_seeker_flac_refire 0.1 +set g_balance_seeker_flac_speed 3000 +set g_balance_seeker_flac_speed_up 1000 +set g_balance_seeker_flac_speed_z 0 +set g_balance_seeker_flac_spread 0.4 +set g_balance_seeker_missile_accel 1400 +set g_balance_seeker_missile_ammo 2 +set g_balance_seeker_missile_animtime 0.2 +set g_balance_seeker_missile_count 3 +set g_balance_seeker_missile_damage 30 +set g_balance_seeker_missile_damageforcescale 4 +set g_balance_seeker_missile_decel 1400 +set g_balance_seeker_missile_delay 0.25 +set g_balance_seeker_missile_edgedamage 10 +set g_balance_seeker_missile_force 150 +set g_balance_seeker_missile_health 5 +set g_balance_seeker_missile_lifetime 15 +set g_balance_seeker_missile_proxy 0 +set g_balance_seeker_missile_proxy_delay 0.2 +set g_balance_seeker_missile_proxy_maxrange 45 +set g_balance_seeker_missile_radius 80 +set g_balance_seeker_missile_refire 0.5 +set g_balance_seeker_missile_smart 1 +set g_balance_seeker_missile_smart_mindist 800 +set g_balance_seeker_missile_smart_trace_max 2500 +set g_balance_seeker_missile_smart_trace_min 1000 +set g_balance_seeker_missile_speed 700 +set g_balance_seeker_missile_speed_max 1300 +set g_balance_seeker_missile_speed_up 300 +set g_balance_seeker_missile_speed_z 0 +set g_balance_seeker_missile_spread 0 +set g_balance_seeker_missile_turnrate 0.65 +set g_balance_seeker_reload_ammo 0 +set g_balance_seeker_reload_time 2 +set g_balance_seeker_switchdelay_drop 0.2 +set g_balance_seeker_switchdelay_raise 0.2 +set g_balance_seeker_tag_ammo 1 +set g_balance_seeker_tag_animtime 0.2 +set g_balance_seeker_tag_damageforcescale 4 +set g_balance_seeker_tag_health 5 +set g_balance_seeker_tag_lifetime 15 +set g_balance_seeker_tag_refire 0.75 +set g_balance_seeker_tag_speed 5000 +set g_balance_seeker_tag_spread 0 +set g_balance_seeker_tag_tracker_lifetime 10 +set g_balance_seeker_type 0 +set g_balance_seeker_weaponreplace "" +set g_balance_seeker_weaponstart 0 +set g_balance_seeker_weaponstartoverride -1 +set g_balance_seeker_weaponthrowable 1 +// }}} +// {{{ #19: Shockwave (MUTATOR WEAPON) +set g_balance_shockwave_blast_animtime 0.3 +set g_balance_shockwave_blast_damage 20 +set g_balance_shockwave_blast_distance 1000 +set g_balance_shockwave_blast_edgedamage 0 +set g_balance_shockwave_blast_force 200 +set g_balance_shockwave_blast_force_forwardbias 50 +set g_balance_shockwave_blast_force_zscale 2 +set g_balance_shockwave_blast_jump_damage 20 +set g_balance_shockwave_blast_jump_edgedamage 0 +set g_balance_shockwave_blast_jump_force 300 +set g_balance_shockwave_blast_jump_force_velocitybias 0 +set g_balance_shockwave_blast_jump_force_zscale 1.25 +set g_balance_shockwave_blast_jump_multiplier_accuracy 0.5 +set g_balance_shockwave_blast_jump_multiplier_distance 0.5 +set g_balance_shockwave_blast_jump_multiplier_min 0 +set g_balance_shockwave_blast_jump_radius 150 +set g_balance_shockwave_blast_multiplier_accuracy 0.5 +set g_balance_shockwave_blast_multiplier_distance 0.5 +set g_balance_shockwave_blast_multiplier_min 0 +set g_balance_shockwave_blast_refire 0.75 +set g_balance_shockwave_blast_splash_damage 15 +set g_balance_shockwave_blast_splash_edgedamage 0 +set g_balance_shockwave_blast_splash_force 100 +set g_balance_shockwave_blast_splash_force_forwardbias 50 +set g_balance_shockwave_blast_splash_multiplier_accuracy 0.5 +set g_balance_shockwave_blast_splash_multiplier_distance 0.5 +set g_balance_shockwave_blast_splash_multiplier_min 0 +set g_balance_shockwave_blast_splash_radius 70 +set g_balance_shockwave_blast_spread_max 120 +set g_balance_shockwave_blast_spread_min 25 +set g_balance_shockwave_melee_animtime 1.3 +set g_balance_shockwave_melee_damage 80 +set g_balance_shockwave_melee_delay 0.25 +set g_balance_shockwave_melee_force 200 +set g_balance_shockwave_melee_multihit 1 +set g_balance_shockwave_melee_no_doubleslap 1 +set g_balance_shockwave_melee_nonplayerdamage 40 +set g_balance_shockwave_melee_range 120 +set g_balance_shockwave_melee_refire 1.25 +set g_balance_shockwave_melee_swing_side 120 +set g_balance_shockwave_melee_swing_up 30 +set g_balance_shockwave_melee_time 0.15 +set g_balance_shockwave_melee_traces 10 +set g_balance_shockwave_switchdelay_drop 0.2 +set g_balance_shockwave_switchdelay_raise 0.2 +set g_balance_shockwave_weaponreplace "" +set g_balance_shockwave_weaponstart 0 +set g_balance_shockwave_weaponstartoverride -1 +set g_balance_shockwave_weaponthrowable 0 +// }}} +// {{{ #20: Arc +set g_balance_arc_beam_ammo 4 +set g_balance_arc_beam_animtime 0.2 +set g_balance_arc_beam_botaimlifetime 0 +set g_balance_arc_beam_botaimspeed 0 +set g_balance_arc_beam_damage 115 +set g_balance_arc_beam_degreespersegment 1 +set g_balance_arc_beam_distancepersegment 0 +set g_balance_arc_beam_falloff_halflifedist 0 +set g_balance_arc_beam_falloff_maxdist 0 +set g_balance_arc_beam_falloff_mindist 0 +set g_balance_arc_beam_force 900 +set g_balance_arc_beam_healing_amax 100 +set g_balance_arc_beam_healing_aps 50 +set g_balance_arc_beam_healing_hmax 150 +set g_balance_arc_beam_healing_hps 50 +set g_balance_arc_cooldown 2.5 +set g_balance_arc_overheat_max 5 +set g_balance_arc_overheat_min 3 +set g_balance_arc_beam_heat 1 +set g_balance_arc_burst_heat 4 +set g_balance_arc_beam_maxangle 10 +set g_balance_arc_beam_nonplayerdamage 80 +set g_balance_arc_beam_range 1000 +set g_balance_arc_beam_refire 0.5 +set g_balance_arc_beam_returnspeed 8 +set g_balance_arc_beam_tightness 0.5 +set g_balance_arc_burst_ammo 15 +set g_balance_arc_burst_damage 250 +set g_balance_arc_burst_healing_aps 100 +set g_balance_arc_burst_healing_hps 100 +set g_balance_arc_switchdelay_drop 0.3 +set g_balance_arc_switchdelay_raise 0.3 +set g_balance_arc_weaponreplace "" +set g_balance_arc_weaponstart 0 +set g_balance_arc_weaponstartoverride -1 +set g_balance_arc_weaponthrowable 1 +// }}} diff --git a/bal-wep-xpm.cfg b/bal-wep-xpm.cfg new file mode 100644 index 0000000000..320ce22804 --- /dev/null +++ b/bal-wep-xpm.cfg @@ -0,0 +1,763 @@ +// {{{ #1: Blaster +set g_balance_blaster_primary_animtime 0.2 +set g_balance_blaster_primary_damage 25 +set g_balance_blaster_primary_delay 0 +set g_balance_blaster_primary_edgedamage 12.5 +set g_balance_blaster_primary_force 300 +set g_balance_blaster_primary_force_zscale 1.25 +set g_balance_blaster_primary_lifetime 5 +set g_balance_blaster_primary_radius 60 +set g_balance_blaster_primary_refire 0.7 +set g_balance_blaster_primary_shotangle 0 +set g_balance_blaster_primary_speed 6000 +set g_balance_blaster_primary_spread 0 +set g_balance_blaster_secondary 0 +set g_balance_blaster_secondary_animtime 0.2 +set g_balance_blaster_secondary_damage 25 +set g_balance_blaster_secondary_delay 0 +set g_balance_blaster_secondary_edgedamage 12.5 +set g_balance_blaster_secondary_force 300 +set g_balance_blaster_secondary_force_zscale 1.2 +set g_balance_blaster_secondary_lifetime 5 +set g_balance_blaster_secondary_radius 70 +set g_balance_blaster_secondary_refire 0.7 +set g_balance_blaster_secondary_shotangle 0 +set g_balance_blaster_secondary_speed 6000 +set g_balance_blaster_secondary_spread 0 +set g_balance_blaster_switchdelay_drop 0.2 +set g_balance_blaster_switchdelay_raise 0.2 +set g_balance_blaster_weaponreplace "" +set g_balance_blaster_weaponstart 1 +set g_balance_blaster_weaponstartoverride -1 +set g_balance_blaster_weaponthrowable 0 +// }}} +// {{{ #2: Shotgun +set g_balance_shotgun_primary_ammo 1 +set g_balance_shotgun_primary_animtime 0.2 +set g_balance_shotgun_primary_bullets 14 +set g_balance_shotgun_primary_damage 3.5 +set g_balance_shotgun_primary_force 15 +set g_balance_shotgun_primary_refire 0.75 +set g_balance_shotgun_primary_solidpenetration 3.8 +set g_balance_shotgun_primary_spread 0.11 +set g_balance_shotgun_reload_ammo 0 +set g_balance_shotgun_reload_time 2 +set g_balance_shotgun_secondary 1 +set g_balance_shotgun_secondary_animtime 1 +set g_balance_shotgun_secondary_damage 70 +set g_balance_shotgun_secondary_force 200 +set g_balance_shotgun_secondary_melee_delay 0.25 +set g_balance_shotgun_secondary_melee_multihit 1 +set g_balance_shotgun_secondary_melee_no_doubleslap 1 +set g_balance_shotgun_secondary_melee_nonplayerdamage 40 +set g_balance_shotgun_secondary_melee_range 120 +set g_balance_shotgun_secondary_melee_swing_side 120 +set g_balance_shotgun_secondary_melee_swing_up 30 +set g_balance_shotgun_secondary_melee_time 0.15 +set g_balance_shotgun_secondary_melee_traces 10 +set g_balance_shotgun_secondary_refire 1.25 +set g_balance_shotgun_secondary_alt_animtime 0.2 +set g_balance_shotgun_secondary_alt_refire 1.2 +set g_balance_shotgun_switchdelay_drop 0.2 +set g_balance_shotgun_switchdelay_raise 0.2 +set g_balance_shotgun_weaponreplace "" +set g_balance_shotgun_weaponstart 1 +set g_balance_shotgun_weaponstartoverride -1 +set g_balance_shotgun_weaponthrowable 1 +// }}} +// {{{ #3: Machine Gun +set g_balance_machinegun_burst 3 +set g_balance_machinegun_burst_ammo 3 +set g_balance_machinegun_burst_animtime 0.3 +set g_balance_machinegun_burst_refire 0.06 +set g_balance_machinegun_burst_refire2 0.45 +set g_balance_machinegun_burst_speed 0 +set g_balance_machinegun_first 1 +set g_balance_machinegun_first_ammo 1 +set g_balance_machinegun_first_damage 14 +set g_balance_machinegun_first_force 5 +set g_balance_machinegun_first_refire 0.125 +set g_balance_machinegun_first_spread 0.03 +set g_balance_machinegun_mode 1 +set g_balance_machinegun_reload_ammo 60 +set g_balance_machinegun_reload_time 2 +set g_balance_machinegun_solidpenetration 13.1 +set g_balance_machinegun_spread_add 0.012 +set g_balance_machinegun_spread_max 0.05 +set g_balance_machinegun_spread_min 0.02 +set g_balance_machinegun_sustained_ammo 1 +set g_balance_machinegun_sustained_damage 10 +set g_balance_machinegun_sustained_force 5 +set g_balance_machinegun_sustained_refire 0.1 +set g_balance_machinegun_sustained_spread 0.03 +set g_balance_machinegun_switchdelay_drop 0.2 +set g_balance_machinegun_switchdelay_raise 0.2 +set g_balance_machinegun_weaponreplace "" +set g_balance_machinegun_weaponstart 0 +set g_balance_machinegun_weaponstartoverride -1 +set g_balance_machinegun_weaponthrowable 1 +// }}} +// {{{ #4: Mortar +set g_balance_mortar_bouncefactor 0.5 +set g_balance_mortar_bouncestop 0.075 +set g_balance_mortar_primary_ammo 2 +set g_balance_mortar_primary_animtime 0.3 +set g_balance_mortar_primary_damage 55 +set g_balance_mortar_primary_damageforcescale 0 +set g_balance_mortar_primary_edgedamage 25 +set g_balance_mortar_primary_force 250 +set g_balance_mortar_primary_health 15 +set g_balance_mortar_primary_lifetime 5 +set g_balance_mortar_primary_lifetime_stick 0 +set g_balance_mortar_primary_radius 120 +set g_balance_mortar_primary_refire 0.8 +set g_balance_mortar_primary_remote_minbouncecnt 0 +set g_balance_mortar_primary_speed 1900 +set g_balance_mortar_primary_speed_up 225 +set g_balance_mortar_primary_speed_z 0 +set g_balance_mortar_primary_spread 0 +set g_balance_mortar_primary_type 0 +set g_balance_mortar_reload_ammo 0 +set g_balance_mortar_reload_time 2 +set g_balance_mortar_secondary_ammo 2 +set g_balance_mortar_secondary_animtime 0.3 +set g_balance_mortar_secondary_damage 55 +set g_balance_mortar_secondary_damageforcescale 4 +set g_balance_mortar_secondary_edgedamage 30 +set g_balance_mortar_secondary_force 250 +set g_balance_mortar_secondary_health 30 +set g_balance_mortar_secondary_lifetime 5 +set g_balance_mortar_secondary_lifetime_bounce 0.5 +set g_balance_mortar_secondary_lifetime_stick 0 +set g_balance_mortar_secondary_radius 120 +set g_balance_mortar_secondary_refire 0.7 +set g_balance_mortar_secondary_remote_detonateprimary 0 +set g_balance_mortar_secondary_speed 1400 +set g_balance_mortar_secondary_speed_up 150 +set g_balance_mortar_secondary_speed_z 0 +set g_balance_mortar_secondary_spread 0 +set g_balance_mortar_secondary_type 1 +set g_balance_mortar_switchdelay_drop 0.2 +set g_balance_mortar_switchdelay_raise 0.2 +set g_balance_mortar_weaponreplace "" +set g_balance_mortar_weaponstart 0 +set g_balance_mortar_weaponstartoverride -1 +set g_balance_mortar_weaponthrowable 1 +// }}} +// {{{ #5: Mine Layer (MUTATOR WEAPON) +set g_balance_minelayer_ammo 4 +set g_balance_minelayer_animtime 0.4 +set g_balance_minelayer_damage 40 +set g_balance_minelayer_damageforcescale 0 +set g_balance_minelayer_detonatedelay -1 +set g_balance_minelayer_edgedamage 20 +set g_balance_minelayer_force 250 +set g_balance_minelayer_health 15 +set g_balance_minelayer_lifetime 10 +set g_balance_minelayer_lifetime_countdown 0.5 +set g_balance_minelayer_limit 3 +set g_balance_minelayer_protection 0 +set g_balance_minelayer_proximityradius 150 +set g_balance_minelayer_radius 175 +set g_balance_minelayer_refire 1.5 +set g_balance_minelayer_reload_ammo 0 +set g_balance_minelayer_reload_time 2 +set g_balance_minelayer_remote_damage 45 +set g_balance_minelayer_remote_edgedamage 40 +set g_balance_minelayer_remote_force 300 +set g_balance_minelayer_remote_radius 200 +set g_balance_minelayer_speed 1000 +set g_balance_minelayer_switchdelay_drop 0.2 +set g_balance_minelayer_switchdelay_raise 0.2 +set g_balance_minelayer_time 0.5 +set g_balance_minelayer_weaponreplace "" +set g_balance_minelayer_weaponstart 0 +set g_balance_minelayer_weaponstartoverride -1 +set g_balance_minelayer_weaponthrowable 1 +// }}} +// {{{ #6: Electro +set g_balance_electro_combo_comboradius 300 +set g_balance_electro_combo_comboradius_thruwall 200 +set g_balance_electro_combo_damage 50 +set g_balance_electro_combo_edgedamage 25 +set g_balance_electro_combo_force 120 +set g_balance_electro_combo_radius 150 +set g_balance_electro_combo_safeammocheck 1 +set g_balance_electro_combo_speed 2000 +set g_balance_electro_primary_ammo 4 +set g_balance_electro_primary_animtime 0.3 +set g_balance_electro_primary_comboradius 300 +set g_balance_electro_primary_damage 40 +set g_balance_electro_primary_edgedamage 20 +set g_balance_electro_primary_force 200 +set g_balance_electro_primary_lifetime 5 +set g_balance_electro_primary_midaircombo_explode 1 +set g_balance_electro_primary_midaircombo_interval 0.1 +set g_balance_electro_primary_midaircombo_radius 150 +set g_balance_electro_primary_radius 100 +set g_balance_electro_primary_refire 0.6 +set g_balance_electro_primary_speed 2500 +set g_balance_electro_primary_spread 0 +set g_balance_electro_reload_ammo 0 +set g_balance_electro_reload_time 2 +set g_balance_electro_secondary_ammo 2 +set g_balance_electro_secondary_animtime 0.2 +set g_balance_electro_secondary_bouncefactor 0.3 +set g_balance_electro_secondary_bouncestop 0.05 +set g_balance_electro_secondary_count 3 +set g_balance_electro_secondary_damage 30 +set g_balance_electro_secondary_damagedbycontents 1 +set g_balance_electro_secondary_damageforcescale 4 +set g_balance_electro_secondary_edgedamage 15 +set g_balance_electro_secondary_force 50 +set g_balance_electro_secondary_health 5 +set g_balance_electro_secondary_lifetime 4 +set g_balance_electro_secondary_radius 150 +set g_balance_electro_secondary_refire 0.2 +set g_balance_electro_secondary_refire2 1.6 +set g_balance_electro_secondary_speed 1000 +set g_balance_electro_secondary_speed_up 200 +set g_balance_electro_secondary_speed_z 0 +set g_balance_electro_secondary_spread 0.04 +set g_balance_electro_secondary_touchexplode 0 +set g_balance_electro_switchdelay_drop 0.2 +set g_balance_electro_switchdelay_raise 0.2 +set g_balance_electro_weaponreplace "" +set g_balance_electro_weaponstart 0 +set g_balance_electro_weaponstartoverride -1 +set g_balance_electro_weaponthrowable 1 +// }}} +// {{{ #7: Crylink +set g_balance_crylink_primary_ammo 3 +set g_balance_crylink_primary_animtime 0.3 +set g_balance_crylink_primary_bouncedamagefactor 0.5 +set g_balance_crylink_primary_bounces 1 +set g_balance_crylink_primary_damage 12 +set g_balance_crylink_primary_edgedamage 6 +set g_balance_crylink_primary_force -50 +set g_balance_crylink_primary_joindelay 0.1 +set g_balance_crylink_primary_joinexplode 1 +set g_balance_crylink_primary_joinexplode_damage 0 +set g_balance_crylink_primary_joinexplode_edgedamage 0 +set g_balance_crylink_primary_joinexplode_force 0 +set g_balance_crylink_primary_joinexplode_radius 0 +set g_balance_crylink_primary_joinspread 0.2 +set g_balance_crylink_primary_linkexplode 1 +set g_balance_crylink_primary_middle_fadetime 5 +set g_balance_crylink_primary_middle_lifetime 5 +set g_balance_crylink_primary_other_fadetime 5 +set g_balance_crylink_primary_other_lifetime 5 +set g_balance_crylink_primary_radius 80 +set g_balance_crylink_primary_refire 0.7 +set g_balance_crylink_primary_shots 6 +set g_balance_crylink_primary_speed 2000 +set g_balance_crylink_primary_spread 0.08 +set g_balance_crylink_reload_ammo 0 +set g_balance_crylink_reload_time 2 +set g_balance_crylink_secondary 1 +set g_balance_crylink_secondary_ammo 2 +set g_balance_crylink_secondary_animtime 0.2 +set g_balance_crylink_secondary_bouncedamagefactor 0.5 +set g_balance_crylink_secondary_bounces 0 +set g_balance_crylink_secondary_damage 10 +set g_balance_crylink_secondary_edgedamage 5 +set g_balance_crylink_secondary_force -250 +set g_balance_crylink_secondary_joindelay 0 +set g_balance_crylink_secondary_joinexplode 0 +set g_balance_crylink_secondary_joinexplode_damage 0 +set g_balance_crylink_secondary_joinexplode_edgedamage 0 +set g_balance_crylink_secondary_joinexplode_force 0 +set g_balance_crylink_secondary_joinexplode_radius 0 +set g_balance_crylink_secondary_joinspread 0 +set g_balance_crylink_secondary_linkexplode 1 +set g_balance_crylink_secondary_middle_fadetime 5 +set g_balance_crylink_secondary_middle_lifetime 5 +set g_balance_crylink_secondary_other_fadetime 5 +set g_balance_crylink_secondary_other_lifetime 5 +set g_balance_crylink_secondary_radius 100 +set g_balance_crylink_secondary_refire 0.7 +set g_balance_crylink_secondary_shots 5 +set g_balance_crylink_secondary_speed 3000 +set g_balance_crylink_secondary_spread 0.01 +set g_balance_crylink_secondary_spreadtype 1 +set g_balance_crylink_switchdelay_drop 0.2 +set g_balance_crylink_switchdelay_raise 0.2 +set g_balance_crylink_weaponreplace "" +set g_balance_crylink_weaponstart 0 +set g_balance_crylink_weaponstartoverride -1 +set g_balance_crylink_weaponthrowable 1 +// }}} +// {{{ #8: Vortex +set g_balance_vortex_charge 1 +set g_balance_vortex_charge_animlimit 0.5 +set g_balance_vortex_charge_limit 1 +set g_balance_vortex_charge_maxspeed 800 +set g_balance_vortex_charge_mindmg 40 +set g_balance_vortex_charge_minspeed 400 +set g_balance_vortex_charge_rate 0.6 +set g_balance_vortex_charge_rot_pause 0 +set g_balance_vortex_charge_rot_rate 0 +set g_balance_vortex_charge_shot_multiplier 0 +set g_balance_vortex_charge_start 0.5 +set g_balance_vortex_charge_velocity_rate 0 +set g_balance_vortex_primary_ammo 6 +set g_balance_vortex_primary_animtime 0.4 +set g_balance_vortex_primary_damage 80 +set g_balance_vortex_primary_damagefalloff_forcehalflife 0 +set g_balance_vortex_primary_damagefalloff_halflife 0 +set g_balance_vortex_primary_damagefalloff_maxdist 0 +set g_balance_vortex_primary_damagefalloff_mindist 0 +set g_balance_vortex_primary_force 400 +set g_balance_vortex_primary_refire 1.5 +set g_balance_vortex_reload_ammo 0 +set g_balance_vortex_reload_time 2 +set g_balance_vortex_secondary 0 +set g_balance_vortex_secondary_ammo 2 +set g_balance_vortex_secondary_animtime 0 +set g_balance_vortex_secondary_chargepool 0 +set g_balance_vortex_secondary_chargepool_pause_regen 1 +set g_balance_vortex_secondary_chargepool_regen 0.15 +set g_balance_vortex_secondary_damage 0 +set g_balance_vortex_secondary_damagefalloff_forcehalflife 0 +set g_balance_vortex_secondary_damagefalloff_halflife 0 +set g_balance_vortex_secondary_damagefalloff_maxdist 0 +set g_balance_vortex_secondary_damagefalloff_mindist 0 +set g_balance_vortex_secondary_force 0 +set g_balance_vortex_secondary_refire 0 +set g_balance_vortex_switchdelay_drop 0.25 +set g_balance_vortex_switchdelay_raise 0.25 +set g_balance_vortex_weaponreplace "" +set g_balance_vortex_weaponstart 0 +set g_balance_vortex_weaponstartoverride -1 +set g_balance_vortex_weaponthrowable 1 +// }}} +// {{{ #9: Hagar +set g_balance_hagar_primary_ammo 1 +set g_balance_hagar_primary_damage 25 +set g_balance_hagar_primary_damageforcescale 0 +set g_balance_hagar_primary_edgedamage 12.5 +set g_balance_hagar_primary_force 100 +set g_balance_hagar_primary_health 15 +set g_balance_hagar_primary_lifetime 5 +set g_balance_hagar_primary_radius 65 +set g_balance_hagar_primary_refire 0.16667 +set g_balance_hagar_primary_speed 2200 +set g_balance_hagar_primary_spread 0.03 +set g_balance_hagar_reload_ammo 0 +set g_balance_hagar_reload_time 2 +set g_balance_hagar_secondary 1 +set g_balance_hagar_secondary_ammo 1 +set g_balance_hagar_secondary_damage 35 +set g_balance_hagar_secondary_damageforcescale 0 +set g_balance_hagar_secondary_edgedamage 17.5 +set g_balance_hagar_secondary_force 75 +set g_balance_hagar_secondary_health 15 +set g_balance_hagar_secondary_lifetime_min 10 +set g_balance_hagar_secondary_lifetime_rand 0 +set g_balance_hagar_secondary_load 1 +set g_balance_hagar_secondary_load_abort 1 +set g_balance_hagar_secondary_load_animtime 0.2 +set g_balance_hagar_secondary_load_hold 4 +set g_balance_hagar_secondary_load_linkexplode 0 +set g_balance_hagar_secondary_load_max 4 +set g_balance_hagar_secondary_load_releasedeath 0 +set g_balance_hagar_secondary_load_speed 0.5 +set g_balance_hagar_secondary_load_spread 0.075 +set g_balance_hagar_secondary_load_spread_bias 0.5 +set g_balance_hagar_secondary_radius 80 +set g_balance_hagar_secondary_refire 0.5 +set g_balance_hagar_secondary_speed 2000 +set g_balance_hagar_secondary_spread 0.05 +set g_balance_hagar_switchdelay_drop 0.2 +set g_balance_hagar_switchdelay_raise 0.2 +set g_balance_hagar_weaponreplace "" +set g_balance_hagar_weaponstart 0 +set g_balance_hagar_weaponstartoverride -1 +set g_balance_hagar_weaponthrowable 1 +// }}} +// {{{ #10: Devastator +set g_balance_devastator_ammo 4 +set g_balance_devastator_animtime 0.4 +set g_balance_devastator_damage 80 +set g_balance_devastator_damageforcescale 1 +set g_balance_devastator_detonatedelay 0.02 +set g_balance_devastator_edgedamage 40 +set g_balance_devastator_force 400 +set g_balance_devastator_guidedelay 0.2 +set g_balance_devastator_guidegoal 512 +set g_balance_devastator_guiderate 90 +set g_balance_devastator_guideratedelay 0.01 +set g_balance_devastator_guidestop 0 +set g_balance_devastator_health 30 +set g_balance_devastator_lifetime 10 +set g_balance_devastator_radius 110 +set g_balance_devastator_refire 1.1 +set g_balance_devastator_reload_ammo 0 +set g_balance_devastator_reload_time 2 +set g_balance_devastator_remote_damage 70 +set g_balance_devastator_remote_edgedamage 35 +set g_balance_devastator_remote_force 300 +set g_balance_devastator_remote_jump_damage 70 +set g_balance_devastator_remote_jump_radius 0 +set g_balance_devastator_remote_jump_velocity_z_add 400 +set g_balance_devastator_remote_jump_velocity_z_max 1500 +set g_balance_devastator_remote_jump_velocity_z_min 400 +set g_balance_devastator_remote_radius 110 +set g_balance_devastator_speed 1300 +set g_balance_devastator_speedaccel 1300 +set g_balance_devastator_speedstart 1000 +set g_balance_devastator_switchdelay_drop 0.2 +set g_balance_devastator_switchdelay_raise 0.2 +set g_balance_devastator_weaponreplace "" +set g_balance_devastator_weaponstart 0 +set g_balance_devastator_weaponstartoverride -1 +set g_balance_devastator_weaponthrowable 1 +// }}} +// {{{ #11: Port-O-Launch +set g_balance_porto_primary_animtime 0.3 +set g_balance_porto_primary_lifetime 5 +set g_balance_porto_primary_refire 1.5 +set g_balance_porto_primary_speed 1000 +set g_balance_porto_secondary 1 +set g_balance_porto_secondary_animtime 0.3 +set g_balance_porto_secondary_lifetime 5 +set g_balance_porto_secondary_refire 1.5 +set g_balance_porto_secondary_speed 1000 +set g_balance_porto_switchdelay_drop 0.2 +set g_balance_porto_switchdelay_raise 0.2 +set g_balance_porto_weaponreplace "" +set g_balance_porto_weaponstart 0 +set g_balance_porto_weaponstartoverride -1 +set g_balance_porto_weaponthrowable 1 +// }}} +// {{{ #12: Vaporizer +set g_balance_vaporizer_primary_ammo 10 +set g_balance_vaporizer_primary_animtime 0.3 +set g_balance_vaporizer_primary_refire 1 +set g_balance_vaporizer_reload_ammo 0 +set g_balance_vaporizer_reload_time 0 +set g_balance_vaporizer_secondary_ammo 0 +set g_balance_vaporizer_secondary_animtime 0.2 +set g_balance_vaporizer_secondary_damage 25 +set g_balance_vaporizer_secondary_delay 0 +set g_balance_vaporizer_secondary_edgedamage 12.5 +set g_balance_vaporizer_secondary_force 400 +set g_balance_vaporizer_secondary_lifetime 5 +set g_balance_vaporizer_secondary_radius 70 +set g_balance_vaporizer_secondary_refire 0.7 +set g_balance_vaporizer_secondary_shotangle 0 +set g_balance_vaporizer_secondary_speed 6000 +set g_balance_vaporizer_secondary_spread 0 +set g_balance_vaporizer_switchdelay_drop 0.2 +set g_balance_vaporizer_switchdelay_raise 0.2 +set g_balance_vaporizer_weaponreplace "" +set g_balance_vaporizer_weaponstart 0 +set g_balance_vaporizer_weaponstartoverride -1 +set g_balance_vaporizer_weaponthrowable 0 +// }}} +// {{{ #13: Grappling Hook +set g_balance_hook_primary_ammo 5 +set g_balance_hook_primary_animtime 0.3 +set g_balance_hook_primary_hooked_ammo 5 +set g_balance_hook_primary_hooked_time_free 2 +set g_balance_hook_primary_hooked_time_max 0 +set g_balance_hook_primary_refire 0.2 +set g_balance_hook_secondary_animtime 0.3 +set g_balance_hook_secondary_damage 25 +set g_balance_hook_secondary_damageforcescale 0 +set g_balance_hook_secondary_duration 1.5 +set g_balance_hook_secondary_edgedamage 5 +set g_balance_hook_secondary_force -2000 +set g_balance_hook_secondary_gravity 5 +set g_balance_hook_secondary_health 15 +set g_balance_hook_secondary_lifetime 5 +set g_balance_hook_secondary_power 3 +set g_balance_hook_secondary_radius 500 +set g_balance_hook_secondary_refire 3 +set g_balance_hook_secondary_speed 0 +set g_balance_hook_switchdelay_drop 0.2 +set g_balance_hook_switchdelay_raise 0.2 +set g_balance_hook_weaponreplace "" +set g_balance_hook_weaponstart 0 +set g_balance_hook_weaponstartoverride -1 +set g_balance_hook_weaponthrowable 1 +// }}} +// {{{ #14: Heavy Laser Assault Cannon (MUTATOR WEAPON) +set g_balance_hlac_primary_ammo 1 +set g_balance_hlac_primary_animtime 0.4 +set g_balance_hlac_primary_damage 18 +set g_balance_hlac_primary_edgedamage 9 +set g_balance_hlac_primary_force 90 +set g_balance_hlac_primary_lifetime 5 +set g_balance_hlac_primary_radius 70 +set g_balance_hlac_primary_refire 0.15 +set g_balance_hlac_primary_speed 9000 +set g_balance_hlac_primary_spread_add 0.0045 +set g_balance_hlac_primary_spread_crouchmod 0.25 +set g_balance_hlac_primary_spread_max 0.25 +set g_balance_hlac_primary_spread_min 0.01 +set g_balance_hlac_reload_ammo 0 +set g_balance_hlac_reload_time 2 +set g_balance_hlac_secondary 1 +set g_balance_hlac_secondary_ammo 10 +set g_balance_hlac_secondary_animtime 0.3 +set g_balance_hlac_secondary_damage 15 +set g_balance_hlac_secondary_edgedamage 7.5 +set g_balance_hlac_secondary_force 90 +set g_balance_hlac_secondary_lifetime 5 +set g_balance_hlac_secondary_radius 70 +set g_balance_hlac_secondary_refire 1 +set g_balance_hlac_secondary_shots 6 +set g_balance_hlac_secondary_speed 9000 +set g_balance_hlac_secondary_spread 0.15 +set g_balance_hlac_secondary_spread_crouchmod 0.5 +set g_balance_hlac_switchdelay_drop 0.2 +set g_balance_hlac_switchdelay_raise 0.2 +set g_balance_hlac_weaponreplace "" +set g_balance_hlac_weaponstart 0 +set g_balance_hlac_weaponstartoverride -1 +set g_balance_hlac_weaponthrowable 1 +// }}} +// {{{ #15: @!#%'n Tuba +set g_balance_tuba_animtime 0.05 +set g_balance_tuba_attenuation 0.5 +set g_balance_tuba_damage 5 +set g_balance_tuba_edgedamage 0 +set g_balance_tuba_fadetime 0.25 +set g_balance_tuba_force 40 +set g_balance_tuba_pitchstep 6 +set g_balance_tuba_radius 200 +set g_balance_tuba_refire 0.05 +set g_balance_tuba_switchdelay_drop 0.2 +set g_balance_tuba_switchdelay_raise 0.2 +set g_balance_tuba_volume 1 +set g_balance_tuba_weaponreplace "" +set g_balance_tuba_weaponstart 0 +set g_balance_tuba_weaponstartoverride -1 +set g_balance_tuba_weaponthrowable 1 +// }}} +// {{{ #16: Rifle (MUTATOR WEAPON) +set g_balance_rifle_bursttime 0 +set g_balance_rifle_primary_ammo 10 +set g_balance_rifle_primary_animtime 0.4 +set g_balance_rifle_primary_bullethail 0 +set g_balance_rifle_primary_burstcost 0 +set g_balance_rifle_primary_damage 80 +set g_balance_rifle_primary_force 100 +set g_balance_rifle_primary_refire 1.2 +set g_balance_rifle_primary_shots 1 +set g_balance_rifle_primary_solidpenetration 62.2 +set g_balance_rifle_primary_spread 0 +set g_balance_rifle_primary_tracer 1 +set g_balance_rifle_reload_ammo 80 +set g_balance_rifle_reload_time 2 +set g_balance_rifle_secondary 1 +set g_balance_rifle_secondary_ammo 10 +set g_balance_rifle_secondary_animtime 0.3 +set g_balance_rifle_secondary_bullethail 0 +set g_balance_rifle_secondary_burstcost 0 +set g_balance_rifle_secondary_damage 20 +set g_balance_rifle_secondary_force 50 +set g_balance_rifle_secondary_refire 0.9 +set g_balance_rifle_secondary_reload 0 +set g_balance_rifle_secondary_shots 4 +set g_balance_rifle_secondary_solidpenetration 15.5 +set g_balance_rifle_secondary_spread 0.04 +set g_balance_rifle_secondary_tracer 0 +set g_balance_rifle_switchdelay_drop 0.2 +set g_balance_rifle_switchdelay_raise 0.2 +set g_balance_rifle_weaponreplace "" +set g_balance_rifle_weaponstart 0 +set g_balance_rifle_weaponstartoverride -1 +set g_balance_rifle_weaponthrowable 1 +// }}} +// {{{ #17: Fireball +set g_balance_fireball_primary_animtime 0.4 +set g_balance_fireball_primary_bfgdamage 100 +set g_balance_fireball_primary_bfgforce 0 +set g_balance_fireball_primary_bfgradius 1000 +set g_balance_fireball_primary_damage 200 +set g_balance_fireball_primary_damageforcescale 0 +set g_balance_fireball_primary_edgedamage 50 +set g_balance_fireball_primary_force 600 +set g_balance_fireball_primary_health 0 +set g_balance_fireball_primary_laserburntime 0.5 +set g_balance_fireball_primary_laserdamage 80 +set g_balance_fireball_primary_laseredgedamage 20 +set g_balance_fireball_primary_laserradius 256 +set g_balance_fireball_primary_lifetime 15 +set g_balance_fireball_primary_radius 200 +set g_balance_fireball_primary_refire 2 +set g_balance_fireball_primary_refire2 0 +set g_balance_fireball_primary_speed 1200 +set g_balance_fireball_primary_spread 0 +set g_balance_fireball_secondary_animtime 0.3 +set g_balance_fireball_secondary_damage 40 +set g_balance_fireball_secondary_damageforcescale 4 +set g_balance_fireball_secondary_damagetime 5 +set g_balance_fireball_secondary_laserburntime 0.5 +set g_balance_fireball_secondary_laserdamage 50 +set g_balance_fireball_secondary_laseredgedamage 20 +set g_balance_fireball_secondary_laserradius 110 +set g_balance_fireball_secondary_lifetime 7 +set g_balance_fireball_secondary_refire 1.5 +set g_balance_fireball_secondary_speed 900 +set g_balance_fireball_secondary_speed_up 100 +set g_balance_fireball_secondary_speed_z 0 +set g_balance_fireball_secondary_spread 0 +set g_balance_fireball_switchdelay_drop 0.2 +set g_balance_fireball_switchdelay_raise 0.2 +set g_balance_fireball_weaponreplace "" +set g_balance_fireball_weaponstart 0 +set g_balance_fireball_weaponstartoverride -1 +set g_balance_fireball_weaponthrowable 0 +// }}} +// {{{ #18: T.A.G. Seeker (MUTATOR WEAPON) +set g_balance_seeker_flac_ammo 1 +set g_balance_seeker_flac_animtime 0.1 +set g_balance_seeker_flac_damage 15 +set g_balance_seeker_flac_edgedamage 10 +set g_balance_seeker_flac_force 50 +set g_balance_seeker_flac_lifetime 0.1 +set g_balance_seeker_flac_lifetime_rand 0.05 +set g_balance_seeker_flac_radius 100 +set g_balance_seeker_flac_refire 0.1 +set g_balance_seeker_flac_speed 3000 +set g_balance_seeker_flac_speed_up 1000 +set g_balance_seeker_flac_speed_z 0 +set g_balance_seeker_flac_spread 0.4 +set g_balance_seeker_missile_accel 1400 +set g_balance_seeker_missile_ammo 2 +set g_balance_seeker_missile_animtime 0.2 +set g_balance_seeker_missile_count 3 +set g_balance_seeker_missile_damage 30 +set g_balance_seeker_missile_damageforcescale 4 +set g_balance_seeker_missile_decel 1400 +set g_balance_seeker_missile_delay 0.25 +set g_balance_seeker_missile_edgedamage 10 +set g_balance_seeker_missile_force 150 +set g_balance_seeker_missile_health 5 +set g_balance_seeker_missile_lifetime 15 +set g_balance_seeker_missile_proxy 0 +set g_balance_seeker_missile_proxy_delay 0.2 +set g_balance_seeker_missile_proxy_maxrange 45 +set g_balance_seeker_missile_radius 80 +set g_balance_seeker_missile_refire 0.5 +set g_balance_seeker_missile_smart 1 +set g_balance_seeker_missile_smart_mindist 800 +set g_balance_seeker_missile_smart_trace_max 2500 +set g_balance_seeker_missile_smart_trace_min 1000 +set g_balance_seeker_missile_speed 700 +set g_balance_seeker_missile_speed_max 1300 +set g_balance_seeker_missile_speed_up 300 +set g_balance_seeker_missile_speed_z 0 +set g_balance_seeker_missile_spread 0 +set g_balance_seeker_missile_turnrate 0.65 +set g_balance_seeker_reload_ammo 0 +set g_balance_seeker_reload_time 2 +set g_balance_seeker_switchdelay_drop 0.2 +set g_balance_seeker_switchdelay_raise 0.2 +set g_balance_seeker_tag_ammo 1 +set g_balance_seeker_tag_animtime 0.2 +set g_balance_seeker_tag_damageforcescale 4 +set g_balance_seeker_tag_health 5 +set g_balance_seeker_tag_lifetime 15 +set g_balance_seeker_tag_refire 0.75 +set g_balance_seeker_tag_speed 5000 +set g_balance_seeker_tag_spread 0 +set g_balance_seeker_tag_tracker_lifetime 10 +set g_balance_seeker_type 0 +set g_balance_seeker_weaponreplace "" +set g_balance_seeker_weaponstart 0 +set g_balance_seeker_weaponstartoverride -1 +set g_balance_seeker_weaponthrowable 1 +// }}} +// {{{ #19: Shockwave (MUTATOR WEAPON) +set g_balance_shockwave_blast_animtime 0.3 +set g_balance_shockwave_blast_damage 20 +set g_balance_shockwave_blast_distance 1000 +set g_balance_shockwave_blast_edgedamage 0 +set g_balance_shockwave_blast_force 200 +set g_balance_shockwave_blast_force_forwardbias 50 +set g_balance_shockwave_blast_force_zscale 2 +set g_balance_shockwave_blast_jump_damage 20 +set g_balance_shockwave_blast_jump_edgedamage 0 +set g_balance_shockwave_blast_jump_force 300 +set g_balance_shockwave_blast_jump_force_velocitybias 0 +set g_balance_shockwave_blast_jump_force_zscale 1.25 +set g_balance_shockwave_blast_jump_multiplier_accuracy 0.5 +set g_balance_shockwave_blast_jump_multiplier_distance 0.5 +set g_balance_shockwave_blast_jump_multiplier_min 0 +set g_balance_shockwave_blast_jump_radius 150 +set g_balance_shockwave_blast_multiplier_accuracy 0.5 +set g_balance_shockwave_blast_multiplier_distance 0.5 +set g_balance_shockwave_blast_multiplier_min 0 +set g_balance_shockwave_blast_refire 0.75 +set g_balance_shockwave_blast_splash_damage 15 +set g_balance_shockwave_blast_splash_edgedamage 0 +set g_balance_shockwave_blast_splash_force 100 +set g_balance_shockwave_blast_splash_force_forwardbias 50 +set g_balance_shockwave_blast_splash_multiplier_accuracy 0.5 +set g_balance_shockwave_blast_splash_multiplier_distance 0.5 +set g_balance_shockwave_blast_splash_multiplier_min 0 +set g_balance_shockwave_blast_splash_radius 70 +set g_balance_shockwave_blast_spread_max 120 +set g_balance_shockwave_blast_spread_min 25 +set g_balance_shockwave_melee_animtime 1.3 +set g_balance_shockwave_melee_damage 80 +set g_balance_shockwave_melee_delay 0.25 +set g_balance_shockwave_melee_force 200 +set g_balance_shockwave_melee_multihit 1 +set g_balance_shockwave_melee_no_doubleslap 1 +set g_balance_shockwave_melee_nonplayerdamage 40 +set g_balance_shockwave_melee_range 120 +set g_balance_shockwave_melee_refire 1.25 +set g_balance_shockwave_melee_swing_side 120 +set g_balance_shockwave_melee_swing_up 30 +set g_balance_shockwave_melee_time 0.15 +set g_balance_shockwave_melee_traces 10 +set g_balance_shockwave_switchdelay_drop 0.2 +set g_balance_shockwave_switchdelay_raise 0.2 +set g_balance_shockwave_weaponreplace "" +set g_balance_shockwave_weaponstart 0 +set g_balance_shockwave_weaponstartoverride -1 +set g_balance_shockwave_weaponthrowable 0 +// }}} +// {{{ #20: Arc +set g_balance_arc_beam_ammo 4 +set g_balance_arc_beam_animtime 0.2 +set g_balance_arc_beam_botaimlifetime 0 +set g_balance_arc_beam_botaimspeed 0 +set g_balance_arc_beam_damage 115 +set g_balance_arc_beam_degreespersegment 1 +set g_balance_arc_beam_distancepersegment 0 +set g_balance_arc_beam_falloff_halflifedist 0 +set g_balance_arc_beam_falloff_maxdist 0 +set g_balance_arc_beam_falloff_mindist 0 +set g_balance_arc_beam_force 900 +set g_balance_arc_beam_healing_amax 100 +set g_balance_arc_beam_healing_aps 50 +set g_balance_arc_beam_healing_hmax 150 +set g_balance_arc_beam_healing_hps 50 +set g_balance_arc_cooldown 2.5 +set g_balance_arc_overheat_max 5 +set g_balance_arc_overheat_min 3 +set g_balance_arc_beam_heat 1 +set g_balance_arc_burst_heat 4 +set g_balance_arc_beam_maxangle 10 +set g_balance_arc_beam_nonplayerdamage 80 +set g_balance_arc_beam_range 1000 +set g_balance_arc_beam_refire 0.5 +set g_balance_arc_beam_returnspeed 8 +set g_balance_arc_beam_tightness 0.5 +set g_balance_arc_burst_ammo 15 +set g_balance_arc_burst_damage 250 +set g_balance_arc_burst_healing_aps 100 +set g_balance_arc_burst_healing_hps 100 +set g_balance_arc_switchdelay_drop 0.3 +set g_balance_arc_switchdelay_raise 0.3 +set g_balance_arc_weaponreplace "" +set g_balance_arc_weaponstart 0 +set g_balance_arc_weaponstartoverride -1 +set g_balance_arc_weaponthrowable 1 +// }}} diff --git a/balance-nexuiz25.cfg b/balance-nexuiz25.cfg new file mode 100644 index 0000000000..95680da377 --- /dev/null +++ b/balance-nexuiz25.cfg @@ -0,0 +1,228 @@ +g_mod_balance Nexuiz25 + +// {{{ starting gear +set g_balance_health_start 150 +set g_balance_armor_start 0 +set g_start_ammo_shells 40 +set g_start_ammo_nails 0 +set g_start_ammo_rockets 0 +set g_start_ammo_cells 0 +set g_start_ammo_plasma 0 +set g_start_ammo_fuel 0 +set g_warmup_start_health 250 "starting values when being in warmup-stage" +set g_warmup_start_armor 100 "starting values when being in warmup-stage" +set g_warmup_start_ammo_shells 50 "starting values when being in warmup-stage" +set g_warmup_start_ammo_nails 150 "starting values when being in warmup-stage" +set g_warmup_start_ammo_rockets 50 "starting values when being in warmup-stage" +set g_warmup_start_ammo_cells 50 "starting values when being in warmup-stage" +set g_warmup_start_ammo_plasma 50 "starting values when being in warmup-stage" +set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage" +set g_lms_start_health 250 +set g_lms_start_armor 100 +set g_lms_start_ammo_shells 50 +set g_lms_start_ammo_nails 150 +set g_lms_start_ammo_rockets 50 +set g_lms_start_ammo_cells 50 +set g_lms_start_ammo_plasma 50 +set g_lms_start_ammo_fuel 0 +set g_balance_nix_roundtime 25 +set g_balance_nix_incrtime 1.6 +set g_balance_nix_ammo_shells 15 +set g_balance_nix_ammo_nails 45 +set g_balance_nix_ammo_rockets 15 +set g_balance_nix_ammo_cells 15 +set g_balance_nix_ammo_plasma 15 +set g_balance_nix_ammo_fuel 0 +set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume +set g_balance_nix_ammoincr_nails 6 +set g_balance_nix_ammoincr_rockets 2 +set g_balance_nix_ammoincr_cells 2 +set g_balance_nix_ammoincr_plasma 2 +set g_balance_nix_ammoincr_fuel 2 +// }}} + +// {{{ pickup items +set g_pickup_ammo_anyway 0 +set g_pickup_weapons_anyway 0 +set g_pickup_shells 15 +set g_pickup_shells_weapon 15 +set g_pickup_shells_max 999 +set g_pickup_nails 80 +set g_pickup_nails_weapon 80 +set g_pickup_nails_max 999 +set g_pickup_rockets 15 +set g_pickup_rockets_weapon 15 +set g_pickup_rockets_max 999 +set g_pickup_cells 25 +set g_pickup_cells_weapon 25 +set g_pickup_cells_max 999 +set g_pickup_plasma 25 +set g_pickup_plasma_weapon 25 +set g_pickup_plasma_max 999 +set g_pickup_fuel 25 +set g_pickup_fuel_weapon 25 +set g_pickup_fuel_jetpack 50 +set g_pickup_fuel_max 999 +set g_pickup_armorsmall 5 +set g_pickup_armorsmall_max 999 +set g_pickup_armorsmall_anyway 0 +set g_pickup_armormedium 25 +set g_pickup_armormedium_max 999 +set g_pickup_armormedium_anyway 0 +set g_pickup_armorbig 50 +set g_pickup_armorbig_max 999 +set g_pickup_armorbig_anyway 0 +set g_pickup_armorlarge 100 +set g_pickup_armorlarge_max 999 +set g_pickup_armorlarge_anyway 0 +set g_pickup_healthsmall 5 +set g_pickup_healthsmall_max 999 +set g_pickup_healthsmall_anyway 0 +set g_pickup_healthmedium 25 +set g_pickup_healthmedium_max 999 +set g_pickup_healthmedium_anyway 0 +set g_pickup_healthlarge 50 +set g_pickup_healthlarge_max 999 +set g_pickup_healthlarge_anyway 0 +set g_pickup_healthmega 100 +set g_pickup_healthmega_max 999 +set g_pickup_healthmega_anyway 0 +set g_pickup_respawntime_short 15 +set g_pickup_respawntime_medium 20 +set g_pickup_respawntime_long 30 +set g_pickup_respawntime_powerup 120 +set g_pickup_respawntime_weapon 15 +set g_pickup_respawntime_superweapon 120 +set g_pickup_respawntime_ammo 15 +set g_pickup_respawntimejitter_short 0 +set g_pickup_respawntimejitter_medium 0 +set g_pickup_respawntimejitter_long 0 +set g_pickup_respawntimejitter_powerup 10 +set g_pickup_respawntimejitter_weapon 0 +set g_pickup_respawntimejitter_superweapon 10 +set g_pickup_respawntimejitter_ammo 0 +// }}} + +// {{{ regen/rot +set g_balance_health_regen 0.1 +set g_balance_health_regenlinear 0 +set g_balance_pause_health_regen 5 +set g_balance_pause_health_regen_spawn 0 +set g_balance_health_rot 0.1 +set g_balance_health_rotlinear 0 +set g_balance_pause_health_rot 5 +set g_balance_pause_health_rot_spawn 10 +set g_balance_health_regenstable 100 +set g_balance_health_rotstable 100 +set g_balance_health_limit 999 +set g_balance_armor_regen 0 +set g_balance_armor_regenlinear 0 +set g_balance_armor_rot 0.1 +set g_balance_armor_rotlinear 0 +set g_balance_pause_armor_rot 5 +set g_balance_pause_armor_rot_spawn 10 +set g_balance_armor_regenstable 100 +set g_balance_armor_rotstable 100 +set g_balance_armor_limit 999 +set g_balance_armor_blockpercent 0.6 +set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)" +set g_balance_fuel_regenlinear 0 +set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter +set g_balance_fuel_rot 0.05 +set g_balance_fuel_rotlinear 0 +set g_balance_pause_fuel_rot 5 +set g_balance_pause_fuel_rot_spawn 10 +set g_balance_fuel_regenstable 50 +set g_balance_fuel_rotstable 100 +set g_balance_fuel_limit 999 +// }}} + +// {{{ misc +set g_balance_selfdamagepercent 0.6 +set g_weaponspeedfactor 1 "weapon projectile speed multiplier" +set g_weaponratefactor 1 "weapon fire rate multiplier" +set g_weapondamagefactor 1 "weapon damage multiplier" +set g_weaponforcefactor 1 "weapon force multiplier" +set g_weaponspreadfactor 1 "weapon spread multiplier" +set g_balance_firetransfer_time 0.9 +set g_balance_firetransfer_damage 0.8 +set g_throughfloor_damage 1 +set g_throughfloor_force 1 +set g_projectiles_damage 2 +// possible values: +// -2: absolutely no damage to projectiles (no exceptions) +// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines) +// 0: only damage from contents (lava/slime) or exceptions +// 1: only self damage or damage from contents or exceptions +// 2: allow all damage to projectiles normally +set g_projectiles_keep_owner 0 +set g_projectiles_newton_style 2 +// possible values: +// 0: absolute velocity projectiles (like Quake) +// 1: relative velocity projectiles, "Newtonian" (like Tribes 2) +// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard) +set g_projectiles_newton_style_2_minfactor 0.7 +set g_projectiles_newton_style_2_maxfactor 5 +set g_projectiles_spread_style 0 +// possible values: +// 0: forward + solid sphere (like Quake) - varies velocity +// 1: forward + flattened solid sphere +// 2: forward + solid circle +// 3: forward + normal distribution 3D - varies velocity +// 4: forward + normal distribution on a plane +// 5: forward + circle with 1-r falloff +// 6: forward + circle with 1-r^2 falloff +// 7: forward + circle with (1-r)(2-r) falloff +set g_balance_falldamage_deadminspeed 150 +set g_balance_falldamage_minspeed 1400 +set g_balance_falldamage_factor 0.15 +set g_balance_falldamage_maxdamage 25 +set g_balance_damagepush_speedfactor 0 +set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage +set g_balance_contents_drowndelay 10 // time under water before a player begins drowning +set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning +set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava +set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime +set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime +set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap" +// }}} + +// {{{ powerups +set g_balance_powerup_invincible_takedamage 0.2 +set g_balance_powerup_invincible_time 30 +set g_balance_powerup_strength_damage 3 +set g_balance_powerup_strength_force 4 +set g_balance_powerup_strength_time 30 +set g_balance_powerup_strength_selfdamage 1.5 +set g_balance_powerup_strength_selfforce 1.5 +set g_balance_superweapons_time 30 +// }}} + +// {{{ jetpack/hook +set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack" +set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction" +set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)" +set g_jetpack_maxspeed_side 1500 "max speed of the jetpack in xy direction" +set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" +set g_jetpack_fuel 8 "fuel per second for jetpack" +set g_jetpack_attenuation 2 "jetpack sound attenuation" + +set g_grappling_hook_tarzan 2 // 2: can also pull players +set g_balance_grapplehook_speed_fly 1800 +set g_balance_grapplehook_speed_pull 2000 +set g_balance_grapplehook_force_rubber 2000 +set g_balance_grapplehook_force_rubber_overstretch 1000 +set g_balance_grapplehook_length_min 50 +set g_balance_grapplehook_stretch 50 +set g_balance_grapplehook_airfriction 0.2 +set g_balance_grapplehook_health 130 +set g_balance_grapplehook_damagedbycontents 0 +set g_balance_grapplehook_refire 0.2 +// }}} + +// {{{ port-o-launch +set g_balance_portal_health 200 // these get recharged whenever the portal is used +set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used +// }}} + +exec bal-wep-nexuiz25.cfg diff --git a/balance-samual.cfg b/balance-samual.cfg new file mode 100644 index 0000000000..cb4949762f --- /dev/null +++ b/balance-samual.cfg @@ -0,0 +1,228 @@ +g_mod_balance Samual + +// {{{ starting gear +set g_balance_health_start 100 +set g_balance_armor_start 0 +set g_start_ammo_shells 15 +set g_start_ammo_nails 0 +set g_start_ammo_rockets 0 +set g_start_ammo_cells 0 +set g_start_ammo_plasma 0 +set g_start_ammo_fuel 0 +set g_warmup_start_health 100 "starting values when being in warmup-stage" +set g_warmup_start_armor 100 "starting values when being in warmup-stage" +set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage" +set g_warmup_start_ammo_nails 160 "starting values when being in warmup-stage" +set g_warmup_start_ammo_rockets 80 "starting values when being in warmup-stage" +set g_warmup_start_ammo_cells 90 "starting values when being in warmup-stage" +set g_warmup_start_ammo_plasma 90 "starting values when being in warmup-stage" +set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage" +set g_lms_start_health 200 +set g_lms_start_armor 200 +set g_lms_start_ammo_shells 60 +set g_lms_start_ammo_nails 320 +set g_lms_start_ammo_rockets 160 +set g_lms_start_ammo_cells 180 +set g_lms_start_ammo_plasma 180 +set g_lms_start_ammo_fuel 0 +set g_balance_nix_roundtime 25 +set g_balance_nix_incrtime 1.6 +set g_balance_nix_ammo_shells 60 +set g_balance_nix_ammo_nails 320 +set g_balance_nix_ammo_rockets 160 +set g_balance_nix_ammo_cells 180 +set g_balance_nix_ammo_plasma 180 +set g_balance_nix_ammo_fuel 0 +set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume +set g_balance_nix_ammoincr_nails 6 +set g_balance_nix_ammoincr_rockets 2 +set g_balance_nix_ammoincr_cells 2 +set g_balance_nix_ammoincr_plasma 2 +set g_balance_nix_ammoincr_fuel 2 +// }}} + +// {{{ pickup items +set g_pickup_ammo_anyway 1 +set g_pickup_weapons_anyway 1 +set g_pickup_shells 15 +set g_pickup_shells_weapon 15 +set g_pickup_shells_max 60 +set g_pickup_nails 80 +set g_pickup_nails_weapon 80 +set g_pickup_nails_max 320 +set g_pickup_rockets 40 +set g_pickup_rockets_weapon 40 +set g_pickup_rockets_max 160 +set g_pickup_cells 30 +set g_pickup_cells_weapon 30 +set g_pickup_cells_max 180 +set g_pickup_plasma 30 +set g_pickup_plasma_weapon 30 +set g_pickup_plasma_max 180 +set g_pickup_fuel 50 +set g_pickup_fuel_weapon 50 +set g_pickup_fuel_jetpack 100 +set g_pickup_fuel_max 100 +set g_pickup_armorsmall 5 +set g_pickup_armorsmall_max 200 +set g_pickup_armorsmall_anyway 1 +set g_pickup_armormedium 25 +set g_pickup_armormedium_max 200 +set g_pickup_armormedium_anyway 1 +set g_pickup_armorbig 50 +set g_pickup_armorbig_max 200 +set g_pickup_armorbig_anyway 1 +set g_pickup_armorlarge 100 +set g_pickup_armorlarge_max 200 +set g_pickup_armorlarge_anyway 1 +set g_pickup_healthsmall 5 +set g_pickup_healthsmall_max 200 +set g_pickup_healthsmall_anyway 1 +set g_pickup_healthmedium 25 +set g_pickup_healthmedium_max 200 +set g_pickup_healthmedium_anyway 1 +set g_pickup_healthlarge 50 +set g_pickup_healthlarge_max 200 +set g_pickup_healthlarge_anyway 1 +set g_pickup_healthmega 100 +set g_pickup_healthmega_max 200 +set g_pickup_healthmega_anyway 1 +set g_pickup_respawntime_short 15 +set g_pickup_respawntime_medium 20 +set g_pickup_respawntime_long 30 +set g_pickup_respawntime_powerup 120 +set g_pickup_respawntime_weapon 10 +set g_pickup_respawntime_superweapon 120 +set g_pickup_respawntime_ammo 10 +set g_pickup_respawntimejitter_short 0 +set g_pickup_respawntimejitter_medium 0 +set g_pickup_respawntimejitter_long 0 +set g_pickup_respawntimejitter_powerup 30 +set g_pickup_respawntimejitter_weapon 0 +set g_pickup_respawntimejitter_superweapon 10 +set g_pickup_respawntimejitter_ammo 0 +// }}} + +// {{{ regen/rot +set g_balance_health_regen 0.08 +set g_balance_health_regenlinear 0.5 +set g_balance_pause_health_regen 5 +set g_balance_pause_health_regen_spawn 0 +set g_balance_health_rot 0.04 +set g_balance_health_rotlinear 0.75 +set g_balance_pause_health_rot 1 +set g_balance_pause_health_rot_spawn 5 +set g_balance_health_regenstable 100 +set g_balance_health_rotstable 100 +set g_balance_health_limit 999 +set g_balance_armor_regen 0 +set g_balance_armor_regenlinear 0 +set g_balance_armor_rot 0.04 +set g_balance_armor_rotlinear 0.75 +set g_balance_pause_armor_rot 1 +set g_balance_pause_armor_rot_spawn 5 +set g_balance_armor_regenstable 100 +set g_balance_armor_rotstable 100 +set g_balance_armor_limit 999 +set g_balance_armor_blockpercent 0.6 +set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)" +set g_balance_fuel_regenlinear 0 +set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter +set g_balance_fuel_rot 0.05 +set g_balance_fuel_rotlinear 0 +set g_balance_pause_fuel_rot 5 +set g_balance_pause_fuel_rot_spawn 10 +set g_balance_fuel_regenstable 50 +set g_balance_fuel_rotstable 100 +set g_balance_fuel_limit 999 +// }}} + +// {{{ misc +set g_balance_selfdamagepercent 0.65 +set g_weaponspeedfactor 1 "weapon projectile speed multiplier" +set g_weaponratefactor 1 "weapon fire rate multiplier" +set g_weapondamagefactor 1 "weapon damage multiplier" +set g_weaponforcefactor 1 "weapon force multiplier" +set g_weaponspreadfactor 1 "weapon spread multiplier" +set g_balance_firetransfer_time 0.9 +set g_balance_firetransfer_damage 0.8 +set g_throughfloor_damage 0.75 +set g_throughfloor_force 0.75 +set g_projectiles_damage 2 +// possible values: +// -2: absolutely no damage to projectiles (no exceptions) +// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines) +// 0: only damage from contents (lava/slime) or exceptions +// 1: only self damage or damage from contents or exceptions +// 2: allow all damage to projectiles normally +set g_projectiles_keep_owner 0 +set g_projectiles_newton_style 2 +// possible values: +// 0: absolute velocity projectiles (like Quake) +// 1: relative velocity projectiles, "Newtonian" (like Tribes 2) +// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard) +set g_projectiles_newton_style_2_minfactor 0.8 +set g_projectiles_newton_style_2_maxfactor 1.5 +set g_projectiles_spread_style 7 +// possible values: +// 0: forward + solid sphere (like Quake) - varies velocity +// 1: forward + flattened solid sphere +// 2: forward + solid circle +// 3: forward + normal distribution 3D - varies velocity +// 4: forward + normal distribution on a plane +// 5: forward + circle with 1-r falloff +// 6: forward + circle with 1-r^2 falloff +// 7: forward + circle with (1-r)(2-r) falloff +set g_balance_falldamage_deadminspeed 250 +set g_balance_falldamage_minspeed 900 +set g_balance_falldamage_factor 0.20 +set g_balance_falldamage_maxdamage 40 +set g_balance_damagepush_speedfactor 2.5 +set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage +set g_balance_contents_drowndelay 10 // time under water before a player begins drowning +set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning +set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava +set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime +set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime +set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap" +// }}} + +// {{{ powerups +set g_balance_powerup_invincible_takedamage 0.25 // only 1/4th damage is taken +set g_balance_powerup_invincible_time 30 +set g_balance_powerup_strength_damage 3 +set g_balance_powerup_strength_force 3 +set g_balance_powerup_strength_time 30 +set g_balance_powerup_strength_selfdamage 1.5 +set g_balance_powerup_strength_selfforce 1.5 +set g_balance_superweapons_time 30 +// }}} + +// {{{ jetpack/hook +set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack" +set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction" +set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)" +set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction" +set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" +set g_jetpack_fuel 8 "fuel per second for jetpack" +set g_jetpack_attenuation 2 "jetpack sound attenuation" + +set g_grappling_hook_tarzan 2 // 2: can also pull players +set g_balance_grapplehook_speed_fly 1800 +set g_balance_grapplehook_speed_pull 2000 +set g_balance_grapplehook_force_rubber 2000 +set g_balance_grapplehook_force_rubber_overstretch 1000 +set g_balance_grapplehook_length_min 50 +set g_balance_grapplehook_stretch 50 +set g_balance_grapplehook_airfriction 0.2 +set g_balance_grapplehook_health 50 +set g_balance_grapplehook_damagedbycontents 1 +set g_balance_grapplehook_refire 0.2 +// }}} + +// {{{ port-o-launch +set g_balance_portal_health 200 // these get recharged whenever the portal is used +set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used +// }}} + +exec bal-wep-samual.cfg diff --git a/balance-xdf.cfg b/balance-xdf.cfg new file mode 100644 index 0000000000..4852a59d6f --- /dev/null +++ b/balance-xdf.cfg @@ -0,0 +1,228 @@ +g_mod_balance XDF + +// {{{ starting gear +set g_balance_health_start 100 +set g_balance_armor_start 0 +set g_start_ammo_shells 15 +set g_start_ammo_nails 0 +set g_start_ammo_rockets 0 +set g_start_ammo_cells 0 +set g_start_ammo_plasma 0 +set g_start_ammo_fuel 0 +set g_warmup_start_health 100 "starting values when being in warmup-stage" +set g_warmup_start_armor 100 "starting values when being in warmup-stage" +set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage" +set g_warmup_start_ammo_nails 160 "starting values when being in warmup-stage" +set g_warmup_start_ammo_rockets 80 "starting values when being in warmup-stage" +set g_warmup_start_ammo_cells 90 "starting values when being in warmup-stage" +set g_warmup_start_ammo_plasma 90 "starting values when being in warmup-stage" +set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage" +set g_lms_start_health 200 +set g_lms_start_armor 200 +set g_lms_start_ammo_shells 60 +set g_lms_start_ammo_nails 320 +set g_lms_start_ammo_rockets 160 +set g_lms_start_ammo_cells 180 +set g_lms_start_ammo_plasma 180 +set g_lms_start_ammo_fuel 0 +set g_balance_nix_roundtime 25 +set g_balance_nix_incrtime 1.6 +set g_balance_nix_ammo_shells 60 +set g_balance_nix_ammo_nails 320 +set g_balance_nix_ammo_rockets 160 +set g_balance_nix_ammo_cells 180 +set g_balance_nix_ammo_plasma 180 +set g_balance_nix_ammo_fuel 0 +set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume +set g_balance_nix_ammoincr_nails 6 +set g_balance_nix_ammoincr_rockets 2 +set g_balance_nix_ammoincr_cells 2 +set g_balance_nix_ammoincr_plasma 2 +set g_balance_nix_ammoincr_fuel 2 +// }}} + +// {{{ pickup items +set g_pickup_ammo_anyway 1 +set g_pickup_weapons_anyway 1 +set g_pickup_shells 15 +set g_pickup_shells_weapon 15 +set g_pickup_shells_max 60 +set g_pickup_nails 80 +set g_pickup_nails_weapon 80 +set g_pickup_nails_max 320 +set g_pickup_rockets 40 +set g_pickup_rockets_weapon 40 +set g_pickup_rockets_max 160 +set g_pickup_cells 30 +set g_pickup_cells_weapon 30 +set g_pickup_cells_max 180 +set g_pickup_plasma 30 +set g_pickup_plasma_weapon 30 +set g_pickup_plasma_max 180 +set g_pickup_fuel 50 +set g_pickup_fuel_weapon 50 +set g_pickup_fuel_jetpack 100 +set g_pickup_fuel_max 100 +set g_pickup_armorsmall 5 +set g_pickup_armorsmall_max 200 +set g_pickup_armorsmall_anyway 1 +set g_pickup_armormedium 25 +set g_pickup_armormedium_max 200 +set g_pickup_armormedium_anyway 1 +set g_pickup_armorbig 50 +set g_pickup_armorbig_max 200 +set g_pickup_armorbig_anyway 1 +set g_pickup_armorlarge 100 +set g_pickup_armorlarge_max 200 +set g_pickup_armorlarge_anyway 1 +set g_pickup_healthsmall 5 +set g_pickup_healthsmall_max 200 +set g_pickup_healthsmall_anyway 1 +set g_pickup_healthmedium 25 +set g_pickup_healthmedium_max 200 +set g_pickup_healthmedium_anyway 1 +set g_pickup_healthlarge 50 +set g_pickup_healthlarge_max 200 +set g_pickup_healthlarge_anyway 1 +set g_pickup_healthmega 100 +set g_pickup_healthmega_max 200 +set g_pickup_healthmega_anyway 1 +set g_pickup_respawntime_short 0.1 +set g_pickup_respawntime_medium 0.1 +set g_pickup_respawntime_long 0.1 +set g_pickup_respawntime_powerup 0.1 +set g_pickup_respawntime_weapon 0.1 +set g_pickup_respawntime_superweapon 0.1 +set g_pickup_respawntime_ammo 0.1 +set g_pickup_respawntimejitter_short 0 +set g_pickup_respawntimejitter_medium 0 +set g_pickup_respawntimejitter_long 0 +set g_pickup_respawntimejitter_powerup 30 +set g_pickup_respawntimejitter_weapon 0 +set g_pickup_respawntimejitter_superweapon 10 +set g_pickup_respawntimejitter_ammo 0 +// }}} + +// {{{ regen/rot +set g_balance_health_regen 0.08 +set g_balance_health_regenlinear 0.5 +set g_balance_pause_health_regen 5 +set g_balance_pause_health_regen_spawn 0 +set g_balance_health_rot 0.04 +set g_balance_health_rotlinear 0.75 +set g_balance_pause_health_rot 1 +set g_balance_pause_health_rot_spawn 5 +set g_balance_health_regenstable 100 +set g_balance_health_rotstable 100 +set g_balance_health_limit 999 +set g_balance_armor_regen 0 +set g_balance_armor_regenlinear 0 +set g_balance_armor_rot 0.04 +set g_balance_armor_rotlinear 0.75 +set g_balance_pause_armor_rot 1 +set g_balance_pause_armor_rot_spawn 5 +set g_balance_armor_regenstable 100 +set g_balance_armor_rotstable 100 +set g_balance_armor_limit 999 +set g_balance_armor_blockpercent 0.6 +set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)" +set g_balance_fuel_regenlinear 0 +set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter +set g_balance_fuel_rot 0.05 +set g_balance_fuel_rotlinear 0 +set g_balance_pause_fuel_rot 5 +set g_balance_pause_fuel_rot_spawn 10 +set g_balance_fuel_regenstable 50 +set g_balance_fuel_rotstable 100 +set g_balance_fuel_limit 999 +// }}} + +// {{{ misc +set g_balance_selfdamagepercent 0 +set g_weaponspeedfactor 1 "weapon projectile speed multiplier" +set g_weaponratefactor 1 "weapon fire rate multiplier" +set g_weapondamagefactor 1 "weapon damage multiplier" +set g_weaponforcefactor 1 "weapon force multiplier" +set g_weaponspreadfactor 1 "weapon spread multiplier" +set g_balance_firetransfer_time 0.9 +set g_balance_firetransfer_damage 0.8 +set g_throughfloor_damage 0.4 +set g_throughfloor_force 0.7 +set g_projectiles_damage 2 +// possible values: +// -2: absolutely no damage to projectiles (no exceptions) +// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines) +// 0: only damage from contents (lava/slime) or exceptions +// 1: only self damage or damage from contents or exceptions +// 2: allow all damage to projectiles normally +set g_projectiles_keep_owner 0 +set g_projectiles_newton_style 2 +// possible values: +// 0: absolute velocity projectiles (like Quake) +// 1: relative velocity projectiles, "Newtonian" (like Tribes 2) +// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard) +set g_projectiles_newton_style_2_minfactor 0.8 +set g_projectiles_newton_style_2_maxfactor 1.5 +set g_projectiles_spread_style 7 +// possible values: +// 0: forward + solid sphere (like Quake) - varies velocity +// 1: forward + flattened solid sphere +// 2: forward + solid circle +// 3: forward + normal distribution 3D - varies velocity +// 4: forward + normal distribution on a plane +// 5: forward + circle with 1-r falloff +// 6: forward + circle with 1-r^2 falloff +// 7: forward + circle with (1-r)(2-r) falloff +set g_balance_falldamage_deadminspeed 250 +set g_balance_falldamage_minspeed 900 +set g_balance_falldamage_factor 0.20 +set g_balance_falldamage_maxdamage 40 +set g_balance_damagepush_speedfactor 2.5 +set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage +set g_balance_contents_drowndelay 10 // time under water before a player begins drowning +set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning +set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava +set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime +set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime +set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap" +// }}} + +// {{{ powerups +set g_balance_powerup_invincible_takedamage 0.25 // only 1/4th damage is taken +set g_balance_powerup_invincible_time 999 +set g_balance_powerup_strength_damage 3 +set g_balance_powerup_strength_force 3 +set g_balance_powerup_strength_time 999 +set g_balance_powerup_strength_selfdamage 1.5 +set g_balance_powerup_strength_selfforce 1.5 +set g_balance_superweapons_time 30 +// }}} + +// {{{ jetpack/hook +set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack" +set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction" +set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)" +set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction" +set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" +set g_jetpack_fuel 8 "fuel per second for jetpack" +set g_jetpack_attenuation 2 "jetpack sound attenuation" + +set g_grappling_hook_tarzan 2 // 2: can also pull players +set g_balance_grapplehook_speed_fly 1800 +set g_balance_grapplehook_speed_pull 2000 +set g_balance_grapplehook_force_rubber 2000 +set g_balance_grapplehook_force_rubber_overstretch 1000 +set g_balance_grapplehook_length_min 50 +set g_balance_grapplehook_stretch 50 +set g_balance_grapplehook_airfriction 0.2 +set g_balance_grapplehook_health 130 +set g_balance_grapplehook_damagedbycontents 0 +set g_balance_grapplehook_refire 0.2 +// }}} + +// {{{ port-o-launch +set g_balance_portal_health 200 // these get recharged whenever the portal is used +set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used +// }}} + +exec bal-wep-xdf.cfg diff --git a/balance-xonotic.cfg b/balance-xonotic.cfg new file mode 100644 index 0000000000..08ff39c0e7 --- /dev/null +++ b/balance-xonotic.cfg @@ -0,0 +1,228 @@ +g_mod_balance Xonotic + +// {{{ starting gear +set g_balance_health_start 100 +set g_balance_armor_start 0 +set g_start_ammo_shells 15 +set g_start_ammo_nails 0 +set g_start_ammo_rockets 0 +set g_start_ammo_cells 0 +set g_start_ammo_plasma 0 +set g_start_ammo_fuel 0 +set g_warmup_start_health 100 "starting values when being in warmup-stage" +set g_warmup_start_armor 100 "starting values when being in warmup-stage" +set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage" +set g_warmup_start_ammo_nails 160 "starting values when being in warmup-stage" +set g_warmup_start_ammo_rockets 80 "starting values when being in warmup-stage" +set g_warmup_start_ammo_cells 90 "starting values when being in warmup-stage" +set g_warmup_start_ammo_plasma 90 "starting values when being in warmup-stage" +set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage" +set g_lms_start_health 200 +set g_lms_start_armor 200 +set g_lms_start_ammo_shells 60 +set g_lms_start_ammo_nails 320 +set g_lms_start_ammo_rockets 160 +set g_lms_start_ammo_cells 180 +set g_lms_start_ammo_plasma 180 +set g_lms_start_ammo_fuel 0 +set g_balance_nix_roundtime 25 +set g_balance_nix_incrtime 1.6 +set g_balance_nix_ammo_shells 60 +set g_balance_nix_ammo_nails 320 +set g_balance_nix_ammo_rockets 160 +set g_balance_nix_ammo_cells 180 +set g_balance_nix_ammo_plasma 180 +set g_balance_nix_ammo_fuel 0 +set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume +set g_balance_nix_ammoincr_nails 6 +set g_balance_nix_ammoincr_rockets 2 +set g_balance_nix_ammoincr_cells 2 +set g_balance_nix_ammoincr_plasma 2 +set g_balance_nix_ammoincr_fuel 2 +// }}} + +// {{{ pickup items +set g_pickup_ammo_anyway 1 +set g_pickup_weapons_anyway 1 +set g_pickup_shells 15 +set g_pickup_shells_weapon 15 +set g_pickup_shells_max 60 +set g_pickup_nails 80 +set g_pickup_nails_weapon 80 +set g_pickup_nails_max 320 +set g_pickup_rockets 40 +set g_pickup_rockets_weapon 40 +set g_pickup_rockets_max 160 +set g_pickup_cells 30 +set g_pickup_cells_weapon 30 +set g_pickup_cells_max 180 +set g_pickup_plasma 30 +set g_pickup_plasma_weapon 30 +set g_pickup_plasma_max 180 +set g_pickup_fuel 50 +set g_pickup_fuel_weapon 50 +set g_pickup_fuel_jetpack 100 +set g_pickup_fuel_max 100 +set g_pickup_armorsmall 5 +set g_pickup_armorsmall_max 200 +set g_pickup_armorsmall_anyway 1 +set g_pickup_armormedium 25 +set g_pickup_armormedium_max 200 +set g_pickup_armormedium_anyway 1 +set g_pickup_armorbig 50 +set g_pickup_armorbig_max 200 +set g_pickup_armorbig_anyway 1 +set g_pickup_armorlarge 100 +set g_pickup_armorlarge_max 200 +set g_pickup_armorlarge_anyway 1 +set g_pickup_healthsmall 5 +set g_pickup_healthsmall_max 200 +set g_pickup_healthsmall_anyway 1 +set g_pickup_healthmedium 25 +set g_pickup_healthmedium_max 200 +set g_pickup_healthmedium_anyway 1 +set g_pickup_healthlarge 50 +set g_pickup_healthlarge_max 200 +set g_pickup_healthlarge_anyway 1 +set g_pickup_healthmega 100 +set g_pickup_healthmega_max 200 +set g_pickup_healthmega_anyway 1 +set g_pickup_respawntime_short 15 +set g_pickup_respawntime_medium 20 +set g_pickup_respawntime_long 30 +set g_pickup_respawntime_powerup 120 +set g_pickup_respawntime_weapon 10 +set g_pickup_respawntime_superweapon 120 +set g_pickup_respawntime_ammo 10 +set g_pickup_respawntimejitter_short 0 +set g_pickup_respawntimejitter_medium 0 +set g_pickup_respawntimejitter_long 0 +set g_pickup_respawntimejitter_powerup 0 +set g_pickup_respawntimejitter_weapon 0 +set g_pickup_respawntimejitter_superweapon 10 +set g_pickup_respawntimejitter_ammo 0 +// }}} + +// {{{ regen/rot +set g_balance_health_regen 0.08 +set g_balance_health_regenlinear 0.5 +set g_balance_pause_health_regen 5 +set g_balance_pause_health_regen_spawn 0 +set g_balance_health_rot 0.02 +set g_balance_health_rotlinear 1 +set g_balance_pause_health_rot 1 +set g_balance_pause_health_rot_spawn 5 +set g_balance_health_regenstable 100 +set g_balance_health_rotstable 100 +set g_balance_health_limit 999 +set g_balance_armor_regen 0 +set g_balance_armor_regenlinear 0 +set g_balance_armor_rot 0.02 +set g_balance_armor_rotlinear 1 +set g_balance_pause_armor_rot 1 +set g_balance_pause_armor_rot_spawn 5 +set g_balance_armor_regenstable 100 +set g_balance_armor_rotstable 100 +set g_balance_armor_limit 999 +set g_balance_armor_blockpercent 0.7 +set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)" +set g_balance_fuel_regenlinear 0 +set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter +set g_balance_fuel_rot 0.05 +set g_balance_fuel_rotlinear 0 +set g_balance_pause_fuel_rot 5 +set g_balance_pause_fuel_rot_spawn 10 +set g_balance_fuel_regenstable 50 +set g_balance_fuel_rotstable 100 +set g_balance_fuel_limit 999 +// }}} + +// {{{ misc +set g_balance_selfdamagepercent 0.65 +set g_weaponspeedfactor 1 "weapon projectile speed multiplier" +set g_weaponratefactor 1 "weapon fire rate multiplier" +set g_weapondamagefactor 1 "weapon damage multiplier" +set g_weaponforcefactor 1 "weapon force multiplier" +set g_weaponspreadfactor 1 "weapon spread multiplier" +set g_balance_firetransfer_time 0.9 +set g_balance_firetransfer_damage 0.8 +set g_throughfloor_damage 0.75 +set g_throughfloor_force 0.75 +set g_projectiles_damage -2 +// possible values: +// -2: absolutely no damage to projectiles (no exceptions) +// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines) +// 0: only damage from contents (lava/slime) or exceptions +// 1: only self damage or damage from contents or exceptions +// 2: allow all damage to projectiles normally +set g_projectiles_keep_owner 0 +set g_projectiles_newton_style 0 +// possible values: +// 0: absolute velocity projectiles (like Quake) +// 1: relative velocity projectiles, "Newtonian" (like Tribes 2) +// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard) +set g_projectiles_newton_style_2_minfactor 0.8 +set g_projectiles_newton_style_2_maxfactor 1.5 +set g_projectiles_spread_style 7 +// possible values: +// 0: forward + solid sphere (like Quake) - varies velocity +// 1: forward + flattened solid sphere +// 2: forward + solid circle +// 3: forward + normal distribution 3D - varies velocity +// 4: forward + normal distribution on a plane +// 5: forward + circle with 1-r falloff +// 6: forward + circle with 1-r^2 falloff +// 7: forward + circle with (1-r)(2-r) falloff +set g_balance_falldamage_deadminspeed 250 +set g_balance_falldamage_minspeed 900 +set g_balance_falldamage_factor 0.20 +set g_balance_falldamage_maxdamage 40 +set g_balance_damagepush_speedfactor 2.5 +set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage +set g_balance_contents_drowndelay 10 // time under water before a player begins drowning +set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning +set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava +set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime +set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime +set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap" +// }}} + +// {{{ powerups +set g_balance_powerup_invincible_takedamage 0.33 // only 1/3rd damage is taken +set g_balance_powerup_invincible_time 30 +set g_balance_powerup_strength_damage 3 +set g_balance_powerup_strength_force 3 +set g_balance_powerup_strength_time 30 +set g_balance_powerup_strength_selfdamage 1.5 +set g_balance_powerup_strength_selfforce 1.5 +set g_balance_superweapons_time 30 +// }}} + +// {{{ jetpack/hook +set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack" +set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction" +set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)" +set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction" +set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" +set g_jetpack_fuel 8 "fuel per second for jetpack" +set g_jetpack_attenuation 2 "jetpack sound attenuation" + +set g_grappling_hook_tarzan 2 // 2: can also pull players +set g_balance_grapplehook_speed_fly 1800 +set g_balance_grapplehook_speed_pull 2000 +set g_balance_grapplehook_force_rubber 2000 +set g_balance_grapplehook_force_rubber_overstretch 1000 +set g_balance_grapplehook_length_min 50 +set g_balance_grapplehook_stretch 50 +set g_balance_grapplehook_airfriction 0.2 +set g_balance_grapplehook_health 50 +set g_balance_grapplehook_damagedbycontents 1 +set g_balance_grapplehook_refire 0.2 +// }}} + +// {{{ port-o-launch +set g_balance_portal_health 200 // these get recharged whenever the portal is used +set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used +// }}} + +exec bal-wep-xonotic.cfg diff --git a/balance-xpm.cfg b/balance-xpm.cfg new file mode 100644 index 0000000000..d12637da71 --- /dev/null +++ b/balance-xpm.cfg @@ -0,0 +1,228 @@ +g_mod_balance XPM + +// {{{ starting gear +set g_balance_health_start 100 +set g_balance_armor_start 0 +set g_start_ammo_shells 15 +set g_start_ammo_nails 0 +set g_start_ammo_rockets 0 +set g_start_ammo_cells 0 +set g_start_ammo_plasma 0 +set g_start_ammo_fuel 0 +set g_warmup_start_health 100 "starting values when being in warmup-stage" +set g_warmup_start_armor 100 "starting values when being in warmup-stage" +set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage" +set g_warmup_start_ammo_nails 160 "starting values when being in warmup-stage" +set g_warmup_start_ammo_rockets 80 "starting values when being in warmup-stage" +set g_warmup_start_ammo_cells 90 "starting values when being in warmup-stage" +set g_warmup_start_ammo_plasma 90 "starting values when being in warmup-stage" +set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage" +set g_lms_start_health 200 +set g_lms_start_armor 200 +set g_lms_start_ammo_shells 60 +set g_lms_start_ammo_nails 320 +set g_lms_start_ammo_rockets 160 +set g_lms_start_ammo_cells 180 +set g_lms_start_ammo_plasma 180 +set g_lms_start_ammo_fuel 0 +set g_balance_nix_roundtime 25 +set g_balance_nix_incrtime 1.6 +set g_balance_nix_ammo_shells 60 +set g_balance_nix_ammo_nails 320 +set g_balance_nix_ammo_rockets 160 +set g_balance_nix_ammo_cells 180 +set g_balance_nix_ammo_plasma 180 +set g_balance_nix_ammo_fuel 0 +set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume +set g_balance_nix_ammoincr_nails 6 +set g_balance_nix_ammoincr_rockets 2 +set g_balance_nix_ammoincr_cells 2 +set g_balance_nix_ammoincr_plasma 2 +set g_balance_nix_ammoincr_fuel 2 +// }}} + +// {{{ pickup items +set g_pickup_ammo_anyway 1 +set g_pickup_weapons_anyway 1 +set g_pickup_shells 15 +set g_pickup_shells_weapon 15 +set g_pickup_shells_max 60 +set g_pickup_nails 80 +set g_pickup_nails_weapon 80 +set g_pickup_nails_max 320 +set g_pickup_rockets 40 +set g_pickup_rockets_weapon 40 +set g_pickup_rockets_max 160 +set g_pickup_cells 30 +set g_pickup_cells_weapon 30 +set g_pickup_cells_max 180 +set g_pickup_plasma 30 +set g_pickup_plasma_weapon 30 +set g_pickup_plasma_max 180 +set g_pickup_fuel 50 +set g_pickup_fuel_weapon 50 +set g_pickup_fuel_jetpack 100 +set g_pickup_fuel_max 100 +set g_pickup_armorsmall 5 +set g_pickup_armorsmall_max 200 +set g_pickup_armorsmall_anyway 0 +set g_pickup_armormedium 25 +set g_pickup_armormedium_max 100 +set g_pickup_armormedium_anyway 0 +set g_pickup_armorbig 50 +set g_pickup_armorbig_max 100 +set g_pickup_armorbig_anyway 0 +set g_pickup_armorlarge 100 +set g_pickup_armorlarge_max 200 +set g_pickup_armorlarge_anyway 0 +set g_pickup_healthsmall 5 +set g_pickup_healthsmall_max 200 +set g_pickup_healthsmall_anyway 0 +set g_pickup_healthmedium 25 +set g_pickup_healthmedium_max 100 +set g_pickup_healthmedium_anyway 0 +set g_pickup_healthlarge 50 +set g_pickup_healthlarge_max 100 +set g_pickup_healthlarge_anyway 0 +set g_pickup_healthmega 100 +set g_pickup_healthmega_max 200 +set g_pickup_healthmega_anyway 0 +set g_pickup_respawntime_short 15 +set g_pickup_respawntime_medium 20 +set g_pickup_respawntime_long 30 +set g_pickup_respawntime_powerup 120 +set g_pickup_respawntime_weapon 10 +set g_pickup_respawntime_superweapon 120 +set g_pickup_respawntime_ammo 10 +set g_pickup_respawntimejitter_short 0 +set g_pickup_respawntimejitter_medium 0 +set g_pickup_respawntimejitter_long 0 +set g_pickup_respawntimejitter_powerup 0 +set g_pickup_respawntimejitter_weapon 0 +set g_pickup_respawntimejitter_superweapon 10 +set g_pickup_respawntimejitter_ammo 0 +// }}} + +// {{{ regen/rot +set g_balance_health_regen 0.08 +set g_balance_health_regenlinear 0.5 +set g_balance_pause_health_regen 5 +set g_balance_pause_health_regen_spawn 0 +set g_balance_health_rot 0.02 +set g_balance_health_rotlinear 1 +set g_balance_pause_health_rot 1 +set g_balance_pause_health_rot_spawn 5 +set g_balance_health_regenstable 100 +set g_balance_health_rotstable 100 +set g_balance_health_limit 999 +set g_balance_armor_regen 0 +set g_balance_armor_regenlinear 0 +set g_balance_armor_rot 0.02 +set g_balance_armor_rotlinear 1 +set g_balance_pause_armor_rot 1 +set g_balance_pause_armor_rot_spawn 5 +set g_balance_armor_regenstable 100 +set g_balance_armor_rotstable 100 +set g_balance_armor_limit 999 +set g_balance_armor_blockpercent 0.7 +set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)" +set g_balance_fuel_regenlinear 0 +set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter +set g_balance_fuel_rot 0.05 +set g_balance_fuel_rotlinear 0 +set g_balance_pause_fuel_rot 5 +set g_balance_pause_fuel_rot_spawn 10 +set g_balance_fuel_regenstable 50 +set g_balance_fuel_rotstable 100 +set g_balance_fuel_limit 999 +// }}} + +// {{{ misc +set g_balance_selfdamagepercent 0.65 +set g_weaponspeedfactor 1 "weapon projectile speed multiplier" +set g_weaponratefactor 1 "weapon fire rate multiplier" +set g_weapondamagefactor 1 "weapon damage multiplier" +set g_weaponforcefactor 1 "weapon force multiplier" +set g_weaponspreadfactor 1 "weapon spread multiplier" +set g_balance_firetransfer_time 0.9 +set g_balance_firetransfer_damage 0.8 +set g_throughfloor_damage 0.75 +set g_throughfloor_force 0.75 +set g_projectiles_damage -2 +// possible values: +// -2: absolutely no damage to projectiles (no exceptions) +// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines) +// 0: only damage from contents (lava/slime) or exceptions +// 1: only self damage or damage from contents or exceptions +// 2: allow all damage to projectiles normally +set g_projectiles_keep_owner 0 +set g_projectiles_newton_style 0 +// possible values: +// 0: absolute velocity projectiles (like Quake) +// 1: relative velocity projectiles, "Newtonian" (like Tribes 2) +// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard) +set g_projectiles_newton_style_2_minfactor 0.8 +set g_projectiles_newton_style_2_maxfactor 1.5 +set g_projectiles_spread_style 7 +// possible values: +// 0: forward + solid sphere (like Quake) - varies velocity +// 1: forward + flattened solid sphere +// 2: forward + solid circle +// 3: forward + normal distribution 3D - varies velocity +// 4: forward + normal distribution on a plane +// 5: forward + circle with 1-r falloff +// 6: forward + circle with 1-r^2 falloff +// 7: forward + circle with (1-r)(2-r) falloff +set g_balance_falldamage_deadminspeed 250 +set g_balance_falldamage_minspeed 900 +set g_balance_falldamage_factor 0.20 +set g_balance_falldamage_maxdamage 40 +set g_balance_damagepush_speedfactor 2.5 +set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage +set g_balance_contents_drowndelay 10 // time under water before a player begins drowning +set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning +set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava +set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime +set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime +set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap" +// }}} + +// {{{ powerups +set g_balance_powerup_invincible_takedamage 0.33 // only 1/3th damage is taken +set g_balance_powerup_invincible_time 30 +set g_balance_powerup_strength_damage 3 +set g_balance_powerup_strength_force 3 +set g_balance_powerup_strength_time 30 +set g_balance_powerup_strength_selfdamage 1.5 +set g_balance_powerup_strength_selfforce 1.5 +set g_balance_superweapons_time 30 +// }}} + +// {{{ jetpack/hook +set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack" +set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction" +set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)" +set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction" +set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" +set g_jetpack_fuel 8 "fuel per second for jetpack" +set g_jetpack_attenuation 2 "jetpack sound attenuation" + +set g_grappling_hook_tarzan 2 // 2: can also pull players +set g_balance_grapplehook_speed_fly 1800 +set g_balance_grapplehook_speed_pull 2000 +set g_balance_grapplehook_force_rubber 2000 +set g_balance_grapplehook_force_rubber_overstretch 1000 +set g_balance_grapplehook_length_min 50 +set g_balance_grapplehook_stretch 50 +set g_balance_grapplehook_airfriction 0.2 +set g_balance_grapplehook_health 50 +set g_balance_grapplehook_damagedbycontents 1 +set g_balance_grapplehook_refire 0.2 +// }}} + +// {{{ port-o-launch +set g_balance_portal_health 200 // these get recharged whenever the portal is used +set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used +// }}} + +exec bal-wep-xpm.cfg diff --git a/balance25.cfg b/balance25.cfg deleted file mode 100644 index be0e84f3f1..0000000000 --- a/balance25.cfg +++ /dev/null @@ -1,706 +0,0 @@ -g_mod_balance Nexuiz25 - -// {{{ starting gear -set g_start_weapon_laser -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_shotgun -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_uzi -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_hagar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_balance_health_start 150 -set g_balance_armor_start 0 -set g_start_ammo_shells 40 -set g_start_ammo_nails 0 -set g_start_ammo_rockets 0 -set g_start_ammo_cells 0 -set g_start_ammo_fuel 0 -set g_warmup_start_health 250 "starting values when being in warmup-stage" -set g_warmup_start_armor 100 "starting values when being in warmup-stage" -set g_warmup_start_ammo_shells 50 "starting values when being in warmup-stage" -set g_warmup_start_ammo_nails 150 "starting values when being in warmup-stage" -set g_warmup_start_ammo_rockets 50 "starting values when being in warmup-stage" -set g_warmup_start_ammo_cells 50 "starting values when being in warmup-stage" -set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage" -set g_lms_start_health 250 -set g_lms_start_armor 100 -set g_lms_start_ammo_shells 50 -set g_lms_start_ammo_nails 150 -set g_lms_start_ammo_rockets 50 -set g_lms_start_ammo_cells 50 -set g_lms_start_ammo_fuel 0 -set g_balance_nix_roundtime 25 -set g_balance_nix_incrtime 1.6 -set g_balance_nix_ammo_shells 15 -set g_balance_nix_ammo_nails 45 -set g_balance_nix_ammo_rockets 15 -set g_balance_nix_ammo_cells 15 -set g_balance_nix_ammo_fuel 0 -set g_balance_nix_ammoincr_shells 2 -set g_balance_nix_ammoincr_nails 6 -set g_balance_nix_ammoincr_rockets 2 -set g_balance_nix_ammoincr_cells 2 -set g_balance_nix_ammoincr_fuel 2 -// }}} - -// {{{ pickup items -set g_pickup_ammo_anyway 0 -set g_pickup_weapons_anyway 0 -set g_pickup_shells 15 -set g_pickup_shells_weapon 15 -set g_pickup_shells_max 999 -set g_pickup_nails 80 -set g_pickup_nails_weapon 80 -set g_pickup_nails_max 999 -set g_pickup_rockets 15 -set g_pickup_rockets_weapon 15 -set g_pickup_rockets_max 999 -set g_pickup_cells 25 -set g_pickup_cells_weapon 25 -set g_pickup_cells_max 999 -set g_pickup_fuel 25 -set g_pickup_fuel_weapon 25 -set g_pickup_fuel_jetpack 50 -set g_pickup_fuel_max 999 -set g_pickup_armorsmall 5 -set g_pickup_armorsmall_max 999 -set g_pickup_armorsmall_anyway 0 -set g_pickup_armormedium 25 -set g_pickup_armormedium_max 999 -set g_pickup_armormedium_anyway 0 -set g_pickup_armorbig 50 -set g_pickup_armorbig_max 999 -set g_pickup_armorbig_anyway 0 -set g_pickup_armorlarge 100 -set g_pickup_armorlarge_max 999 -set g_pickup_armorlarge_anyway 0 -set g_pickup_healthsmall 5 -set g_pickup_healthsmall_max 999 -set g_pickup_healthsmall_anyway 0 -set g_pickup_healthmedium 25 -set g_pickup_healthmedium_max 999 -set g_pickup_healthmedium_anyway 0 -set g_pickup_healthlarge 50 -set g_pickup_healthlarge_max 999 -set g_pickup_healthlarge_anyway 0 -set g_pickup_healthmega 100 -set g_pickup_healthmega_max 999 -set g_pickup_healthmega_anyway 0 -set g_pickup_respawntime_short 15 -set g_pickup_respawntime_medium 20 -set g_pickup_respawntime_long 30 -set g_pickup_respawntime_powerup 120 -set g_pickup_respawntime_weapon 15 -set g_pickup_respawntime_superweapon 120 -set g_pickup_respawntime_ammo 15 -set g_pickup_respawntimejitter_short 0 -set g_pickup_respawntimejitter_medium 0 -set g_pickup_respawntimejitter_long 0 -set g_pickup_respawntimejitter_powerup 10 -set g_pickup_respawntimejitter_weapon 0 -set g_pickup_respawntimejitter_superweapon 10 -set g_pickup_respawntimejitter_ammo 0 -// }}} - -// {{{ regen/rot -set g_balance_health_regen 0.1 -set g_balance_health_regenlinear 0 -set g_balance_pause_health_regen 5 -set g_balance_pause_health_regen_spawn 0 -set g_balance_health_rot 0.1 -set g_balance_health_rotlinear 0 -set g_balance_pause_health_rot 5 -set g_balance_pause_health_rot_spawn 10 -set g_balance_health_regenstable 100 -set g_balance_health_rotstable 100 -set g_balance_health_limit 999 -set g_balance_armor_regen 0 -set g_balance_armor_regenlinear 0 -set g_balance_armor_rot 0.1 -set g_balance_armor_rotlinear 0 -set g_balance_pause_armor_rot 5 -set g_balance_pause_armor_rot_spawn 10 -set g_balance_armor_regenstable 100 -set g_balance_armor_rotstable 100 -set g_balance_armor_limit 999 -set g_balance_armor_blockpercent 0.6 -set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)" -set g_balance_fuel_regenlinear 0 -set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter -set g_balance_fuel_rot 0.05 -set g_balance_fuel_rotlinear 0 -set g_balance_pause_fuel_rot 5 -set g_balance_pause_fuel_rot_spawn 10 -set g_balance_fuel_regenstable 50 -set g_balance_fuel_rotstable 100 -set g_balance_fuel_limit 999 -// }}} - -// {{{ misc -set g_balance_selfdamagepercent 0.6 -set g_weaponspeedfactor 1 "weapon projectile speed multiplier" -set g_weaponratefactor 1 "weapon fire rate multiplier" -set g_weapondamagefactor 1 "weapon damage multiplier" -set g_weaponforcefactor 1 "weapon force multiplier" -set g_weaponspreadfactor 1 "weapon spread multiplier" -set g_balance_firetransfer_time 0.9 -set g_balance_firetransfer_damage 0.8 -set g_throughfloor_damage 1 -set g_throughfloor_force 1 -set g_projectiles_damage 2 -// possible values: -// -2: absolutely no damage to projectiles (no exceptions) -// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines) -// 0: only damage from contents (lava/slime) or exceptions -// 1: only self damage or damage from contents or exceptions -// 2: allow all damage to projectiles normally -set g_projectiles_keep_owner 0 -set g_projectiles_newton_style 2 -// possible values: -// 0: absolute velocity projectiles (like Quake) -// 1: relative velocity projectiles, "Newtonian" (like Tribes 2) -// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard) -set g_projectiles_newton_style_2_minfactor 0.7 -set g_projectiles_newton_style_2_maxfactor 5 -set g_projectiles_spread_style 0 -// possible values: -// 0: forward + solid sphere (like Quake) - varies velocity -// 1: forward + flattened solid sphere -// 2: forward + solid circle -// 3: forward + normal distribution 3D - varies velocity -// 4: forward + normal distribution on a plane -// 5: forward + circle with 1-r falloff -// 6: forward + circle with 1-r^2 falloff -// 7: forward + circle with (1-r)(2-r) falloff -set g_balance_falldamage_deadminspeed 150 -set g_balance_falldamage_minspeed 1400 -set g_balance_falldamage_factor 0.15 -set g_balance_falldamage_maxdamage 25 -set g_balance_damagepush_speedfactor 0 -set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage -set g_balance_contents_drowndelay 10 // time under water before a player begins drowning -set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning -set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava -set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime -set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime -set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap" -// }}} - -// {{{ powerups -set g_balance_powerup_invincible_takedamage 0.2 -set g_balance_powerup_invincible_time 30 -set g_balance_powerup_strength_damage 3 -set g_balance_powerup_strength_force 4 -set g_balance_powerup_strength_time 30 -set g_balance_powerup_strength_selfdamage 1.5 -set g_balance_powerup_strength_selfforce 1.5 -set g_balance_superweapons_time 30 -// }}} - -// {{{ jetpack/hook -set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack" -set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction" -set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)" -set g_jetpack_maxspeed_side 1500 "max speed of the jetpack in xy direction" -set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" -set g_jetpack_fuel 8 "fuel per second for jetpack" -set g_jetpack_attenuation 2 "jetpack sound attenuation" - -set g_grappling_hook_tarzan 2 // 2: can also pull players -set g_balance_grapplehook_speed_fly 1800 -set g_balance_grapplehook_speed_pull 2000 -set g_balance_grapplehook_force_rubber 2000 -set g_balance_grapplehook_force_rubber_overstretch 1000 -set g_balance_grapplehook_length_min 50 -set g_balance_grapplehook_stretch 50 -set g_balance_grapplehook_airfriction 0.2 -set g_balance_grapplehook_health 130 -set g_balance_grapplehook_damagedbycontents 0 -set g_balance_grapplehook_refire 0.2 -// }}} - -// {{{ weapon properties -// {{{ laser -set g_balance_laser_primary_damage 35 -set g_balance_laser_primary_edgedamage 10 -set g_balance_laser_primary_force 400 -set g_balance_laser_primary_radius 70 -set g_balance_laser_primary_speed 9000 -set g_balance_laser_primary_spread 0 -set g_balance_laser_primary_refire 0.7 -set g_balance_laser_primary_animtime 0.3 -set g_balance_laser_primary_lifetime 30 -set g_balance_laser_primary_shotangle 0 -set g_balance_laser_primary_delay 0.05 -set g_balance_laser_primary_gauntlet 0 -set g_balance_laser_primary_force_zscale 1 -set g_balance_laser_primary_force_velocitybias 0 -set g_balance_laser_primary_force_other_scale 1 -set g_balance_laser_secondary 0 // when 1, a secondary laser mode exists -set g_balance_laser_secondary_damage 35 -set g_balance_laser_secondary_edgedamage 10 -set g_balance_laser_secondary_force 400 -set g_balance_laser_secondary_radius 70 -set g_balance_laser_secondary_speed 9000 -set g_balance_laser_secondary_spread 0 -set g_balance_laser_secondary_refire 0.7 -set g_balance_laser_secondary_animtime 0.3 -set g_balance_laser_secondary_lifetime 30 -set g_balance_laser_secondary_shotangle 0 -set g_balance_laser_secondary_delay 0 -set g_balance_laser_secondary_gauntlet 0 -set g_balance_laser_secondary_force_zscale 1 -set g_balance_laser_secondary_force_velocitybias 0 -set g_balance_laser_secondary_force_other_scale 1 -set g_balance_laser_switchdelay_drop 0.15 -set g_balance_laser_switchdelay_raise 0.15 -set g_balance_laser_reload_ammo 0 //default: 6 -set g_balance_laser_reload_time 2 -// }}} -// {{{ shotgun -set g_balance_shotgun_primary_bullets 6 -set g_balance_shotgun_primary_damage 9 -set g_balance_shotgun_primary_force 60 -set g_balance_shotgun_primary_spread 0.07 -set g_balance_shotgun_primary_refire 0.5 -set g_balance_shotgun_primary_animtime 0.2 -set g_balance_shotgun_primary_ammo 1 -set g_balance_shotgun_primary_solidpenetration 3.8 -set g_balance_shotgun_secondary 1 -set g_balance_shotgun_secondary_melee_delay 0.25 // 0.35 was too slow -set g_balance_shotgun_secondary_melee_range 120 -set g_balance_shotgun_secondary_melee_swing_side 120 -set g_balance_shotgun_secondary_melee_swing_up 30 -set g_balance_shotgun_secondary_melee_time 0.15 -set g_balance_shotgun_secondary_melee_traces 10 -set g_balance_shotgun_secondary_melee_no_doubleslap 1 -set g_balance_shotgun_secondary_melee_nonplayerdamage 0 -set g_balance_shotgun_secondary_melee_multihit 1 -set g_balance_shotgun_secondary_damage 115 -set g_balance_shotgun_secondary_force 150 -set g_balance_shotgun_secondary_refire 1.1 -set g_balance_shotgun_secondary_animtime 1 -set g_balance_shotgun_switchdelay_drop 0.15 -set g_balance_shotgun_switchdelay_raise 0.15 -set g_balance_shotgun_reload_ammo 0 //default: 5 -set g_balance_shotgun_reload_time 2 -// }}} -// {{{ uzi -set g_balance_uzi_mode 0 // Activates varible spread for sustained & burst mode secondary -set g_balance_uzi_spread_min 0.02 -set g_balance_uzi_spread_max 0.6 -set g_balance_uzi_spread_add 0.012 - -set g_balance_uzi_burst 0 // # of bullets in a burst (if set to 2 or more) -set g_balance_uzi_burst_animtime 0.75 -set g_balance_uzi_burst_refire 0.05 // refire between burst bullets -set g_balance_uzi_burst_refire2 0.75 // refire after burst -set g_balance_uzi_burst_spread 0.04 -set g_balance_uzi_burst_damage 18 -set g_balance_uzi_burst_force 50 -set g_balance_uzi_burst_ammo 3 - -set g_balance_uzi_first 1 -set g_balance_uzi_first_damage 30 -set g_balance_uzi_first_force 50 -set g_balance_uzi_first_spread 0.015 -set g_balance_uzi_first_refire 0.2 -set g_balance_uzi_first_ammo 1 - -set g_balance_uzi_sustained_damage 15 -set g_balance_uzi_sustained_force 27 -set g_balance_uzi_sustained_spread 0.05 -set g_balance_uzi_sustained_refire 0.1 -set g_balance_uzi_sustained_ammo 1 - -set g_balance_uzi_solidpenetration 13.1 - -set g_balance_uzi_switchdelay_drop 0.15 -set g_balance_uzi_switchdelay_raise 0.15 - -set g_balance_uzi_reload_ammo 0 //default: 30 -set g_balance_uzi_reload_time 2 -// }}} -// {{{ mortar -set g_balance_grenadelauncher_primary_type 0 -set g_balance_grenadelauncher_primary_damage 70 -set g_balance_grenadelauncher_primary_edgedamage 38 -set g_balance_grenadelauncher_primary_force 400 -set g_balance_grenadelauncher_primary_radius 140 -set g_balance_grenadelauncher_primary_speed 2000 -set g_balance_grenadelauncher_primary_speed_up 200 -set g_balance_grenadelauncher_primary_speed_z 0 -set g_balance_grenadelauncher_primary_spread 0 -set g_balance_grenadelauncher_primary_lifetime 30 -set g_balance_grenadelauncher_primary_lifetime2 1 -set g_balance_grenadelauncher_primary_refire 0.8 -set g_balance_grenadelauncher_primary_animtime 0.3 -set g_balance_grenadelauncher_primary_ammo 2 -set g_balance_grenadelauncher_primary_health 25 -set g_balance_grenadelauncher_primary_damageforcescale 4 -set g_balance_grenadelauncher_primary_remote_minbouncecnt 0 - -set g_balance_grenadelauncher_secondary_type 1 -set g_balance_grenadelauncher_secondary_damage 70 -set g_balance_grenadelauncher_secondary_edgedamage 38 -set g_balance_grenadelauncher_secondary_force 400 -set g_balance_grenadelauncher_secondary_radius 140 -set g_balance_grenadelauncher_secondary_speed 1400 -set g_balance_grenadelauncher_secondary_speed_up 200 -set g_balance_grenadelauncher_secondary_speed_z 0 -set g_balance_grenadelauncher_secondary_spread 0 -set g_balance_grenadelauncher_secondary_lifetime 2.5 -set g_balance_grenadelauncher_secondary_lifetime_bounce 0 -set g_balance_grenadelauncher_secondary_lifetime_stick 0 -set g_balance_grenadelauncher_secondary_refire 0.7 -set g_balance_grenadelauncher_secondary_animtime 0.3 -set g_balance_grenadelauncher_secondary_ammo 2 -set g_balance_grenadelauncher_secondary_health 10 -set g_balance_grenadelauncher_secondary_damageforcescale 4 -set g_balance_grenadelauncher_secondary_remote_detonateprimary 0 - -set g_balance_grenadelauncher_bouncefactor 0.5 -set g_balance_grenadelauncher_bouncestop 0.075 - -set g_balance_grenadelauncher_switchdelay_drop 0.15 -set g_balance_grenadelauncher_switchdelay_raise 0.15 - -set g_balance_grenadelauncher_reload_ammo 0 //default: 12 -set g_balance_grenadelauncher_reload_time 2 -// }}} -// {{{ electro -set g_balance_electro_lightning 0 -set g_balance_electro_primary_damage 65 -set g_balance_electro_primary_edgedamage 0 -set g_balance_electro_primary_force 200 -set g_balance_electro_primary_force_up 0 -set g_balance_electro_primary_radius 150 -set g_balance_electro_primary_comboradius 0 -set g_balance_electro_primary_speed 2000 -set g_balance_electro_primary_spread 0 -set g_balance_electro_primary_lifetime 30 -set g_balance_electro_primary_refire 0.6 -set g_balance_electro_primary_animtime 0.3 -set g_balance_electro_primary_ammo 2 -set g_balance_electro_primary_range 0 -set g_balance_electro_primary_falloff_mindist 255 // 0.3 * radius -set g_balance_electro_primary_falloff_maxdist 850 -set g_balance_electro_primary_falloff_halflifedist 425 -set g_balance_electro_secondary_damage 50 -set g_balance_electro_secondary_edgedamage 0 -set g_balance_electro_secondary_force 200 -set g_balance_electro_secondary_radius 150 -set g_balance_electro_secondary_speed 900 -set g_balance_electro_secondary_speed_up 200 -set g_balance_electro_secondary_speed_z 0 -set g_balance_electro_secondary_spread 0 -set g_balance_electro_secondary_lifetime 5 -set g_balance_electro_secondary_refire 0.3 -set g_balance_electro_secondary_refire2 0 -set g_balance_electro_secondary_animtime 0.3 -set g_balance_electro_secondary_ammo 2 -set g_balance_electro_secondary_health 5 -set g_balance_electro_secondary_damageforcescale 4 -set g_balance_electro_secondary_damagedbycontents 0 -set g_balance_electro_secondary_count 1 -set g_balance_electro_secondary_bouncefactor 0.5 -set g_balance_electro_secondary_bouncestop 0.075 -set g_balance_electro_combo_damage 80 -set g_balance_electro_combo_edgedamage 0 -set g_balance_electro_combo_force 200 -set g_balance_electro_combo_radius 250 -set g_balance_electro_combo_comboradius 0 -set g_balance_electro_combo_speed 2000 -set g_balance_electro_combo_safeammocheck 0 -set g_balance_electro_switchdelay_drop 0.15 -set g_balance_electro_switchdelay_raise 0.15 -set g_balance_electro_reload_ammo 0 //default: 20 -set g_balance_electro_reload_time 2 -// }}} -// {{{ crylink -set g_balance_crylink_primary_damage 18 -set g_balance_crylink_primary_edgedamage 0 -set g_balance_crylink_primary_force -55 -set g_balance_crylink_primary_radius 80 -set g_balance_crylink_primary_speed 7000 -set g_balance_crylink_primary_spread 0.03 -set g_balance_crylink_primary_shots 4 -set g_balance_crylink_primary_bounces 1 -set g_balance_crylink_primary_refire 0.4 -set g_balance_crylink_primary_animtime 0.3 -set g_balance_crylink_primary_ammo 2 -set g_balance_crylink_primary_bouncedamagefactor 0.5 -set g_balance_crylink_primary_joindelay 0 -set g_balance_crylink_primary_joinspread 0 -set g_balance_crylink_primary_joinexplode 0 -set g_balance_crylink_primary_joinexplode_damage 0 -set g_balance_crylink_primary_joinexplode_edgedamage 0 -set g_balance_crylink_primary_joinexplode_radius 0 -set g_balance_crylink_primary_joinexplode_force 0 -set g_balance_crylink_primary_linkexplode 1 - -set g_balance_crylink_primary_middle_lifetime 5 // range: 35000 full, fades to 70000 -set g_balance_crylink_primary_middle_fadetime 5 -set g_balance_crylink_primary_other_lifetime 0.1 // range: 700 full, fades to 2450 -set g_balance_crylink_primary_other_fadetime 0.25 - -set g_balance_crylink_secondary 1 -set g_balance_crylink_secondary_damage 18 -set g_balance_crylink_secondary_edgedamage 0 -set g_balance_crylink_secondary_force -55 -set g_balance_crylink_secondary_radius 3 -set g_balance_crylink_secondary_speed 7000 -set g_balance_crylink_secondary_spread 0.08 -set g_balance_crylink_secondary_spreadtype 0 -set g_balance_crylink_secondary_shots 7 -set g_balance_crylink_secondary_bounces 0 -set g_balance_crylink_secondary_refire 0.5 -set g_balance_crylink_secondary_animtime 0.3 -set g_balance_crylink_secondary_ammo 2 -set g_balance_crylink_secondary_bouncedamagefactor 0.5 -set g_balance_crylink_secondary_joindelay 0 -set g_balance_crylink_secondary_joinspread 0 -set g_balance_crylink_secondary_joinexplode 0 -set g_balance_crylink_secondary_joinexplode_damage 0 -set g_balance_crylink_secondary_joinexplode_edgedamage 0 -set g_balance_crylink_secondary_joinexplode_radius 0 -set g_balance_crylink_secondary_joinexplode_force 0 -set g_balance_crylink_secondary_linkexplode 1 - -set g_balance_crylink_secondary_middle_lifetime 5 // range: 35000 full, fades to 70000 -set g_balance_crylink_secondary_middle_fadetime 5 -set g_balance_crylink_secondary_line_lifetime 2 // range: 35000 full, fades to 70000 -set g_balance_crylink_secondary_line_fadetime 2 - -set g_balance_crylink_switchdelay_drop 0.15 -set g_balance_crylink_switchdelay_raise 0.15 - -set g_balance_crylink_reload_ammo 0 //default: 10 -set g_balance_crylink_reload_time 2 -// }}} -// {{{ nex -set g_balance_nex_primary_damage 100 -set g_balance_nex_primary_force 600 -set g_balance_nex_primary_refire 1.5 -set g_balance_nex_primary_animtime 0.3 -set g_balance_nex_primary_ammo 5 -set g_balance_nex_primary_damagefalloff_mindist 0 -set g_balance_nex_primary_damagefalloff_maxdist 0 -set g_balance_nex_primary_damagefalloff_halflife 0 -set g_balance_nex_primary_damagefalloff_forcehalflife 0 - -set g_balance_nex_secondary 0 -set g_balance_nex_secondary_charge 0 -set g_balance_nex_secondary_charge_rate 0.1 -set g_balance_nex_secondary_chargepool 0 -set g_balance_nex_secondary_chargepool_regen 0.15 -set g_balance_nex_secondary_chargepool_pause_regen 1 -set g_balance_nex_secondary_chargepool_pause_health_regen 1 -set g_balance_nex_secondary_damage 100 -set g_balance_nex_secondary_force 600 -set g_balance_nex_secondary_refire 1.5 -set g_balance_nex_secondary_animtime 0.3 -set g_balance_nex_secondary_ammo 5 -set g_balance_nex_secondary_damagefalloff_mindist 0 -set g_balance_nex_secondary_damagefalloff_maxdist 0 -set g_balance_nex_secondary_damagefalloff_halflife 0 -set g_balance_nex_secondary_damagefalloff_forcehalflife 0 - -set g_balance_nex_charge 0 -set g_balance_nex_charge_mindmg 40 -set g_balance_nex_charge_start 0 -set g_balance_nex_charge_rate 0.1 -set g_balance_nex_charge_animlimit 0.5 -set g_balance_nex_charge_limit 0.5 -set g_balance_nex_charge_rot_rate 0 -set g_balance_nex_charge_rot_pause 0 // Dont rot down until this long after release of charge button -set g_balance_nex_charge_shot_multiplier 0.5 -set g_balance_nex_charge_velocity_rate 0.2 -set g_balance_nex_charge_minspeed 400 -set g_balance_nex_charge_maxspeed 1000 - -set g_balance_nex_switchdelay_drop 0.15 -set g_balance_nex_switchdelay_raise 0.15 - -set g_balance_nex_reload_ammo 0 //default: 25 -set g_balance_nex_reload_time 2 -// }}} -// {{{ minstanex -set g_balance_minstanex_refire 1 -set g_balance_minstanex_animtime 0.3 -set g_balance_minstanex_ammo 10 -set g_balance_minstanex_laser_ammo 0 -set g_balance_minstanex_laser_animtime 0.3 -set g_balance_minstanex_laser_refire 0.7 -set g_balance_minstanex_switchdelay_drop 0.15 -set g_balance_minstanex_switchdelay_raise 0.15 -set g_balance_minstanex_reload_ammo 0 //default: 50 -set g_balance_minstanex_reload_time 2 -// }}} -// {{{ hagar -set g_balance_hagar_primary_damage 37 -set g_balance_hagar_primary_edgedamage 15 -set g_balance_hagar_primary_force 100 -set g_balance_hagar_primary_health 0 -set g_balance_hagar_primary_damageforcescale 0 -set g_balance_hagar_primary_radius 65 -set g_balance_hagar_primary_spread 0.010 -set g_balance_hagar_primary_speed 3000 -set g_balance_hagar_primary_lifetime 30 -set g_balance_hagar_primary_refire 0.15 -set g_balance_hagar_primary_ammo 1 -set g_balance_hagar_secondary 1 -set g_balance_hagar_secondary_load 0 -set g_balance_hagar_secondary_load_speed 0.6 -set g_balance_hagar_secondary_load_spread 0.075 -set g_balance_hagar_secondary_load_spread_bias 0.5 -set g_balance_hagar_secondary_load_max 4 -set g_balance_hagar_secondary_load_hold 0 -set g_balance_hagar_secondary_load_releasedeath 1 -set g_balance_hagar_secondary_load_abort 1 -set g_balance_hagar_secondary_load_linkexplode 0 -set g_balance_hagar_secondary_load_animtime 0.2 -set g_balance_hagar_secondary_damage 37 -set g_balance_hagar_secondary_edgedamage 15 -set g_balance_hagar_secondary_force 100 -set g_balance_hagar_secondary_health 0 -set g_balance_hagar_secondary_damageforcescale 0 -set g_balance_hagar_secondary_radius 65 -set g_balance_hagar_secondary_spread 0.015 -set g_balance_hagar_secondary_speed 1400 -set g_balance_hagar_secondary_lifetime_min 30 -set g_balance_hagar_secondary_lifetime_rand 0 -set g_balance_hagar_secondary_refire 0.15 -set g_balance_hagar_secondary_ammo 1 -set g_balance_hagar_switchdelay_drop 0.15 -set g_balance_hagar_switchdelay_raise 0.15 -set g_balance_hagar_reload_ammo 0 //default: 25 -set g_balance_hagar_reload_time 2 -// }}} -// {{{ rocketlauncher -set g_balance_rocketlauncher_damage 105 -set g_balance_rocketlauncher_edgedamage 40 -set g_balance_rocketlauncher_force 600 -set g_balance_rocketlauncher_radius 150 -set g_balance_rocketlauncher_speed 850 -set g_balance_rocketlauncher_speedaccel 0 -set g_balance_rocketlauncher_speedstart 850 -set g_balance_rocketlauncher_lifetime 30 -set g_balance_rocketlauncher_refire 1 -set g_balance_rocketlauncher_animtime 0.3 -set g_balance_rocketlauncher_ammo 3 -set g_balance_rocketlauncher_health 40 -set g_balance_rocketlauncher_damageforcescale 4 -set g_balance_rocketlauncher_detonatedelay 0.2 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time -set g_balance_rocketlauncher_guiderate 90 // max degrees per second -set g_balance_rocketlauncher_guideratedelay 0.01 // immediate -set g_balance_rocketlauncher_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic) -set g_balance_rocketlauncher_guidedelay 0.15 // delay before guiding kicks in -set g_balance_rocketlauncher_guidestop 0 // stop guiding when firing again -set g_balance_rocketlauncher_remote_damage 105 -set g_balance_rocketlauncher_remote_edgedamage 40 -set g_balance_rocketlauncher_remote_radius 150 -set g_balance_rocketlauncher_remote_force 600 -set g_balance_rocketlauncher_switchdelay_drop 0.15 -set g_balance_rocketlauncher_switchdelay_raise 0.15 -set g_balance_rocketlauncher_reload_ammo 0 //default: 25 -set g_balance_rocketlauncher_reload_time 2 -// }}} -// {{{ porto -set g_balance_porto_primary_refire 1.5 -set g_balance_porto_primary_animtime 0.3 -set g_balance_porto_primary_speed 2000 -set g_balance_porto_primary_lifetime 30 -set g_balance_porto_secondary 0 -set g_balance_porto_secondary_refire 1.5 -set g_balance_porto_secondary_animtime 0.3 -set g_balance_porto_secondary_speed 2000 -set g_balance_porto_secondary_lifetime 30 -set g_balance_porto_switchdelay_drop 0.15 -set g_balance_porto_switchdelay_raise 0.15 -set g_balance_portal_health 200 // these get recharged whenever the portal is used -set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used -// }}} -// {{{ hook -set g_balance_hook_primary_fuel 5 // hook monkeys set 0 -set g_balance_hook_primary_refire 0.2 // hook monkeys set 0 -set g_balance_hook_primary_animtime 0.3 // good shoot anim -set g_balance_hook_primary_hooked_time_max 0 // infinite -set g_balance_hook_primary_hooked_time_free 2 // 2s being hooked are free -set g_balance_hook_primary_hooked_fuel 5 // fuel per second hooked -set g_balance_hook_secondary_damage 25 // not much -set g_balance_hook_secondary_edgedamage 5 // not much -set g_balance_hook_secondary_radius 500 // LOTS -set g_balance_hook_secondary_force -2000 // LOTS -set g_balance_hook_secondary_ammo 50 // a whole pack -set g_balance_hook_secondary_lifetime 30 // infinite -set g_balance_hook_secondary_speed 0 // not much throwing -set g_balance_hook_secondary_gravity 5 // fast falling -set g_balance_hook_secondary_refire 3 // don't drop too many bombs... -set g_balance_hook_secondary_animtime 0.3 // good shoot anim -set g_balance_hook_secondary_power 3 // effect behaves like a square function -set g_balance_hook_secondary_duration 1.5 // effect runs for three seconds -set g_balance_hook_secondary_health 0 -set g_balance_hook_secondary_damageforcescale 0 -set g_balance_hook_switchdelay_drop 0.15 -set g_balance_hook_switchdelay_raise 0.15 -// }}} -// {{{ tuba -set g_balance_tuba_refire 0.05 -set g_balance_tuba_animtime 0.05 -set g_balance_tuba_attenuation 0.5 -set g_balance_tuba_volume 1 -set g_balance_tuba_fadetime 0.25 -set g_balance_tuba_damage 5 -set g_balance_tuba_edgedamage 0 -set g_balance_tuba_radius 200 -set g_balance_tuba_force 40 -set g_balance_tuba_pitchstep 6 -set g_balance_tuba_switchdelay_drop 0.15 -set g_balance_tuba_switchdelay_raise 0.15 -// }}} -// {{{ fireball -set g_balance_fireball_primary_animtime 0.15 -set g_balance_fireball_primary_bfgdamage 100 -set g_balance_fireball_primary_bfgforce 0 -set g_balance_fireball_primary_bfgradius 1000 -set g_balance_fireball_primary_damage 200 -set g_balance_fireball_primary_damageforcescale 4 -set g_balance_fireball_primary_edgedamage 0 -set g_balance_fireball_primary_force 700 -set g_balance_fireball_primary_health 50 -set g_balance_fireball_primary_laserburntime 0.5 -set g_balance_fireball_primary_laserdamage 80 -set g_balance_fireball_primary_laseredgedamage 20 -set g_balance_fireball_primary_laserradius 256 -set g_balance_fireball_primary_lifetime 15 -set g_balance_fireball_primary_radius 200 -set g_balance_fireball_primary_refire 5 -set g_balance_fireball_primary_refire2 0 -set g_balance_fireball_primary_speed 650 -set g_balance_fireball_primary_spread 0 -set g_balance_fireball_secondary_animtime 0.3 -set g_balance_fireball_secondary_damage 40 -set g_balance_fireball_secondary_damageforcescale 4 -set g_balance_fireball_secondary_damagetime 5 -set g_balance_fireball_secondary_force 100 -set g_balance_fireball_secondary_laserburntime 0.5 -set g_balance_fireball_secondary_laserdamage 50 -set g_balance_fireball_secondary_laseredgedamage 20 -set g_balance_fireball_secondary_laserradius 110 -set g_balance_fireball_secondary_lifetime 7 -set g_balance_fireball_secondary_refire 2 -set g_balance_fireball_secondary_speed 900 -set g_balance_fireball_secondary_speed_up 100 -set g_balance_fireball_secondary_speed_z 0 -set g_balance_fireball_secondary_spread 0 -set g_balance_fireball_switchdelay_drop 0.15 -set g_balance_fireball_switchdelay_raise 0.15 -// }}} diff --git a/balanceFruitieX.cfg b/balanceFruitieX.cfg deleted file mode 100644 index 99d458d1d7..0000000000 --- a/balanceFruitieX.cfg +++ /dev/null @@ -1,706 +0,0 @@ -g_mod_balance FruitieX - -// {{{ starting gear -set g_start_weapon_laser -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_shotgun -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_uzi -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_hagar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_balance_health_start 125 -set g_balance_armor_start 0 -set g_start_ammo_shells 20 -set g_start_ammo_nails 0 -set g_start_ammo_rockets 0 -set g_start_ammo_cells 0 -set g_start_ammo_fuel 0 -set g_warmup_start_health 200 "starting values when being in warmup-stage" -set g_warmup_start_armor 100 "starting values when being in warmup-stage" -set g_warmup_start_ammo_shells 50 "starting values when being in warmup-stage" -set g_warmup_start_ammo_nails 150 "starting values when being in warmup-stage" -set g_warmup_start_ammo_rockets 50 "starting values when being in warmup-stage" -set g_warmup_start_ammo_cells 50 "starting values when being in warmup-stage" -set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage" -set g_lms_start_health 200 -set g_lms_start_armor 100 -set g_lms_start_ammo_shells 30 -set g_lms_start_ammo_nails 200 -set g_lms_start_ammo_rockets 150 -set g_lms_start_ammo_cells 150 -set g_lms_start_ammo_fuel 0 -set g_balance_nix_roundtime 25 -set g_balance_nix_incrtime 1.6 -set g_balance_nix_ammo_shells 15 -set g_balance_nix_ammo_nails 45 -set g_balance_nix_ammo_rockets 15 -set g_balance_nix_ammo_cells 15 -set g_balance_nix_ammo_fuel 0 -set g_balance_nix_ammoincr_shells 2 -set g_balance_nix_ammoincr_nails 6 -set g_balance_nix_ammoincr_rockets 2 -set g_balance_nix_ammoincr_cells 2 -set g_balance_nix_ammoincr_fuel 2 -// }}} - -// {{{ pickup items -set g_pickup_ammo_anyway 1 -set g_pickup_weapons_anyway 1 -set g_pickup_shells 20 -set g_pickup_shells_weapon 10 -set g_pickup_shells_max 45 -set g_pickup_nails 120 -set g_pickup_nails_weapon 60 -set g_pickup_nails_max 300 -set g_pickup_rockets 25 -set g_pickup_rockets_weapon 15 -set g_pickup_rockets_max 150 -set g_pickup_cells 30 -set g_pickup_cells_weapon 20 -set g_pickup_cells_max 150 -set g_pickup_fuel 25 -set g_pickup_fuel_weapon 15 -set g_pickup_fuel_jetpack 50 -set g_pickup_fuel_max 100 -set g_pickup_armorsmall 5 -set g_pickup_armorsmall_max 150 -set g_pickup_armorsmall_anyway 1 -set g_pickup_armormedium 25 -set g_pickup_armormedium_max 50 -set g_pickup_armormedium_anyway 0 -set g_pickup_armorbig 50 -set g_pickup_armorbig_max 75; // LOG: to allow a little more armor from medium armor -set g_pickup_armorbig_anyway 0 -set g_pickup_armorlarge 100 -set g_pickup_armorlarge_max 150 -set g_pickup_armorlarge_anyway 1 -set g_pickup_healthsmall 5 -set g_pickup_healthsmall_max 250 -set g_pickup_healthsmall_anyway 1 -set g_pickup_healthmedium 25 -set g_pickup_healthmedium_max 100 -set g_pickup_healthmedium_anyway 0 -set g_pickup_healthlarge 50 -set g_pickup_healthlarge_max 150 -set g_pickup_healthlarge_anyway 0 -set g_pickup_healthmega 100 -set g_pickup_healthmega_max 250 -set g_pickup_healthmega_anyway 1 -set g_pickup_respawntime_short 15 -set g_pickup_respawntime_medium 20 -set g_pickup_respawntime_long 30 -set g_pickup_respawntime_powerup 120 -set g_pickup_respawntime_weapon 10 -set g_pickup_respawntime_superweapon 120 -set g_pickup_respawntime_ammo 25 -set g_pickup_respawntimejitter_short 0 -set g_pickup_respawntimejitter_medium 0 -set g_pickup_respawntimejitter_long 0 -set g_pickup_respawntimejitter_powerup 10 -set g_pickup_respawntimejitter_weapon 0 -set g_pickup_respawntimejitter_superweapon 10 -set g_pickup_respawntimejitter_ammo 0 -// }}} - -// {{{ regen/rot -set g_balance_health_regen 0.05 -set g_balance_health_regenlinear 0 -set g_balance_pause_health_regen 5 -set g_balance_pause_health_regen_spawn 0 -set g_balance_health_rot 0 -set g_balance_health_rotlinear 1 -set g_balance_pause_health_rot 1 -set g_balance_pause_health_rot_spawn 0 -set g_balance_health_regenstable 100 -set g_balance_health_rotstable 100 -set g_balance_health_limit 999 -set g_balance_armor_regen 0 -set g_balance_armor_regenlinear 0 -set g_balance_armor_rot 0 -set g_balance_armor_rotlinear 1 -set g_balance_pause_armor_rot 1 -set g_balance_pause_armor_rot_spawn 0 -set g_balance_armor_regenstable 100 -set g_balance_armor_rotstable 100 -set g_balance_armor_limit 999 -set g_balance_armor_blockpercent 0.7 -set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)" -set g_balance_fuel_regenlinear 0 -set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter -set g_balance_fuel_rot 0.05 -set g_balance_fuel_rotlinear 0 -set g_balance_pause_fuel_rot 5 -set g_balance_pause_fuel_rot_spawn 10 -set g_balance_fuel_regenstable 50 -set g_balance_fuel_rotstable 100 -set g_balance_fuel_limit 999 -// }}} - -// {{{ misc -set g_balance_selfdamagepercent 0.65 -set g_weaponspeedfactor 1 "weapon projectile speed multiplier" -set g_weaponratefactor 1 "weapon fire rate multiplier" -set g_weapondamagefactor 1 "weapon damage multiplier" -set g_weaponforcefactor 1 "weapon force multiplier" -set g_weaponspreadfactor 1 "weapon spread multiplier" -set g_balance_firetransfer_time 0.9 -set g_balance_firetransfer_damage 0.8 -set g_throughfloor_damage 0.7 -set g_throughfloor_force 0.8 -set g_projectiles_damage 2 -// possible values: -// -2: absolutely no damage to projectiles (no exceptions) -// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines) -// 0: only damage from contents (lava/slime) or exceptions -// 1: only self damage or damage from contents or exceptions -// 2: allow all damage to projectiles normally -set g_projectiles_keep_owner 0 -set g_projectiles_newton_style 2 -// possible values: -// 0: absolute velocity projectiles (like Quake) -// 1: relative velocity projectiles, "Newtonian" (like Tribes 2) -// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard) -set g_projectiles_newton_style_2_minfactor 0.7 -set g_projectiles_newton_style_2_maxfactor 5 -set g_projectiles_spread_style 7 -// possible values: -// 0: forward + solid sphere (like Quake) - varies velocity -// 1: forward + flattened solid sphere -// 2: forward + solid circle -// 3: forward + normal distribution 3D - varies velocity -// 4: forward + normal distribution on a plane -// 5: forward + circle with 1-r falloff -// 6: forward + circle with 1-r^2 falloff -// 7: forward + circle with (1-r)(2-r) falloff -set g_balance_falldamage_deadminspeed 150 -set g_balance_falldamage_minspeed 800 -set g_balance_falldamage_factor 0.20 -set g_balance_falldamage_maxdamage 15 -set g_balance_damagepush_speedfactor 2.5 -set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage -set g_balance_contents_drowndelay 10 // time under water before a player begins drowning -set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning -set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava -set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime -set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime -set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap" -// }}} - -// {{{ powerups -set g_balance_powerup_invincible_takedamage 0.6 -set g_balance_powerup_invincible_time 30 -set g_balance_powerup_strength_damage 3 -set g_balance_powerup_strength_force 4 -set g_balance_powerup_strength_time 30 -set g_balance_powerup_strength_selfdamage 1.5 -set g_balance_powerup_strength_selfforce 1.5 -set g_balance_superweapons_time 30 -// }}} - -// {{{ jetpack/hook -set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack" -set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction" -set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)" -set g_jetpack_maxspeed_side 1500 "max speed of the jetpack in xy direction" -set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" -set g_jetpack_fuel 8 "fuel per second for jetpack" -set g_jetpack_attenuation 2 "jetpack sound attenuation" - -set g_grappling_hook_tarzan 2 // 2: can also pull players -set g_balance_grapplehook_speed_fly 1800 -set g_balance_grapplehook_speed_pull 2000 -set g_balance_grapplehook_force_rubber 2000 -set g_balance_grapplehook_force_rubber_overstretch 1000 -set g_balance_grapplehook_length_min 50 -set g_balance_grapplehook_stretch 50 -set g_balance_grapplehook_airfriction 0.2 -set g_balance_grapplehook_health 130 -set g_balance_grapplehook_damagedbycontents 0 -set g_balance_grapplehook_refire 0.2 -// }}} - -// {{{ weapon properties -// {{{ laser -set g_balance_laser_primary_damage 20 // dps 33, hope that's not too high -set g_balance_laser_primary_edgedamage 20 -set g_balance_laser_primary_force 150 // this looks insanely low, but actually isn't with zscale and velocitybias -set g_balance_laser_primary_radius 60 -set g_balance_laser_primary_speed 5000 -set g_balance_laser_primary_spread 0 -set g_balance_laser_primary_refire 0.6 -set g_balance_laser_primary_animtime 0.4 -set g_balance_laser_primary_lifetime 5 -set g_balance_laser_primary_shotangle 0 -set g_balance_laser_primary_delay 0 -set g_balance_laser_primary_gauntlet 0 -set g_balance_laser_primary_force_zscale 2 // 300 upforce -set g_balance_laser_primary_force_velocitybias 0.3 -set g_balance_laser_primary_force_other_scale 2.5 // force 375 when pushing others around -set g_balance_laser_secondary 0 // when 1, a secondary laser mode exists -set g_balance_laser_secondary_damage 200 // dps -set g_balance_laser_secondary_edgedamage 0 -set g_balance_laser_secondary_force 1300 -set g_balance_laser_secondary_radius 60 -set g_balance_laser_secondary_speed 0 -set g_balance_laser_secondary_spread 0 -set g_balance_laser_secondary_refire 0.066 -set g_balance_laser_secondary_animtime 0.066 -set g_balance_laser_secondary_lifetime 0 -set g_balance_laser_secondary_shotangle 0 -set g_balance_laser_secondary_delay 0 -set g_balance_laser_secondary_gauntlet 1 -set g_balance_laser_secondary_force_zscale 1.25 -set g_balance_laser_secondary_force_velocitybias 0 -set g_balance_laser_secondary_force_other_scale 0 -set g_balance_laser_switchdelay_drop 0.15 -set g_balance_laser_switchdelay_raise 0.15 -set g_balance_laser_reload_ammo 0 //default: 6 -set g_balance_laser_reload_time 2 -// }}} -// {{{ shotgun -set g_balance_shotgun_primary_bullets 18 -set g_balance_shotgun_primary_damage 3.5 // LOG: changed from 4 to 3.5, total damage 63 -set g_balance_shotgun_primary_force 20 -set g_balance_shotgun_primary_spread 0.16 // LOG: changed from 0.18 -> 0.16 to compensate a little for lower damage -set g_balance_shotgun_primary_refire 1 -set g_balance_shotgun_primary_animtime 0.3 -set g_balance_shotgun_primary_ammo 1 -set g_balance_shotgun_primary_solidpenetration 3.8 -set g_balance_shotgun_secondary 1 -set g_balance_shotgun_secondary_melee_delay 0.25 // 0.35 was too slow -set g_balance_shotgun_secondary_melee_range 120 -set g_balance_shotgun_secondary_melee_swing_side 120 -set g_balance_shotgun_secondary_melee_swing_up 30 -set g_balance_shotgun_secondary_melee_time 0.15 -set g_balance_shotgun_secondary_melee_traces 10 -set g_balance_shotgun_secondary_melee_no_doubleslap 1 -set g_balance_shotgun_secondary_melee_nonplayerdamage 0 -set g_balance_shotgun_secondary_melee_multihit 1 -set g_balance_shotgun_secondary_damage 110 -set g_balance_shotgun_secondary_force 150 -set g_balance_shotgun_secondary_refire 1.1 -set g_balance_shotgun_secondary_animtime 1 -set g_balance_shotgun_switchdelay_drop 0.15 -set g_balance_shotgun_switchdelay_raise 0.15 -set g_balance_shotgun_reload_ammo 0 //default: 5 -set g_balance_shotgun_reload_time 2 -// }}} -// {{{ uzi -set g_balance_uzi_mode 1 // Activates varible spread for sustained & burst mode secondary -set g_balance_uzi_spread_min 0.02 -set g_balance_uzi_spread_max 0.3 // LOG: 0.6 -> 0.3 -set g_balance_uzi_spread_add 0.008 - -set g_balance_uzi_burst 3 // # of bullets in a burst (if set to 2 or more) -set g_balance_uzi_burst_animtime 0.45 -set g_balance_uzi_burst_refire 0.05 // refire between burst bullets -set g_balance_uzi_burst_refire2 0.45 // refire after burst -set g_balance_uzi_burst_spread 0.07 -set g_balance_uzi_burst_damage 25 -set g_balance_uzi_burst_force 50 -set g_balance_uzi_burst_ammo 3 - -set g_balance_uzi_first 1 -set g_balance_uzi_first_damage 15 / f/ LOG: 22 -> 15 -set g_balance_uzi_first_force 50 -set g_balance_uzi_first_spread 0.03 -set g_balance_uzi_first_refire 0.2 -set g_balance_uzi_first_ammo 2 - -set g_balance_uzi_sustained_damage 12 // 120 dps -set g_balance_uzi_sustained_force 12 -set g_balance_uzi_sustained_spread 0.06 -set g_balance_uzi_sustained_refire 0.1 -set g_balance_uzi_sustained_ammo 1 - -set g_balance_uzi_solidpenetration 13.1 - -set g_balance_uzi_switchdelay_drop 0.15 -set g_balance_uzi_switchdelay_raise 0.15 - -set g_balance_uzi_reload_ammo 0 //default: 30 -set g_balance_uzi_reload_time 2 -// }}} -// {{{ mortar -set g_balance_grenadelauncher_primary_type 0 -set g_balance_grenadelauncher_primary_damage 44 -set g_balance_grenadelauncher_primary_edgedamage 32 -set g_balance_grenadelauncher_primary_force 300 -set g_balance_grenadelauncher_primary_radius 115 -set g_balance_grenadelauncher_primary_speed 1500 -set g_balance_grenadelauncher_primary_speed_up 225 -set g_balance_grenadelauncher_primary_speed_z 0 -set g_balance_grenadelauncher_primary_spread 0 -set g_balance_grenadelauncher_primary_lifetime 5 -set g_balance_grenadelauncher_primary_lifetime2 0.65 -set g_balance_grenadelauncher_primary_refire 0.8 -set g_balance_grenadelauncher_primary_animtime 0.3 -set g_balance_grenadelauncher_primary_ammo 2 -set g_balance_grenadelauncher_primary_health 80 -set g_balance_grenadelauncher_primary_damageforcescale 0 -set g_balance_grenadelauncher_primary_remote_minbouncecnt 0 - -set g_balance_grenadelauncher_secondary_type 1 -set g_balance_grenadelauncher_secondary_damage 62 -set g_balance_grenadelauncher_secondary_edgedamage 32 -set g_balance_grenadelauncher_secondary_force 300 -set g_balance_grenadelauncher_secondary_radius 150 -set g_balance_grenadelauncher_secondary_speed 1000 -set g_balance_grenadelauncher_secondary_speed_up 250 -set g_balance_grenadelauncher_secondary_speed_z 0 -set g_balance_grenadelauncher_secondary_spread 0 -set g_balance_grenadelauncher_secondary_lifetime 3 -set g_balance_grenadelauncher_secondary_lifetime_bounce 0 -set g_balance_grenadelauncher_secondary_lifetime_stick 0.65 -set g_balance_grenadelauncher_secondary_refire 0.8 -set g_balance_grenadelauncher_secondary_animtime 0.3 -set g_balance_grenadelauncher_secondary_ammo 2 -set g_balance_grenadelauncher_secondary_health 40 -set g_balance_grenadelauncher_secondary_damageforcescale 0 -set g_balance_grenadelauncher_secondary_remote_detonateprimary 0 - -set g_balance_grenadelauncher_bouncefactor 0.5 -set g_balance_grenadelauncher_bouncestop 0.12 - -set g_balance_grenadelauncher_switchdelay_drop 0.15 -set g_balance_grenadelauncher_switchdelay_raise 0.15 - -set g_balance_grenadelauncher_reload_ammo 0 //default: 12 -set g_balance_grenadelauncher_reload_time 2 -// }}} -// {{{ electro -set g_balance_electro_lightning 1 -set g_balance_electro_primary_damage 100 -set g_balance_electro_primary_edgedamage 0 -set g_balance_electro_primary_force 425 -set g_balance_electro_primary_force_up 125 -set g_balance_electro_primary_radius 850 -set g_balance_electro_primary_comboradius 150 -set g_balance_electro_primary_speed 0 -set g_balance_electro_primary_spread 0 -set g_balance_electro_primary_lifetime 0 -set g_balance_electro_primary_refire 0.4 -set g_balance_electro_primary_animtime 0.2 -set g_balance_electro_primary_ammo 5 -set g_balance_electro_primary_range 800 -set g_balance_electro_primary_falloff_mindist 0 -set g_balance_electro_primary_falloff_maxdist 0 -set g_balance_electro_primary_falloff_halflifedist 0 -set g_balance_electro_secondary_damage 25 -set g_balance_electro_secondary_edgedamage 0 -set g_balance_electro_secondary_force 100 -set g_balance_electro_secondary_radius 100 -set g_balance_electro_secondary_speed 700 -set g_balance_electro_secondary_speed_up 200 -set g_balance_electro_secondary_speed_z 0 -set g_balance_electro_secondary_spread 0.08 -set g_balance_electro_secondary_lifetime 3.5 -set g_balance_electro_secondary_refire 0.2 -set g_balance_electro_secondary_refire2 2 -set g_balance_electro_secondary_animtime 0.2 -set g_balance_electro_secondary_ammo 2 -set g_balance_electro_secondary_health 10 -set g_balance_electro_secondary_damageforcescale 4 -set g_balance_electro_secondary_damagedbycontents 0 -set g_balance_electro_secondary_count 3 -set g_balance_electro_secondary_bouncefactor 0.5 -set g_balance_electro_secondary_bouncestop 0.075 -set g_balance_electro_combo_damage 50 -set g_balance_electro_combo_edgedamage 0 -set g_balance_electro_combo_force 80 -set g_balance_electro_combo_radius 250 -set g_balance_electro_combo_comboradius 0 -set g_balance_electro_combo_speed 400 -set g_balance_electro_combo_safeammocheck 1 -set g_balance_electro_switchdelay_drop 0.15 -set g_balance_electro_switchdelay_raise 0.15 -set g_balance_electro_reload_ammo 0 //default: 20 -set g_balance_electro_reload_time 2 -// }}} -// {{{ crylink -set g_balance_crylink_primary_damage 7 // LOG: 10 -> 7 -set g_balance_crylink_primary_edgedamage 4 // LOG: 6 -> 4 -set g_balance_crylink_primary_force 35 -set g_balance_crylink_primary_radius 80 -set g_balance_crylink_primary_speed 1500 -set g_balance_crylink_primary_spread 0.05 -set g_balance_crylink_primary_shots 7 -set g_balance_crylink_primary_bounces 2 -set g_balance_crylink_primary_refire 0.8 -set g_balance_crylink_primary_animtime 0.3 -set g_balance_crylink_primary_ammo 2 -set g_balance_crylink_primary_bouncedamagefactor 0.2 -set g_balance_crylink_primary_joindelay 0 -set g_balance_crylink_primary_joinspread 0.2 -set g_balance_crylink_primary_joinexplode 0 -set g_balance_crylink_primary_joinexplode_damage 0 -set g_balance_crylink_primary_joinexplode_edgedamage 0 -set g_balance_crylink_primary_joinexplode_radius 0 -set g_balance_crylink_primary_joinexplode_force 0 -set g_balance_crylink_primary_linkexplode 1 - -set g_balance_crylink_primary_middle_lifetime 5 // range: 10000 full, fades to 20000 -set g_balance_crylink_primary_middle_fadetime 5 -set g_balance_crylink_primary_other_lifetime 2 // range: 800 full, fades to 1300 -set g_balance_crylink_primary_other_fadetime 0.25 - -set g_balance_crylink_secondary 1 -set g_balance_crylink_secondary_damage 5 // LOG: 8 -> 5 -set g_balance_crylink_secondary_edgedamage 3 -set g_balance_crylink_secondary_force 16 // LOG: 20 -> 16 -set g_balance_crylink_secondary_radius 15 // LOG: 20 -> 15 -set g_balance_crylink_secondary_speed 1250 // LOG: 1500 -> 1250 -set g_balance_crylink_secondary_spread 0.1 -set g_balance_crylink_secondary_spreadtype 0 -set g_balance_crylink_secondary_shots 6 -set g_balance_crylink_secondary_bounces 2 -set g_balance_crylink_secondary_refire 0.9 // LOG: 0.8 -> 0.9 -set g_balance_crylink_secondary_animtime 0.3 -set g_balance_crylink_secondary_ammo 3 // LOG: 2 -> 3 -set g_balance_crylink_secondary_bouncedamagefactor 0.4 // LOG: 0.2 -> 0.4 -set g_balance_crylink_secondary_joindelay 0 -set g_balance_crylink_secondary_joinspread 0.2 -set g_balance_crylink_secondary_joinexplode 0 -set g_balance_crylink_secondary_joinexplode_damage 0 -set g_balance_crylink_secondary_joinexplode_edgedamage 0 -set g_balance_crylink_secondary_joinexplode_radius 0 -set g_balance_crylink_secondary_joinexplode_force 0 -set g_balance_crylink_secondary_linkexplode 0 - -set g_balance_crylink_secondary_middle_lifetime 5 // range: 10000 full, fades to 10000 -set g_balance_crylink_secondary_middle_fadetime 5 -set g_balance_crylink_secondary_line_lifetime 2 // range: 4000 full, fades to 8000 -set g_balance_crylink_secondary_line_fadetime 0.25 - -set g_balance_crylink_switchdelay_drop 0.15 -set g_balance_crylink_switchdelay_raise 0.15 - -set g_balance_crylink_reload_ammo 0 //default: 10 -set g_balance_crylink_reload_time 2 -// }}} -// {{{ nex -set g_balance_nex_primary_damage 90 -set g_balance_nex_primary_force 500 -set g_balance_nex_primary_refire 1 -set g_balance_nex_primary_animtime 0.3 -set g_balance_nex_primary_ammo 5 -set g_balance_nex_primary_damagefalloff_mindist 0 -set g_balance_nex_primary_damagefalloff_maxdist 0 -set g_balance_nex_primary_damagefalloff_halflife 0 -set g_balance_nex_primary_damagefalloff_forcehalflife 0 - -set g_balance_nex_secondary 0 // LOG: disable secondary -set g_balance_nex_secondary_charge 0 // LOG: disable secondary charge -set g_balance_nex_secondary_charge_rate 0.4 -set g_balance_nex_secondary_chargepool 1 -set g_balance_nex_secondary_chargepool_regen 0.25 -set g_balance_nex_secondary_chargepool_pause_regen 2 -set g_balance_nex_secondary_chargepool_pause_health_regen 0.5 -set g_balance_nex_secondary_damage 0 -set g_balance_nex_secondary_force 0 -set g_balance_nex_secondary_refire 0 -set g_balance_nex_secondary_animtime 0 -set g_balance_nex_secondary_ammo 0.4 // full charge pool is 1, so it depletes in 2.5 secs -set g_balance_nex_secondary_damagefalloff_mindist 0 -set g_balance_nex_secondary_damagefalloff_maxdist 0 -set g_balance_nex_secondary_damagefalloff_halflife 0 -set g_balance_nex_secondary_damagefalloff_forcehalflife 0 - -set g_balance_nex_charge 1 -set g_balance_nex_charge_mindmg 20 -set g_balance_nex_charge_start 0.5 -set g_balance_nex_charge_rate 0.5 -set g_balance_nex_charge_animlimit 0.5 -set g_balance_nex_charge_limit 1 // LOG: 0.5 -> 1 - allow to fully charge automaticaly -set g_balance_nex_charge_rot_rate 0 // LOG: 0.1 -> 0 - disable rot -set g_balance_nex_charge_rot_pause 0.5 // Dont rot down until this long after release of charge button -set g_balance_nex_charge_shot_multiplier 0 -set g_balance_nex_charge_velocity_rate 0 -set g_balance_nex_charge_minspeed 600 -set g_balance_nex_charge_maxspeed 1000 - -set g_balance_nex_switchdelay_drop 0.15 -set g_balance_nex_switchdelay_raise 0.15 - -set g_balance_nex_reload_ammo 0 //default: 25 -set g_balance_nex_reload_time 2 -// }}} -// {{{ minstanex -set g_balance_minstanex_refire 1 -set g_balance_minstanex_animtime 0.50 -set g_balance_minstanex_ammo 10 -set g_balance_minstanex_laser_ammo 0 -set g_balance_minstanex_laser_animtime 0.3 -set g_balance_minstanex_laser_refire 0.6 -set g_balance_minstanex_switchdelay_drop 0.15 -set g_balance_minstanex_switchdelay_raise 0.15 -set g_balance_minstanex_reload_ammo 0 //default: 50 -set g_balance_minstanex_reload_time 2 -// }}} -// {{{ hagar -set g_balance_hagar_primary_damage 14 -set g_balance_hagar_primary_edgedamage 6 -set g_balance_hagar_primary_force 70 -set g_balance_hagar_primary_health 0 -set g_balance_hagar_primary_damageforcescale 0 -set g_balance_hagar_primary_radius 110 -set g_balance_hagar_primary_spread 0.1 -set g_balance_hagar_primary_speed 1800 -set g_balance_hagar_primary_lifetime 5 -set g_balance_hagar_primary_refire 0.12 -set g_balance_hagar_primary_ammo 1 -set g_balance_hagar_secondary 1 -set g_balance_hagar_secondary_load 0 -set g_balance_hagar_secondary_load_speed 0.6 -set g_balance_hagar_secondary_load_spread 0.075 -set g_balance_hagar_secondary_load_spread_bias 0.5 -set g_balance_hagar_secondary_load_max 4 -set g_balance_hagar_secondary_load_hold 0 -set g_balance_hagar_secondary_load_releasedeath 1 -set g_balance_hagar_secondary_load_abort 1 -set g_balance_hagar_secondary_load_linkexplode 0 -set g_balance_hagar_secondary_load_animtime 0.2 -set g_balance_hagar_secondary_damage 14 // default for _load: 32 -set g_balance_hagar_secondary_edgedamage 6 // default for _load: 10 -set g_balance_hagar_secondary_force 70 // default for _load: 160 -set g_balance_hagar_secondary_health 0 -set g_balance_hagar_secondary_damageforcescale 0 -set g_balance_hagar_secondary_radius 125 -set g_balance_hagar_secondary_spread 0.15 // default for _load: 0.08 -set g_balance_hagar_secondary_speed 1800 -set g_balance_hagar_secondary_lifetime_min 5 -set g_balance_hagar_secondary_lifetime_rand 0 -set g_balance_hagar_secondary_refire 0.12 // default for _load: 0.8 -set g_balance_hagar_secondary_ammo 1 -set g_balance_hagar_switchdelay_drop 0.15 -set g_balance_hagar_switchdelay_raise 0.15 -set g_balance_hagar_reload_ammo 0 //default: 25 -set g_balance_hagar_reload_time 2 -// }}} -// {{{ rocketlauncher -set g_balance_rocketlauncher_damage 82 -set g_balance_rocketlauncher_edgedamage 32 -set g_balance_rocketlauncher_force 350 -set g_balance_rocketlauncher_radius 130 -set g_balance_rocketlauncher_speed 1400 -set g_balance_rocketlauncher_speedaccel 1400 -set g_balance_rocketlauncher_speedstart 800 -set g_balance_rocketlauncher_lifetime 5 -set g_balance_rocketlauncher_refire 1 -set g_balance_rocketlauncher_animtime 0.3 -set g_balance_rocketlauncher_ammo 3 -set g_balance_rocketlauncher_health 0 -set g_balance_rocketlauncher_damageforcescale 0 -set g_balance_rocketlauncher_detonatedelay 0.05 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time -set g_balance_rocketlauncher_guiderate 42 // max degrees per second -set g_balance_rocketlauncher_guideratedelay 0.01 // immediate -set g_balance_rocketlauncher_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic) -set g_balance_rocketlauncher_guidedelay 0.15 // delay before guiding kicks in -set g_balance_rocketlauncher_guidestop 0 // stop guiding when firing again -set g_balance_rocketlauncher_remote_damage 60 -set g_balance_rocketlauncher_remote_edgedamage 20 -set g_balance_rocketlauncher_remote_radius 120 -set g_balance_rocketlauncher_remote_force 350 -set g_balance_rocketlauncher_switchdelay_drop 0.15 -set g_balance_rocketlauncher_switchdelay_raise 0.15 -set g_balance_rocketlauncher_reload_ammo 0 //default: 25 -set g_balance_rocketlauncher_reload_time 2 -// }}} -// {{{ porto -set g_balance_porto_primary_refire 1.5 -set g_balance_porto_primary_animtime 0.2 -set g_balance_porto_primary_speed 2000 -set g_balance_porto_primary_lifetime 5 -set g_balance_porto_secondary 0 -set g_balance_porto_secondary_refire 1.5 -set g_balance_porto_secondary_animtime 0.2 -set g_balance_porto_secondary_speed 2000 -set g_balance_porto_secondary_lifetime 5 -set g_balance_porto_switchdelay_drop 0.15 -set g_balance_porto_switchdelay_raise 0.15 -set g_balance_portal_health 200 // these get recharged whenever the portal is used -set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used -// }}} -// {{{ hook -set g_balance_hook_primary_fuel 5 // hook monkeys set 0 -set g_balance_hook_primary_refire 0.2 // hook monkeys set 0 -set g_balance_hook_primary_animtime 0.2 // good shoot anim -set g_balance_hook_primary_hooked_time_max 0 // infinite -set g_balance_hook_primary_hooked_time_free 2 // 2s being hooked are free -set g_balance_hook_primary_hooked_fuel 5 // fuel per second hooked -set g_balance_hook_secondary_damage 25 // not much -set g_balance_hook_secondary_edgedamage 5 // not much -set g_balance_hook_secondary_radius 500 // LOTS -set g_balance_hook_secondary_force -2000 // LOTS -set g_balance_hook_secondary_ammo 50 // a whole pack -set g_balance_hook_secondary_lifetime 5 // infinite -set g_balance_hook_secondary_speed 0 // not much throwing -set g_balance_hook_secondary_gravity 5 // fast falling -set g_balance_hook_secondary_refire 3 // don't drop too many bombs... -set g_balance_hook_secondary_animtime 0.2 // good shoot anim -set g_balance_hook_secondary_power 3 // effect behaves like a square function -set g_balance_hook_secondary_duration 1.5 // effect runs for three seconds -set g_balance_hook_secondary_health 0 -set g_balance_hook_secondary_damageforcescale 0 -set g_balance_hook_switchdelay_drop 0.15 -set g_balance_hook_switchdelay_raise 0.15 -// }}} -// {{{ tuba -set g_balance_tuba_refire 0.05 -set g_balance_tuba_animtime 0.05 -set g_balance_tuba_attenuation 0.5 -set g_balance_tuba_volume 1 -set g_balance_tuba_fadetime 0.25 -set g_balance_tuba_damage 5 -set g_balance_tuba_edgedamage 0 -set g_balance_tuba_radius 200 -set g_balance_tuba_force 40 -set g_balance_tuba_pitchstep 6 -set g_balance_tuba_switchdelay_drop 0.15 -set g_balance_tuba_switchdelay_raise 0.15 -// }}} -// {{{ fireball -set g_balance_fireball_primary_animtime 0.2 -set g_balance_fireball_primary_bfgdamage 100 -set g_balance_fireball_primary_bfgforce 0 -set g_balance_fireball_primary_bfgradius 1000 -set g_balance_fireball_primary_damage 200 -set g_balance_fireball_primary_damageforcescale 4 -set g_balance_fireball_primary_edgedamage 0 -set g_balance_fireball_primary_force 700 -set g_balance_fireball_primary_health 50 -set g_balance_fireball_primary_laserburntime 0.5 -set g_balance_fireball_primary_laserdamage 80 -set g_balance_fireball_primary_laseredgedamage 20 -set g_balance_fireball_primary_laserradius 256 -set g_balance_fireball_primary_lifetime 15 -set g_balance_fireball_primary_radius 200 -set g_balance_fireball_primary_refire 5 -set g_balance_fireball_primary_refire2 0 -set g_balance_fireball_primary_speed 650 -set g_balance_fireball_primary_spread 0 -set g_balance_fireball_secondary_animtime 0.2 -set g_balance_fireball_secondary_damage 40 -set g_balance_fireball_secondary_damageforcescale 4 -set g_balance_fireball_secondary_damagetime 5 -set g_balance_fireball_secondary_force 100 -set g_balance_fireball_secondary_laserburntime 0.5 -set g_balance_fireball_secondary_laserdamage 50 -set g_balance_fireball_secondary_laseredgedamage 20 -set g_balance_fireball_secondary_laserradius 110 -set g_balance_fireball_secondary_lifetime 7 -set g_balance_fireball_secondary_refire 2 -set g_balance_fireball_secondary_speed 900 -set g_balance_fireball_secondary_speed_up 100 -set g_balance_fireball_secondary_speed_z 0 -set g_balance_fireball_secondary_spread 0 -set g_balance_fireball_switchdelay_drop 0.15 -set g_balance_fireball_switchdelay_raise 0.15 -// }}} diff --git a/balanceXDF.cfg b/balanceXDF.cfg deleted file mode 100644 index 41d1936193..0000000000 --- a/balanceXDF.cfg +++ /dev/null @@ -1,706 +0,0 @@ -g_mod_balance XDF - -// {{{ starting gear -set g_start_weapon_laser 0 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_shotgun 0 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_uzi 1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_hagar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" // UNTIL IT CAN BE REMOVED FROM CODE -set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_balance_health_start 100 -set g_balance_armor_start 0 -set g_start_ammo_shells 15 -set g_start_ammo_nails 0 -set g_start_ammo_rockets 0 -set g_start_ammo_cells 0 -set g_start_ammo_fuel 0 -set g_warmup_start_health 100 "starting values when being in warmup-stage" -set g_warmup_start_armor 100 "starting values when being in warmup-stage" -set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage" -set g_warmup_start_ammo_nails 160 "starting values when being in warmup-stage" -set g_warmup_start_ammo_rockets 80 "starting values when being in warmup-stage" -set g_warmup_start_ammo_cells 90 "starting values when being in warmup-stage" -set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage" -set g_lms_start_health 200 -set g_lms_start_armor 200 -set g_lms_start_ammo_shells 60 -set g_lms_start_ammo_nails 320 -set g_lms_start_ammo_rockets 160 -set g_lms_start_ammo_cells 180 -set g_lms_start_ammo_fuel 0 -set g_balance_nix_roundtime 25 -set g_balance_nix_incrtime 1.6 -set g_balance_nix_ammo_shells 60 -set g_balance_nix_ammo_nails 320 -set g_balance_nix_ammo_rockets 160 -set g_balance_nix_ammo_cells 180 -set g_balance_nix_ammo_fuel 0 -set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume -set g_balance_nix_ammoincr_nails 6 -set g_balance_nix_ammoincr_rockets 2 -set g_balance_nix_ammoincr_cells 2 -set g_balance_nix_ammoincr_fuel 2 -// }}} - -// {{{ pickup items -set g_pickup_ammo_anyway 1 -set g_pickup_weapons_anyway 1 -set g_pickup_shells 15 -set g_pickup_shells_weapon 15 -set g_pickup_shells_max 60 -set g_pickup_nails 80 -set g_pickup_nails_weapon 80 -set g_pickup_nails_max 320 -set g_pickup_rockets 40 -set g_pickup_rockets_weapon 40 -set g_pickup_rockets_max 160 -set g_pickup_cells 30 -set g_pickup_cells_weapon 30 -set g_pickup_cells_max 180 -set g_pickup_fuel 50 -set g_pickup_fuel_weapon 50 -set g_pickup_fuel_jetpack 100 -set g_pickup_fuel_max 100 -set g_pickup_armorsmall 5 -set g_pickup_armorsmall_max 200 -set g_pickup_armorsmall_anyway 1 -set g_pickup_armormedium 25 -set g_pickup_armormedium_max 200 -set g_pickup_armormedium_anyway 1 -set g_pickup_armorbig 50 -set g_pickup_armorbig_max 200 -set g_pickup_armorbig_anyway 1 -set g_pickup_armorlarge 100 -set g_pickup_armorlarge_max 200 -set g_pickup_armorlarge_anyway 1 -set g_pickup_healthsmall 5 -set g_pickup_healthsmall_max 200 -set g_pickup_healthsmall_anyway 1 -set g_pickup_healthmedium 25 -set g_pickup_healthmedium_max 200 -set g_pickup_healthmedium_anyway 1 -set g_pickup_healthlarge 50 -set g_pickup_healthlarge_max 200 -set g_pickup_healthlarge_anyway 1 -set g_pickup_healthmega 100 -set g_pickup_healthmega_max 200 -set g_pickup_healthmega_anyway 1 -set g_pickup_respawntime_short 0.1 -set g_pickup_respawntime_medium 0.1 -set g_pickup_respawntime_long 0.1 -set g_pickup_respawntime_powerup 0.1 -set g_pickup_respawntime_weapon 0.1 -set g_pickup_respawntime_superweapon 0.1 -set g_pickup_respawntime_ammo 0.1 -set g_pickup_respawntimejitter_short 0 -set g_pickup_respawntimejitter_medium 0 -set g_pickup_respawntimejitter_long 0 -set g_pickup_respawntimejitter_powerup 30 -set g_pickup_respawntimejitter_weapon 0 -set g_pickup_respawntimejitter_superweapon 10 -set g_pickup_respawntimejitter_ammo 0 -// }}} - -// {{{ regen/rot -set g_balance_health_regen 0.08 -set g_balance_health_regenlinear 0.5 -set g_balance_pause_health_regen 5 -set g_balance_pause_health_regen_spawn 0 -set g_balance_health_rot 0.04 -set g_balance_health_rotlinear 0.75 -set g_balance_pause_health_rot 1 -set g_balance_pause_health_rot_spawn 5 -set g_balance_health_regenstable 100 -set g_balance_health_rotstable 100 -set g_balance_health_limit 999 -set g_balance_armor_regen 0 -set g_balance_armor_regenlinear 0 -set g_balance_armor_rot 0.04 -set g_balance_armor_rotlinear 0.75 -set g_balance_pause_armor_rot 1 -set g_balance_pause_armor_rot_spawn 5 -set g_balance_armor_regenstable 100 -set g_balance_armor_rotstable 100 -set g_balance_armor_limit 999 -set g_balance_armor_blockpercent 0.6 -set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)" -set g_balance_fuel_regenlinear 0 -set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter -set g_balance_fuel_rot 0.05 -set g_balance_fuel_rotlinear 0 -set g_balance_pause_fuel_rot 5 -set g_balance_pause_fuel_rot_spawn 10 -set g_balance_fuel_regenstable 50 -set g_balance_fuel_rotstable 100 -set g_balance_fuel_limit 999 -// }}} - -// {{{ misc -set g_balance_selfdamagepercent 0 -set g_weaponspeedfactor 1 "weapon projectile speed multiplier" -set g_weaponratefactor 1 "weapon fire rate multiplier" -set g_weapondamagefactor 1 "weapon damage multiplier" -set g_weaponforcefactor 1 "weapon force multiplier" -set g_weaponspreadfactor 1 "weapon spread multiplier" -set g_balance_firetransfer_time 0.9 -set g_balance_firetransfer_damage 0.8 -set g_throughfloor_damage 0.4 -set g_throughfloor_force 0.7 -set g_projectiles_damage 2 -// possible values: -// -2: absolutely no damage to projectiles (no exceptions) -// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines) -// 0: only damage from contents (lava/slime) or exceptions -// 1: only self damage or damage from contents or exceptions -// 2: allow all damage to projectiles normally -set g_projectiles_keep_owner 0 -set g_projectiles_newton_style 2 -// possible values: -// 0: absolute velocity projectiles (like Quake) -// 1: relative velocity projectiles, "Newtonian" (like Tribes 2) -// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard) -set g_projectiles_newton_style_2_minfactor 0.8 -set g_projectiles_newton_style_2_maxfactor 1.5 -set g_projectiles_spread_style 7 -// possible values: -// 0: forward + solid sphere (like Quake) - varies velocity -// 1: forward + flattened solid sphere -// 2: forward + solid circle -// 3: forward + normal distribution 3D - varies velocity -// 4: forward + normal distribution on a plane -// 5: forward + circle with 1-r falloff -// 6: forward + circle with 1-r^2 falloff -// 7: forward + circle with (1-r)(2-r) falloff -set g_balance_falldamage_deadminspeed 250 -set g_balance_falldamage_minspeed 900 -set g_balance_falldamage_factor 0.20 -set g_balance_falldamage_maxdamage 40 -set g_balance_damagepush_speedfactor 2.5 -set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage -set g_balance_contents_drowndelay 10 // time under water before a player begins drowning -set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning -set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava -set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime -set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime -set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap" -// }}} - -// {{{ powerups -set g_balance_powerup_invincible_takedamage 0.25 // only 1/4th damage is taken -set g_balance_powerup_invincible_time 999 -set g_balance_powerup_strength_damage 3 -set g_balance_powerup_strength_force 3 -set g_balance_powerup_strength_time 999 -set g_balance_powerup_strength_selfdamage 1.5 -set g_balance_powerup_strength_selfforce 1.5 -set g_balance_superweapons_time 30 -// }}} - -// {{{ jetpack/hook -set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack" -set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction" -set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)" -set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction" -set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" -set g_jetpack_fuel 8 "fuel per second for jetpack" -set g_jetpack_attenuation 2 "jetpack sound attenuation" - -set g_grappling_hook_tarzan 2 // 2: can also pull players -set g_balance_grapplehook_speed_fly 1800 -set g_balance_grapplehook_speed_pull 2000 -set g_balance_grapplehook_force_rubber 2000 -set g_balance_grapplehook_force_rubber_overstretch 1000 -set g_balance_grapplehook_length_min 50 -set g_balance_grapplehook_stretch 50 -set g_balance_grapplehook_airfriction 0.2 -set g_balance_grapplehook_health 130 -set g_balance_grapplehook_damagedbycontents 0 -set g_balance_grapplehook_refire 0.2 -// }}} - -// {{{ weapon properties -// {{{ laser -set g_balance_laser_primary_damage 25 -set g_balance_laser_primary_edgedamage 12.5 -set g_balance_laser_primary_force 250 -set g_balance_laser_primary_radius 70 -set g_balance_laser_primary_speed 6000 -set g_balance_laser_primary_spread 0 -set g_balance_laser_primary_refire 0.7 -set g_balance_laser_primary_animtime 0.3 -set g_balance_laser_primary_lifetime 5 -set g_balance_laser_primary_shotangle 0 -set g_balance_laser_primary_delay 0 -set g_balance_laser_primary_gauntlet 0 -set g_balance_laser_primary_force_zscale 1.5 -set g_balance_laser_primary_force_velocitybias 0 -set g_balance_laser_primary_force_other_scale 1 -set g_balance_laser_secondary 0 // when 1, a secondary laser mode exists -set g_balance_laser_secondary_damage 25 -set g_balance_laser_secondary_edgedamage 12.5 -set g_balance_laser_secondary_force 400 -set g_balance_laser_secondary_radius 70 -set g_balance_laser_secondary_speed 12000 -set g_balance_laser_secondary_spread 0 -set g_balance_laser_secondary_refire 0.7 -set g_balance_laser_secondary_animtime 0.3 -set g_balance_laser_secondary_lifetime 5 -set g_balance_laser_secondary_shotangle -90 -set g_balance_laser_secondary_delay 0 -set g_balance_laser_secondary_gauntlet 0 -set g_balance_laser_secondary_force_zscale 1.25 -set g_balance_laser_secondary_force_velocitybias 0 -set g_balance_laser_secondary_force_other_scale 1 -set g_balance_laser_switchdelay_drop 0 -set g_balance_laser_switchdelay_raise 0 -set g_balance_laser_reload_ammo 0 //default: 6 -set g_balance_laser_reload_time 2 -// }}} -// {{{ shotgun -set g_balance_shotgun_primary_bullets 14 -set g_balance_shotgun_primary_damage 4 -set g_balance_shotgun_primary_force 15 -set g_balance_shotgun_primary_spread 0.12 -set g_balance_shotgun_primary_refire 0.75 -set g_balance_shotgun_primary_animtime 0.2 -set g_balance_shotgun_primary_ammo 1 -set g_balance_shotgun_primary_solidpenetration 3.8 -set g_balance_shotgun_secondary 1 -set g_balance_shotgun_secondary_melee_delay 0.25 // 0.35 was too slow -set g_balance_shotgun_secondary_melee_range 120 -set g_balance_shotgun_secondary_melee_swing_side 120 -set g_balance_shotgun_secondary_melee_swing_up 30 -set g_balance_shotgun_secondary_melee_time 0.15 -set g_balance_shotgun_secondary_melee_traces 10 -set g_balance_shotgun_secondary_melee_no_doubleslap 1 -set g_balance_shotgun_secondary_melee_nonplayerdamage 40 -set g_balance_shotgun_secondary_melee_multihit 1 -set g_balance_shotgun_secondary_damage 80 -set g_balance_shotgun_secondary_force 200 -set g_balance_shotgun_secondary_refire 1.25 -set g_balance_shotgun_secondary_animtime 1 -set g_balance_shotgun_switchdelay_drop 0 -set g_balance_shotgun_switchdelay_raise 0 -set g_balance_shotgun_reload_ammo 0 //default: 5 -set g_balance_shotgun_reload_time 2 -// }}} -// {{{ uzi -set g_balance_uzi_mode 1 // Activates varible spread for sustained & burst mode secondary -set g_balance_uzi_spread_min 0 -set g_balance_uzi_spread_max 0 -set g_balance_uzi_spread_add 0 - -set g_balance_uzi_burst 3 // # of bullets in a burst (if set to 2 or more) -set g_balance_uzi_burst_animtime 0.3 -set g_balance_uzi_burst_refire 0.06 // refire between burst bullets -set g_balance_uzi_burst_refire2 0.45 // refire after burst -set g_balance_uzi_burst_spread 0.03 -set g_balance_uzi_burst_damage 25 -set g_balance_uzi_burst_force 20 -set g_balance_uzi_burst_ammo 3 - -set g_balance_uzi_first 1 -set g_balance_uzi_first_damage 14 -set g_balance_uzi_first_force 5 -set g_balance_uzi_first_spread 0.03 -set g_balance_uzi_first_refire 0.4 -set g_balance_uzi_first_ammo 1 - -set g_balance_uzi_sustained_damage 12 -set g_balance_uzi_sustained_force 5 -set g_balance_uzi_sustained_spread 0 -set g_balance_uzi_sustained_refire 0.1 -set g_balance_uzi_sustained_ammo 1 - -set g_balance_uzi_solidpenetration 13.1 - -set g_balance_uzi_switchdelay_drop 0 -set g_balance_uzi_switchdelay_raise 0 - -set g_balance_uzi_reload_ammo 0 //default: 30 -set g_balance_uzi_reload_time 2 -// }}} -// {{{ mortar -set g_balance_grenadelauncher_primary_type 0 -set g_balance_grenadelauncher_primary_damage 50 -set g_balance_grenadelauncher_primary_edgedamage 25 -set g_balance_grenadelauncher_primary_force 250 -set g_balance_grenadelauncher_primary_radius 100 -set g_balance_grenadelauncher_primary_speed 2000 -set g_balance_grenadelauncher_primary_speed_up 200 -set g_balance_grenadelauncher_primary_speed_z 0 -set g_balance_grenadelauncher_primary_spread 0 -set g_balance_grenadelauncher_primary_lifetime 5 -set g_balance_grenadelauncher_primary_lifetime2 1 -set g_balance_grenadelauncher_primary_refire 0.7 -set g_balance_grenadelauncher_primary_animtime 0.3 -set g_balance_grenadelauncher_primary_ammo 2 -set g_balance_grenadelauncher_primary_health 0 -set g_balance_grenadelauncher_primary_damageforcescale 0 -set g_balance_grenadelauncher_primary_remote_minbouncecnt 0 - -set g_balance_grenadelauncher_secondary_type 1 -set g_balance_grenadelauncher_secondary_damage 60 -set g_balance_grenadelauncher_secondary_edgedamage 30 -set g_balance_grenadelauncher_secondary_force 300 -set g_balance_grenadelauncher_secondary_radius 200 -set g_balance_grenadelauncher_secondary_speed 800 -set g_balance_grenadelauncher_secondary_speed_up 0 -set g_balance_grenadelauncher_secondary_speed_z 200 -set g_balance_grenadelauncher_secondary_spread 0 -set g_balance_grenadelauncher_secondary_lifetime 8 -set g_balance_grenadelauncher_secondary_lifetime_bounce 0.5 -set g_balance_grenadelauncher_secondary_lifetime_stick 0 -set g_balance_grenadelauncher_secondary_refire 0.7 -set g_balance_grenadelauncher_secondary_animtime 0.5 -set g_balance_grenadelauncher_secondary_ammo 2 -set g_balance_grenadelauncher_secondary_health 0 -set g_balance_grenadelauncher_secondary_damageforcescale 0 -set g_balance_grenadelauncher_secondary_remote_detonateprimary 0 - -set g_balance_grenadelauncher_bouncefactor 0.5 -set g_balance_grenadelauncher_bouncestop 0.075 - -set g_balance_grenadelauncher_switchdelay_drop 0 -set g_balance_grenadelauncher_switchdelay_raise 0 - -set g_balance_grenadelauncher_reload_ammo 0 //default: 12 -set g_balance_grenadelauncher_reload_time 2 -// }}} -// {{{ electro -set g_balance_electro_lightning 0 -set g_balance_electro_primary_damage 55 -set g_balance_electro_primary_edgedamage 27.5 -set g_balance_electro_primary_force 200 -set g_balance_electro_primary_force_up 0 -set g_balance_electro_primary_radius 100 -set g_balance_electro_primary_comboradius 150 -set g_balance_electro_primary_speed 2500 -set g_balance_electro_primary_spread 0 -set g_balance_electro_primary_lifetime 5 -set g_balance_electro_primary_refire 0.6 -set g_balance_electro_primary_animtime 0.1 -set g_balance_electro_primary_ammo 4 -set g_balance_electro_primary_range 0 -set g_balance_electro_primary_falloff_mindist 255 // 0.3 * radius -set g_balance_electro_primary_falloff_maxdist 850 -set g_balance_electro_primary_falloff_halflifedist 425 -set g_balance_electro_secondary_damage 40 -set g_balance_electro_secondary_edgedamage 20 -set g_balance_electro_secondary_force 200 -set g_balance_electro_secondary_radius 150 -set g_balance_electro_secondary_speed 900 -set g_balance_electro_secondary_speed_up 200 -set g_balance_electro_secondary_speed_z 0 -set g_balance_electro_secondary_spread 0.05 -set g_balance_electro_secondary_lifetime 3 -set g_balance_electro_secondary_refire 0.2 -set g_balance_electro_secondary_refire2 1.5 -set g_balance_electro_secondary_animtime 0.2 -set g_balance_electro_secondary_ammo 2 -set g_balance_electro_secondary_health 5 -set g_balance_electro_secondary_damageforcescale 4 -set g_balance_electro_secondary_damagedbycontents 1 -set g_balance_electro_secondary_count 3 -set g_balance_electro_secondary_bouncefactor 0.4 -set g_balance_electro_secondary_bouncestop 0.05 -set g_balance_electro_combo_damage 40 -set g_balance_electro_combo_edgedamage 20 -set g_balance_electro_combo_force 120 -set g_balance_electro_combo_radius 175 -set g_balance_electro_combo_comboradius 275 -set g_balance_electro_combo_speed 2000 -set g_balance_electro_combo_safeammocheck 1 -set g_balance_electro_switchdelay_drop 0 -set g_balance_electro_switchdelay_raise 0 -set g_balance_electro_reload_ammo 0 //default: 20 -set g_balance_electro_reload_time 2 -// }}} -// {{{ crylink -set g_balance_crylink_primary_damage 12 -set g_balance_crylink_primary_edgedamage 6 -set g_balance_crylink_primary_force -50 -set g_balance_crylink_primary_radius 80 -set g_balance_crylink_primary_speed 2000 -set g_balance_crylink_primary_spread 0.08 -set g_balance_crylink_primary_shots 6 -set g_balance_crylink_primary_bounces 1 -set g_balance_crylink_primary_refire 0.7 -set g_balance_crylink_primary_animtime 0.3 -set g_balance_crylink_primary_ammo 3 -set g_balance_crylink_primary_bouncedamagefactor 0.5 -set g_balance_crylink_primary_joindelay 0.1 -set g_balance_crylink_primary_joinspread 0.2 -set g_balance_crylink_primary_joinexplode 1 -set g_balance_crylink_primary_joinexplode_damage 0 -set g_balance_crylink_primary_joinexplode_edgedamage 0 -set g_balance_crylink_primary_joinexplode_radius 0 -set g_balance_crylink_primary_joinexplode_force 0 -set g_balance_crylink_primary_linkexplode 1 - -set g_balance_crylink_primary_middle_lifetime 5 // range: 35000 full, fades to 70000 -set g_balance_crylink_primary_middle_fadetime 5 -set g_balance_crylink_primary_other_lifetime 5 -set g_balance_crylink_primary_other_fadetime 5 - -set g_balance_crylink_secondary 1 -set g_balance_crylink_secondary_damage 10 -set g_balance_crylink_secondary_edgedamage 5 -set g_balance_crylink_secondary_force -150 -set g_balance_crylink_secondary_radius 100 -set g_balance_crylink_secondary_speed 3000 -set g_balance_crylink_secondary_spread 0.01 -set g_balance_crylink_secondary_spreadtype 1 -set g_balance_crylink_secondary_shots 5 -set g_balance_crylink_secondary_bounces 0 -set g_balance_crylink_secondary_refire 0.7 -set g_balance_crylink_secondary_animtime 0.2 -set g_balance_crylink_secondary_ammo 2 -set g_balance_crylink_secondary_bouncedamagefactor 0.5 -set g_balance_crylink_secondary_joindelay 0 -set g_balance_crylink_secondary_joinspread 0 -set g_balance_crylink_secondary_joinexplode 0 -set g_balance_crylink_secondary_joinexplode_damage 0 -set g_balance_crylink_secondary_joinexplode_edgedamage 0 -set g_balance_crylink_secondary_joinexplode_radius 0 -set g_balance_crylink_secondary_joinexplode_force 0 -set g_balance_crylink_secondary_linkexplode 1 - -set g_balance_crylink_secondary_middle_lifetime 5 // range: 35000 full, fades to 70000 -set g_balance_crylink_secondary_middle_fadetime 5 -set g_balance_crylink_secondary_line_lifetime 5 -set g_balance_crylink_secondary_line_fadetime 5 - -set g_balance_crylink_switchdelay_drop 0 -set g_balance_crylink_switchdelay_raise 0 - -set g_balance_crylink_reload_ammo 0 //default: 10 -set g_balance_crylink_reload_time 2 -// }}} -// {{{ nex -set g_balance_nex_primary_damage 90 -set g_balance_nex_primary_force 400 -set g_balance_nex_primary_refire 1.5 -set g_balance_nex_primary_animtime 0.4 -set g_balance_nex_primary_ammo 6 -set g_balance_nex_primary_damagefalloff_mindist 0 // 1000 For tZork ;3 -set g_balance_nex_primary_damagefalloff_maxdist 0 // 3000 -set g_balance_nex_primary_damagefalloff_halflife 0 // 1500 -set g_balance_nex_primary_damagefalloff_forcehalflife 0 // 1500 - -set g_balance_nex_secondary 0 -set g_balance_nex_secondary_charge 0 -set g_balance_nex_secondary_charge_rate 0.1 -set g_balance_nex_secondary_chargepool 0 -set g_balance_nex_secondary_chargepool_regen 0.15 -set g_balance_nex_secondary_chargepool_pause_regen 1 -set g_balance_nex_secondary_chargepool_pause_health_regen 1 -set g_balance_nex_secondary_damage 0 -set g_balance_nex_secondary_force 0 -set g_balance_nex_secondary_refire 0 -set g_balance_nex_secondary_animtime 0 -set g_balance_nex_secondary_ammo 2 -set g_balance_nex_secondary_damagefalloff_mindist 0 -set g_balance_nex_secondary_damagefalloff_maxdist 0 -set g_balance_nex_secondary_damagefalloff_halflife 0 -set g_balance_nex_secondary_damagefalloff_forcehalflife 0 - -set g_balance_nex_charge 1 -set g_balance_nex_charge_mindmg 40 -set g_balance_nex_charge_start 0.5 -set g_balance_nex_charge_rate 0.4 -set g_balance_nex_charge_animlimit 0.5 -set g_balance_nex_charge_limit 1 -set g_balance_nex_charge_rot_rate 0 -set g_balance_nex_charge_rot_pause 0 // Dont rot down untill this long after release of charge button -set g_balance_nex_charge_shot_multiplier 0 -set g_balance_nex_charge_velocity_rate 0 -set g_balance_nex_charge_minspeed 400 -set g_balance_nex_charge_maxspeed 800 - -set g_balance_nex_switchdelay_drop 0 -set g_balance_nex_switchdelay_raise 0 - -set g_balance_nex_reload_ammo 0 //default: 25 -set g_balance_nex_reload_time 2 -// }}} -// {{{ minstanex -set g_balance_minstanex_refire 1 -set g_balance_minstanex_animtime 0.3 -set g_balance_minstanex_ammo 10 -set g_balance_minstanex_laser_ammo 0 -set g_balance_minstanex_laser_animtime 0.3 -set g_balance_minstanex_laser_refire 0.7 -set g_balance_minstanex_switchdelay_drop 0 -set g_balance_minstanex_switchdelay_raise 0 -set g_balance_minstanex_reload_ammo 0 //default: 50 -set g_balance_minstanex_reload_time 2 -// }}} -// {{{ hagar -set g_balance_hagar_primary_damage 25 -set g_balance_hagar_primary_edgedamage 12.5 -set g_balance_hagar_primary_force 92 -set g_balance_hagar_primary_health 15 -set g_balance_hagar_primary_damageforcescale 0 -set g_balance_hagar_primary_radius 25 -set g_balance_hagar_primary_spread 0.03 -set g_balance_hagar_primary_speed 2000 -set g_balance_hagar_primary_lifetime 5 -set g_balance_hagar_primary_refire 0.11 -set g_balance_hagar_primary_ammo 1 -set g_balance_hagar_secondary 0 -set g_balance_hagar_secondary_load 1 -set g_balance_hagar_secondary_load_speed 0.5 -set g_balance_hagar_secondary_load_spread 0.075 -set g_balance_hagar_secondary_load_spread_bias 0.5 -set g_balance_hagar_secondary_load_max 4 -set g_balance_hagar_secondary_load_hold 4 -set g_balance_hagar_secondary_load_releasedeath 0 -set g_balance_hagar_secondary_load_abort 0 -set g_balance_hagar_secondary_load_linkexplode 0 -set g_balance_hagar_secondary_load_animtime 0.2 -set g_balance_hagar_secondary_damage 40 -set g_balance_hagar_secondary_edgedamage 20 -set g_balance_hagar_secondary_force 75 -set g_balance_hagar_secondary_health 15 -set g_balance_hagar_secondary_damageforcescale 0 -set g_balance_hagar_secondary_radius 80 -set g_balance_hagar_secondary_spread 0.05 -set g_balance_hagar_secondary_speed 2000 -set g_balance_hagar_secondary_lifetime_min 10 -set g_balance_hagar_secondary_lifetime_rand 0 -set g_balance_hagar_secondary_refire 0.5 -set g_balance_hagar_secondary_ammo 1 -set g_balance_hagar_switchdelay_drop 0 -set g_balance_hagar_switchdelay_raise 0 -set g_balance_hagar_reload_ammo 0 //default: 25 -set g_balance_hagar_reload_time 2 -// }}} -// {{{ rocketlauncher -set g_balance_rocketlauncher_damage 80 -set g_balance_rocketlauncher_edgedamage 40 -set g_balance_rocketlauncher_force 350 -set g_balance_rocketlauncher_radius 110 -set g_balance_rocketlauncher_speed 1000 -set g_balance_rocketlauncher_speedaccel 0 -set g_balance_rocketlauncher_speedstart 1000 -set g_balance_rocketlauncher_lifetime 100 -set g_balance_rocketlauncher_refire 0.9 -set g_balance_rocketlauncher_animtime 0.7 -set g_balance_rocketlauncher_ammo 4 -set g_balance_rocketlauncher_health 0 // 30 // 5 hitpoints above maximum laser value -- this way lasers can't blow it up, but grenadelauncher still can most the time. -set g_balance_rocketlauncher_damageforcescale 0 // low damage force scale so that it can still be affected by other hits, but not so much that it does a 90 degree turn -set g_balance_rocketlauncher_detonatedelay 999 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time -set g_balance_rocketlauncher_guiderate 0 // max degrees per second -set g_balance_rocketlauncher_guideratedelay 0.01 // immediate -set g_balance_rocketlauncher_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic) -set g_balance_rocketlauncher_guidedelay 0.2 // delay before guiding kicks in -set g_balance_rocketlauncher_guidestop 1 // stop guiding when firing again -set g_balance_rocketlauncher_remote_damage 70 -set g_balance_rocketlauncher_remote_edgedamage 35 -set g_balance_rocketlauncher_remote_radius 110 -set g_balance_rocketlauncher_remote_force 350 -set g_balance_rocketlauncher_switchdelay_drop 0 -set g_balance_rocketlauncher_switchdelay_raise 0 -set g_balance_rocketlauncher_reload_ammo 0 //default: 25 -set g_balance_rocketlauncher_reload_time 2 -// }}} -// {{{ porto -set g_balance_porto_primary_refire 1.5 -set g_balance_porto_primary_animtime 0.3 -set g_balance_porto_primary_speed 5000 -set g_balance_porto_primary_lifetime 5 -set g_balance_porto_secondary 1 -set g_balance_porto_secondary_refire 1.5 -set g_balance_porto_secondary_animtime 0.3 -set g_balance_porto_secondary_speed 1000 -set g_balance_porto_secondary_lifetime 5 -set g_balance_porto_switchdelay_drop 0 -set g_balance_porto_switchdelay_raise 0 -set g_balance_portal_health 200 // these get recharged whenever the portal is used -set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used -// }}} -// {{{ hook -set g_balance_hook_primary_fuel 5 // hook monkeys set 0 -set g_balance_hook_primary_refire 0.2 // hook monkeys set 0 -set g_balance_hook_primary_animtime 0.3 // good shoot anim -set g_balance_hook_primary_hooked_time_max 0 // infinite -set g_balance_hook_primary_hooked_time_free 2 // 2s being hooked are free -set g_balance_hook_primary_hooked_fuel 5 // fuel per second hooked -set g_balance_hook_secondary_damage 25 // not much -set g_balance_hook_secondary_edgedamage 5 // not much -set g_balance_hook_secondary_radius 500 // LOTS -set g_balance_hook_secondary_force -2000 // LOTS -set g_balance_hook_secondary_ammo 50 // a whole pack -set g_balance_hook_secondary_lifetime 5 // infinite -set g_balance_hook_secondary_speed 0 // not much throwing -set g_balance_hook_secondary_gravity 5 // fast falling -set g_balance_hook_secondary_refire 3 // don't drop too many bombs... -set g_balance_hook_secondary_animtime 0.3 // good shoot anim -set g_balance_hook_secondary_power 3 // effect behaves like a square function -set g_balance_hook_secondary_duration 1.5 // effect runs for three seconds -set g_balance_hook_secondary_health 15 -set g_balance_hook_secondary_damageforcescale 0 -set g_balance_hook_switchdelay_drop 0 -set g_balance_hook_switchdelay_raise 0 -// }}} -// {{{ tuba -set g_balance_tuba_refire 0.05 -set g_balance_tuba_animtime 0.05 -set g_balance_tuba_attenuation 0.5 -set g_balance_tuba_volume 1 -set g_balance_tuba_fadetime 0.25 -set g_balance_tuba_damage 5 -set g_balance_tuba_edgedamage 0 -set g_balance_tuba_radius 200 -set g_balance_tuba_force 40 -set g_balance_tuba_pitchstep 6 -set g_balance_tuba_switchdelay_drop 0 -set g_balance_tuba_switchdelay_raise 0 -// }}} -// {{{ fireball // this is a superweapon -- lets make it behave as one. -set g_balance_fireball_primary_animtime 0.2 -set g_balance_fireball_primary_bfgdamage 100 -set g_balance_fireball_primary_bfgforce 0 -set g_balance_fireball_primary_bfgradius 1000 -set g_balance_fireball_primary_damage 200 -set g_balance_fireball_primary_damageforcescale 0 -set g_balance_fireball_primary_edgedamage 50 -set g_balance_fireball_primary_force 600 -set g_balance_fireball_primary_health 0 -set g_balance_fireball_primary_laserburntime 0.5 -set g_balance_fireball_primary_laserdamage 80 -set g_balance_fireball_primary_laseredgedamage 20 -set g_balance_fireball_primary_laserradius 256 -set g_balance_fireball_primary_lifetime 15 -set g_balance_fireball_primary_radius 200 -set g_balance_fireball_primary_refire 2 -set g_balance_fireball_primary_refire2 0 -set g_balance_fireball_primary_speed 1200 -set g_balance_fireball_primary_spread 0 -set g_balance_fireball_secondary_animtime 0.3 -set g_balance_fireball_secondary_damage 40 -set g_balance_fireball_secondary_damageforcescale 4 -set g_balance_fireball_secondary_damagetime 5 -set g_balance_fireball_secondary_force 100 -set g_balance_fireball_secondary_laserburntime 0.5 -set g_balance_fireball_secondary_laserdamage 50 -set g_balance_fireball_secondary_laseredgedamage 20 -set g_balance_fireball_secondary_laserradius 110 -set g_balance_fireball_secondary_lifetime 7 -set g_balance_fireball_secondary_refire 1.5 -set g_balance_fireball_secondary_speed 900 -set g_balance_fireball_secondary_speed_up 100 -set g_balance_fireball_secondary_speed_z 0 -set g_balance_fireball_secondary_spread 0 -set g_balance_fireball_switchdelay_drop 0 -set g_balance_fireball_switchdelay_raise 0 -// }}} diff --git a/balanceXPM.cfg b/balanceXPM.cfg deleted file mode 100644 index f560aee10b..0000000000 --- a/balanceXPM.cfg +++ /dev/null @@ -1,706 +0,0 @@ -g_mod_balance XPM - -// {{{ starting gear -set g_start_weapon_laser -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_shotgun -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_uzi -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_hagar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" // UNTIL IT CAN BE REMOVED FROM CODE -set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_balance_health_start 100 -set g_balance_armor_start 0 -set g_start_ammo_shells 15 -set g_start_ammo_nails 0 -set g_start_ammo_rockets 0 -set g_start_ammo_cells 0 -set g_start_ammo_fuel 0 -set g_warmup_start_health 100 "starting values when being in warmup-stage" -set g_warmup_start_armor 100 "starting values when being in warmup-stage" -set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage" -set g_warmup_start_ammo_nails 160 "starting values when being in warmup-stage" -set g_warmup_start_ammo_rockets 80 "starting values when being in warmup-stage" -set g_warmup_start_ammo_cells 90 "starting values when being in warmup-stage" -set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage" -set g_lms_start_health 200 -set g_lms_start_armor 200 -set g_lms_start_ammo_shells 60 -set g_lms_start_ammo_nails 320 -set g_lms_start_ammo_rockets 160 -set g_lms_start_ammo_cells 180 -set g_lms_start_ammo_fuel 0 -set g_balance_nix_roundtime 25 -set g_balance_nix_incrtime 1.6 -set g_balance_nix_ammo_shells 60 -set g_balance_nix_ammo_nails 320 -set g_balance_nix_ammo_rockets 160 -set g_balance_nix_ammo_cells 180 -set g_balance_nix_ammo_fuel 0 -set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume -set g_balance_nix_ammoincr_nails 6 -set g_balance_nix_ammoincr_rockets 2 -set g_balance_nix_ammoincr_cells 2 -set g_balance_nix_ammoincr_fuel 2 -// }}} - -// {{{ pickup items -set g_pickup_ammo_anyway 1 -set g_pickup_weapons_anyway 1 -set g_pickup_shells 15 -set g_pickup_shells_weapon 15 -set g_pickup_shells_max 60 -set g_pickup_nails 80 -set g_pickup_nails_weapon 80 -set g_pickup_nails_max 320 -set g_pickup_rockets 40 -set g_pickup_rockets_weapon 40 -set g_pickup_rockets_max 160 -set g_pickup_cells 30 -set g_pickup_cells_weapon 30 -set g_pickup_cells_max 180 -set g_pickup_fuel 50 -set g_pickup_fuel_weapon 50 -set g_pickup_fuel_jetpack 100 -set g_pickup_fuel_max 100 -set g_pickup_armorsmall 5 -set g_pickup_armorsmall_max 200 -set g_pickup_armorsmall_anyway 0 -set g_pickup_armormedium 25 -set g_pickup_armormedium_max 100 -set g_pickup_armormedium_anyway 0 -set g_pickup_armorbig 50 -set g_pickup_armorbig_max 100 -set g_pickup_armorbig_anyway 0 -set g_pickup_armorlarge 100 -set g_pickup_armorlarge_max 200 -set g_pickup_armorlarge_anyway 0 -set g_pickup_healthsmall 5 -set g_pickup_healthsmall_max 200 -set g_pickup_healthsmall_anyway 0 -set g_pickup_healthmedium 25 -set g_pickup_healthmedium_max 100 -set g_pickup_healthmedium_anyway 0 -set g_pickup_healthlarge 50 -set g_pickup_healthlarge_max 100 -set g_pickup_healthlarge_anyway 0 -set g_pickup_healthmega 100 -set g_pickup_healthmega_max 200 -set g_pickup_healthmega_anyway 0 -set g_pickup_respawntime_short 15 -set g_pickup_respawntime_medium 20 -set g_pickup_respawntime_long 30 -set g_pickup_respawntime_powerup 120 -set g_pickup_respawntime_weapon 10 -set g_pickup_respawntime_superweapon 120 -set g_pickup_respawntime_ammo 15 -set g_pickup_respawntimejitter_short 0 -set g_pickup_respawntimejitter_medium 0 -set g_pickup_respawntimejitter_long 0 -set g_pickup_respawntimejitter_powerup 30 -set g_pickup_respawntimejitter_weapon 0 -set g_pickup_respawntimejitter_superweapon 10 -set g_pickup_respawntimejitter_ammo 0 -// }}} - -// {{{ regen/rot -set g_balance_health_regen 0.08 -set g_balance_health_regenlinear 0.5 -set g_balance_pause_health_regen 5 -set g_balance_pause_health_regen_spawn 0 -set g_balance_health_rot 0.03 -set g_balance_health_rotlinear 0.75 -set g_balance_pause_health_rot 1 -set g_balance_pause_health_rot_spawn 5 -set g_balance_health_regenstable 100 -set g_balance_health_rotstable 100 -set g_balance_health_limit 999 -set g_balance_armor_regen 0 -set g_balance_armor_regenlinear 0 -set g_balance_armor_rot 0.03 -set g_balance_armor_rotlinear 0.75 -set g_balance_pause_armor_rot 1 -set g_balance_pause_armor_rot_spawn 5 -set g_balance_armor_regenstable 100 -set g_balance_armor_rotstable 100 -set g_balance_armor_limit 999 -set g_balance_armor_blockpercent 0.6 -set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)" -set g_balance_fuel_regenlinear 0 -set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter -set g_balance_fuel_rot 0.05 -set g_balance_fuel_rotlinear 0 -set g_balance_pause_fuel_rot 5 -set g_balance_pause_fuel_rot_spawn 10 -set g_balance_fuel_regenstable 50 -set g_balance_fuel_rotstable 100 -set g_balance_fuel_limit 999 -// }}} - -// {{{ misc -set g_balance_selfdamagepercent 0.65 -set g_weaponspeedfactor 1 "weapon projectile speed multiplier" -set g_weaponratefactor 1 "weapon fire rate multiplier" -set g_weapondamagefactor 1 "weapon damage multiplier" -set g_weaponforcefactor 1 "weapon force multiplier" -set g_weaponspreadfactor 1 "weapon spread multiplier" -set g_balance_firetransfer_time 0.9 -set g_balance_firetransfer_damage 0.8 -set g_throughfloor_damage 0.75 -set g_throughfloor_force 0.75 -set g_projectiles_damage 1 -// possible values: -// -2: absolutely no damage to projectiles (no exceptions) -// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines) -// 0: only damage from contents (lava/slime) or exceptions -// 1: only self damage or damage from contents or exceptions -// 2: allow all damage to projectiles normally -set g_projectiles_keep_owner 0 -set g_projectiles_newton_style 0 -// possible values: -// 0: absolute velocity projectiles (like Quake) -// 1: relative velocity projectiles, "Newtonian" (like Tribes 2) -// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard) -set g_projectiles_newton_style_2_minfactor 0.8 -set g_projectiles_newton_style_2_maxfactor 1.5 -set g_projectiles_spread_style 7 -// possible values: -// 0: forward + solid sphere (like Quake) - varies velocity -// 1: forward + flattened solid sphere -// 2: forward + solid circle -// 3: forward + normal distribution 3D - varies velocity -// 4: forward + normal distribution on a plane -// 5: forward + circle with 1-r falloff -// 6: forward + circle with 1-r^2 falloff -// 7: forward + circle with (1-r)(2-r) falloff -set g_balance_falldamage_deadminspeed 250 -set g_balance_falldamage_minspeed 900 -set g_balance_falldamage_factor 0.20 -set g_balance_falldamage_maxdamage 40 -set g_balance_damagepush_speedfactor 2.5 -set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage -set g_balance_contents_drowndelay 10 // time under water before a player begins drowning -set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning -set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava -set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime -set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime -set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap" -// }}} - -// {{{ powerups -set g_balance_powerup_invincible_takedamage 0.25 // only 1/4th damage is taken -set g_balance_powerup_invincible_time 30 -set g_balance_powerup_strength_damage 3 -set g_balance_powerup_strength_force 3 -set g_balance_powerup_strength_time 30 -set g_balance_powerup_strength_selfdamage 1.5 -set g_balance_powerup_strength_selfforce 1.5 -set g_balance_superweapons_time 30 -// }}} - -// {{{ jetpack/hook -set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack" -set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction" -set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)" -set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction" -set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" -set g_jetpack_fuel 8 "fuel per second for jetpack" -set g_jetpack_attenuation 2 "jetpack sound attenuation" - -set g_grappling_hook_tarzan 2 // 2: can also pull players -set g_balance_grapplehook_speed_fly 1800 -set g_balance_grapplehook_speed_pull 2000 -set g_balance_grapplehook_force_rubber 2000 -set g_balance_grapplehook_force_rubber_overstretch 1000 -set g_balance_grapplehook_length_min 50 -set g_balance_grapplehook_stretch 50 -set g_balance_grapplehook_airfriction 0.2 -set g_balance_grapplehook_health 50 -set g_balance_grapplehook_damagedbycontents 1 -set g_balance_grapplehook_refire 0.2 -// }}} - -// {{{ weapon properties -// {{{ laser -set g_balance_laser_primary_damage 25 -set g_balance_laser_primary_edgedamage 12.5 -set g_balance_laser_primary_force 300 -set g_balance_laser_primary_radius 70 -set g_balance_laser_primary_speed 6000 -set g_balance_laser_primary_spread 0 -set g_balance_laser_primary_refire 0.7 -set g_balance_laser_primary_animtime 0.2 -set g_balance_laser_primary_lifetime 5 -set g_balance_laser_primary_shotangle 0 -set g_balance_laser_primary_delay 0 -set g_balance_laser_primary_gauntlet 0 -set g_balance_laser_primary_force_zscale 1.2 -set g_balance_laser_primary_force_velocitybias 0 -set g_balance_laser_primary_force_other_scale 1 -set g_balance_laser_secondary 0 // when 1, a secondary laser mode exists -set g_balance_laser_secondary_damage 25 -set g_balance_laser_secondary_edgedamage 12.5 -set g_balance_laser_secondary_force 400 -set g_balance_laser_secondary_radius 70 -set g_balance_laser_secondary_speed 12000 -set g_balance_laser_secondary_spread 0 -set g_balance_laser_secondary_refire 0.7 -set g_balance_laser_secondary_animtime 0.2 -set g_balance_laser_secondary_lifetime 5 -set g_balance_laser_secondary_shotangle -90 -set g_balance_laser_secondary_delay 0 -set g_balance_laser_secondary_gauntlet 0 -set g_balance_laser_secondary_force_zscale 1.25 -set g_balance_laser_secondary_force_velocitybias 0 -set g_balance_laser_secondary_force_other_scale 1 -set g_balance_laser_switchdelay_drop 0.15 -set g_balance_laser_switchdelay_raise 0.15 -set g_balance_laser_reload_ammo 0 //default: 6 -set g_balance_laser_reload_time 2 -// }}} -// {{{ shotgun -set g_balance_shotgun_primary_bullets 14 -set g_balance_shotgun_primary_damage 4 -set g_balance_shotgun_primary_force 15 -set g_balance_shotgun_primary_spread 0.12 -set g_balance_shotgun_primary_refire 0.75 -set g_balance_shotgun_primary_animtime 0.2 -set g_balance_shotgun_primary_ammo 1 -set g_balance_shotgun_primary_solidpenetration 3.8 -set g_balance_shotgun_secondary 1 -set g_balance_shotgun_secondary_melee_delay 0.25 // 0.35 was too slow -set g_balance_shotgun_secondary_melee_range 120 -set g_balance_shotgun_secondary_melee_swing_side 120 -set g_balance_shotgun_secondary_melee_swing_up 30 -set g_balance_shotgun_secondary_melee_time 0.15 -set g_balance_shotgun_secondary_melee_traces 10 -set g_balance_shotgun_secondary_melee_no_doubleslap 1 -set g_balance_shotgun_secondary_melee_nonplayerdamage 40 -set g_balance_shotgun_secondary_melee_multihit 1 -set g_balance_shotgun_secondary_damage 80 -set g_balance_shotgun_secondary_force 200 -set g_balance_shotgun_secondary_refire 1.25 -set g_balance_shotgun_secondary_animtime 1 -set g_balance_shotgun_switchdelay_drop 0.2 -set g_balance_shotgun_switchdelay_raise 0.2 -set g_balance_shotgun_reload_ammo 0 //default: 5 -set g_balance_shotgun_reload_time 2 -// }}} -// {{{ uzi -set g_balance_uzi_mode 1 // Activates varible spread for sustained & burst mode secondary -set g_balance_uzi_spread_min 0.02 -set g_balance_uzi_spread_max 0.05 -set g_balance_uzi_spread_add 0.012 - -set g_balance_uzi_burst 3 // # of bullets in a burst (if set to 2 or more) -set g_balance_uzi_burst_animtime 0.3 -set g_balance_uzi_burst_refire 0.06 // refire between burst bullets -set g_balance_uzi_burst_refire2 0.45 // refire after burst -set g_balance_uzi_burst_spread 0.02 -set g_balance_uzi_burst_damage 25 -set g_balance_uzi_burst_force 20 -set g_balance_uzi_burst_ammo 3 - -set g_balance_uzi_first 1 -set g_balance_uzi_first_damage 14 -set g_balance_uzi_first_force 5 -set g_balance_uzi_first_spread 0.03 -set g_balance_uzi_first_refire 0.125 -set g_balance_uzi_first_ammo 1 - -set g_balance_uzi_sustained_damage 10 // 100 dps -set g_balance_uzi_sustained_force 5 -set g_balance_uzi_sustained_spread 0.03 -set g_balance_uzi_sustained_refire 0.1 -set g_balance_uzi_sustained_ammo 1 - -set g_balance_uzi_solidpenetration 13.1 - -set g_balance_uzi_switchdelay_drop 0.2 -set g_balance_uzi_switchdelay_raise 0.2 - -set g_balance_uzi_reload_ammo 60 //default: 30 -set g_balance_uzi_reload_time 2 -// }}} -// {{{ mortar -set g_balance_grenadelauncher_primary_type 0 -set g_balance_grenadelauncher_primary_damage 50 -set g_balance_grenadelauncher_primary_edgedamage 25 -set g_balance_grenadelauncher_primary_force 250 -set g_balance_grenadelauncher_primary_radius 120 -set g_balance_grenadelauncher_primary_speed 1900 -set g_balance_grenadelauncher_primary_speed_up 225 -set g_balance_grenadelauncher_primary_speed_z 0 -set g_balance_grenadelauncher_primary_spread 0 -set g_balance_grenadelauncher_primary_lifetime 5 -set g_balance_grenadelauncher_primary_lifetime2 1 -set g_balance_grenadelauncher_primary_refire 0.8 -set g_balance_grenadelauncher_primary_animtime 0.3 -set g_balance_grenadelauncher_primary_ammo 2 -set g_balance_grenadelauncher_primary_health 15 -set g_balance_grenadelauncher_primary_damageforcescale 0 -set g_balance_grenadelauncher_primary_remote_minbouncecnt 0 - -set g_balance_grenadelauncher_secondary_type 1 -set g_balance_grenadelauncher_secondary_damage 60 -set g_balance_grenadelauncher_secondary_edgedamage 30 -set g_balance_grenadelauncher_secondary_force 250 -set g_balance_grenadelauncher_secondary_radius 120 -set g_balance_grenadelauncher_secondary_speed 1400 -set g_balance_grenadelauncher_secondary_speed_up 150 -set g_balance_grenadelauncher_secondary_speed_z 0 -set g_balance_grenadelauncher_secondary_spread 0 -set g_balance_grenadelauncher_secondary_lifetime 5 -set g_balance_grenadelauncher_secondary_lifetime_bounce 0.5 -set g_balance_grenadelauncher_secondary_lifetime_stick 0 -set g_balance_grenadelauncher_secondary_refire 0.7 -set g_balance_grenadelauncher_secondary_animtime 0.3 -set g_balance_grenadelauncher_secondary_ammo 2 -set g_balance_grenadelauncher_secondary_health 30 -set g_balance_grenadelauncher_secondary_damageforcescale 4 -set g_balance_grenadelauncher_secondary_remote_detonateprimary 0 - -set g_balance_grenadelauncher_bouncefactor 0.5 -set g_balance_grenadelauncher_bouncestop 0.075 - -set g_balance_grenadelauncher_switchdelay_drop 0.2 -set g_balance_grenadelauncher_switchdelay_raise 0.2 - -set g_balance_grenadelauncher_reload_ammo 0 //default: 12 -set g_balance_grenadelauncher_reload_time 2 -// }}} -// {{{ electro -set g_balance_electro_lightning 0 -set g_balance_electro_primary_damage 40 -set g_balance_electro_primary_edgedamage 20 -set g_balance_electro_primary_force 200 -set g_balance_electro_primary_force_up 0 -set g_balance_electro_primary_radius 100 -set g_balance_electro_primary_comboradius 300 -set g_balance_electro_primary_speed 2500 -set g_balance_electro_primary_spread 0 -set g_balance_electro_primary_lifetime 5 -set g_balance_electro_primary_refire 0.6 -set g_balance_electro_primary_animtime 0.3 -set g_balance_electro_primary_ammo 4 -set g_balance_electro_primary_range 0 -set g_balance_electro_primary_falloff_mindist 255 // 0.3 * radius -set g_balance_electro_primary_falloff_maxdist 850 -set g_balance_electro_primary_falloff_halflifedist 425 -set g_balance_electro_secondary_damage 40 -set g_balance_electro_secondary_edgedamage 20 -set g_balance_electro_secondary_force 50 -set g_balance_electro_secondary_radius 150 -set g_balance_electro_secondary_speed 1000 -set g_balance_electro_secondary_speed_up 200 -set g_balance_electro_secondary_speed_z 0 -set g_balance_electro_secondary_spread 0.04 -set g_balance_electro_secondary_lifetime 4 -set g_balance_electro_secondary_refire 0.2 -set g_balance_electro_secondary_refire2 1.6 -set g_balance_electro_secondary_animtime 0.2 -set g_balance_electro_secondary_ammo 2 -set g_balance_electro_secondary_health 5 -set g_balance_electro_secondary_damageforcescale 4 -set g_balance_electro_secondary_damagedbycontents 1 -set g_balance_electro_secondary_count 3 -set g_balance_electro_secondary_bouncefactor 0.3 -set g_balance_electro_secondary_bouncestop 0.05 -set g_balance_electro_combo_damage 50 -set g_balance_electro_combo_edgedamage 25 -set g_balance_electro_combo_force 120 -set g_balance_electro_combo_radius 150 -set g_balance_electro_combo_comboradius 300 -set g_balance_electro_combo_speed 2000 -set g_balance_electro_combo_safeammocheck 1 -set g_balance_electro_switchdelay_drop 0.2 -set g_balance_electro_switchdelay_raise 0.2 -set g_balance_electro_reload_ammo 0 //default: 20 -set g_balance_electro_reload_time 2 -// }}} -// {{{ crylink -set g_balance_crylink_primary_damage 12 -set g_balance_crylink_primary_edgedamage 6 -set g_balance_crylink_primary_force -50 -set g_balance_crylink_primary_radius 80 -set g_balance_crylink_primary_speed 2000 -set g_balance_crylink_primary_spread 0.08 -set g_balance_crylink_primary_shots 6 -set g_balance_crylink_primary_bounces 1 -set g_balance_crylink_primary_refire 0.7 -set g_balance_crylink_primary_animtime 0.3 -set g_balance_crylink_primary_ammo 3 -set g_balance_crylink_primary_bouncedamagefactor 0.5 -set g_balance_crylink_primary_joindelay 0.1 -set g_balance_crylink_primary_joinspread 0.2 -set g_balance_crylink_primary_joinexplode 1 -set g_balance_crylink_primary_joinexplode_damage 0 -set g_balance_crylink_primary_joinexplode_edgedamage 0 -set g_balance_crylink_primary_joinexplode_radius 0 -set g_balance_crylink_primary_joinexplode_force 0 -set g_balance_crylink_primary_linkexplode 1 - -set g_balance_crylink_primary_middle_lifetime 5 // range: 35000 full, fades to 70000 -set g_balance_crylink_primary_middle_fadetime 5 -set g_balance_crylink_primary_other_lifetime 5 -set g_balance_crylink_primary_other_fadetime 5 - -set g_balance_crylink_secondary 1 -set g_balance_crylink_secondary_damage 10 -set g_balance_crylink_secondary_edgedamage 5 -set g_balance_crylink_secondary_force -250 -set g_balance_crylink_secondary_radius 100 -set g_balance_crylink_secondary_speed 3000 -set g_balance_crylink_secondary_spread 0.01 -set g_balance_crylink_secondary_spreadtype 1 -set g_balance_crylink_secondary_shots 5 -set g_balance_crylink_secondary_bounces 0 -set g_balance_crylink_secondary_refire 0.7 -set g_balance_crylink_secondary_animtime 0.2 -set g_balance_crylink_secondary_ammo 2 -set g_balance_crylink_secondary_bouncedamagefactor 0.5 -set g_balance_crylink_secondary_joindelay 0 -set g_balance_crylink_secondary_joinspread 0 -set g_balance_crylink_secondary_joinexplode 0 -set g_balance_crylink_secondary_joinexplode_damage 0 -set g_balance_crylink_secondary_joinexplode_edgedamage 0 -set g_balance_crylink_secondary_joinexplode_radius 0 -set g_balance_crylink_secondary_joinexplode_force 0 -set g_balance_crylink_secondary_linkexplode 1 - -set g_balance_crylink_secondary_middle_lifetime 5 // range: 35000 full, fades to 70000 -set g_balance_crylink_secondary_middle_fadetime 5 -set g_balance_crylink_secondary_line_lifetime 5 -set g_balance_crylink_secondary_line_fadetime 5 - -set g_balance_crylink_switchdelay_drop 0.2 -set g_balance_crylink_switchdelay_raise 0.2 - -set g_balance_crylink_reload_ammo 0 //default: 10 -set g_balance_crylink_reload_time 2 -// }}} -// {{{ nex -set g_balance_nex_primary_damage 80 -set g_balance_nex_primary_force 400 -set g_balance_nex_primary_refire 1.5 -set g_balance_nex_primary_animtime 0.6 -set g_balance_nex_primary_ammo 6 -set g_balance_nex_primary_damagefalloff_mindist 0 // 1000 For tZork ;3 -set g_balance_nex_primary_damagefalloff_maxdist 0 // 3000 -set g_balance_nex_primary_damagefalloff_halflife 0 // 1500 -set g_balance_nex_primary_damagefalloff_forcehalflife 0 // 1500 - -set g_balance_nex_secondary 0 -set g_balance_nex_secondary_charge 0 -set g_balance_nex_secondary_charge_rate 0.1 -set g_balance_nex_secondary_chargepool 0 -set g_balance_nex_secondary_chargepool_regen 0.15 -set g_balance_nex_secondary_chargepool_pause_regen 1 -set g_balance_nex_secondary_chargepool_pause_health_regen 1 -set g_balance_nex_secondary_damage 0 -set g_balance_nex_secondary_force 0 -set g_balance_nex_secondary_refire 0 -set g_balance_nex_secondary_animtime 0 -set g_balance_nex_secondary_ammo 2 -set g_balance_nex_secondary_damagefalloff_mindist 0 -set g_balance_nex_secondary_damagefalloff_maxdist 0 -set g_balance_nex_secondary_damagefalloff_halflife 0 -set g_balance_nex_secondary_damagefalloff_forcehalflife 0 - -set g_balance_nex_charge 1 -set g_balance_nex_charge_mindmg 40 -set g_balance_nex_charge_start 0.5 -set g_balance_nex_charge_rate 0.4 -set g_balance_nex_charge_animlimit 0.5 -set g_balance_nex_charge_limit 1 -set g_balance_nex_charge_rot_rate 0 -set g_balance_nex_charge_rot_pause 0 // Dont rot down until this long after release of charge button -set g_balance_nex_charge_shot_multiplier 0 -set g_balance_nex_charge_velocity_rate 0 -set g_balance_nex_charge_minspeed 400 -set g_balance_nex_charge_maxspeed 800 - -set g_balance_nex_switchdelay_drop 0.3 -set g_balance_nex_switchdelay_raise 0.25 - -set g_balance_nex_reload_ammo 0 //default: 25 -set g_balance_nex_reload_time 2 -// }}} -// {{{ minstanex -set g_balance_minstanex_refire 1 -set g_balance_minstanex_animtime 0.3 -set g_balance_minstanex_ammo 10 -set g_balance_minstanex_laser_ammo 0 -set g_balance_minstanex_laser_animtime 0.3 -set g_balance_minstanex_laser_refire 0.7 -set g_balance_minstanex_switchdelay_drop 0.2 -set g_balance_minstanex_switchdelay_raise 0.2 -set g_balance_minstanex_reload_ammo 0 //default: 50 -set g_balance_minstanex_reload_time 2 -// }}} -// {{{ hagar -set g_balance_hagar_primary_damage 25 -set g_balance_hagar_primary_edgedamage 12.5 -set g_balance_hagar_primary_force 100 -set g_balance_hagar_primary_health 15 -set g_balance_hagar_primary_damageforcescale 0 -set g_balance_hagar_primary_radius 65 -set g_balance_hagar_primary_spread 0.03 -set g_balance_hagar_primary_speed 2500 -set g_balance_hagar_primary_lifetime 5 -set g_balance_hagar_primary_refire 0.16667 // 6 rockets per second -set g_balance_hagar_primary_ammo 1 -set g_balance_hagar_secondary 1 -set g_balance_hagar_secondary_load 1 -set g_balance_hagar_secondary_load_speed 0.5 -set g_balance_hagar_secondary_load_spread 0.075 -set g_balance_hagar_secondary_load_spread_bias 0.5 -set g_balance_hagar_secondary_load_max 4 -set g_balance_hagar_secondary_load_hold 4 -set g_balance_hagar_secondary_load_releasedeath 0 -set g_balance_hagar_secondary_load_abort 0 -set g_balance_hagar_secondary_load_linkexplode 0 -set g_balance_hagar_secondary_load_animtime 0.2 -set g_balance_hagar_secondary_damage 40 -set g_balance_hagar_secondary_edgedamage 20 -set g_balance_hagar_secondary_force 75 -set g_balance_hagar_secondary_health 15 -set g_balance_hagar_secondary_damageforcescale 0 -set g_balance_hagar_secondary_radius 80 -set g_balance_hagar_secondary_spread 0.05 -set g_balance_hagar_secondary_speed 2000 -set g_balance_hagar_secondary_lifetime_min 10 -set g_balance_hagar_secondary_lifetime_rand 0 -set g_balance_hagar_secondary_refire 0.5 -set g_balance_hagar_secondary_ammo 1 -set g_balance_hagar_switchdelay_drop 0.2 -set g_balance_hagar_switchdelay_raise 0.2 -set g_balance_hagar_reload_ammo 0 //default: 25 -set g_balance_hagar_reload_time 2 -// }}} -// {{{ rocketlauncher -set g_balance_rocketlauncher_damage 70 -set g_balance_rocketlauncher_edgedamage 35 -set g_balance_rocketlauncher_force 450 -set g_balance_rocketlauncher_radius 110 -set g_balance_rocketlauncher_speed 1300 -set g_balance_rocketlauncher_speedaccel 1300 -set g_balance_rocketlauncher_speedstart 1000 -set g_balance_rocketlauncher_lifetime 10 -set g_balance_rocketlauncher_refire 1.2 -set g_balance_rocketlauncher_animtime 0.4 -set g_balance_rocketlauncher_ammo 4 -set g_balance_rocketlauncher_health 30 // 30 // 5 hitpoints above maximum laser value -- this way lasers can't blow it up, but grenadelauncher still can most the time. -set g_balance_rocketlauncher_damageforcescale 1 // low damage force scale so that it can still be affected by other hits, but not so much that it does a 90 degree turn -set g_balance_rocketlauncher_detonatedelay 0.02 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time -set g_balance_rocketlauncher_guiderate 70 // max degrees per second -set g_balance_rocketlauncher_guideratedelay 0.01 // immediate -set g_balance_rocketlauncher_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic) -set g_balance_rocketlauncher_guidedelay 0.2 // delay before guiding kicks in -set g_balance_rocketlauncher_guidestop 0 // stop guiding when firing again -set g_balance_rocketlauncher_remote_damage 70 -set g_balance_rocketlauncher_remote_edgedamage 35 -set g_balance_rocketlauncher_remote_radius 110 -set g_balance_rocketlauncher_remote_force 400 -set g_balance_rocketlauncher_switchdelay_drop 0.3 -set g_balance_rocketlauncher_switchdelay_raise 0.2 -set g_balance_rocketlauncher_reload_ammo 0 //default: 25 -set g_balance_rocketlauncher_reload_time 2 -// }}} -// {{{ porto -set g_balance_porto_primary_refire 1.5 -set g_balance_porto_primary_animtime 0.3 -set g_balance_porto_primary_speed 1000 -set g_balance_porto_primary_lifetime 5 -set g_balance_porto_secondary 1 -set g_balance_porto_secondary_refire 1.5 -set g_balance_porto_secondary_animtime 0.3 -set g_balance_porto_secondary_speed 1000 -set g_balance_porto_secondary_lifetime 5 -set g_balance_porto_switchdelay_drop 0.2 -set g_balance_porto_switchdelay_raise 0.2 -set g_balance_portal_health 200 // these get recharged whenever the portal is used -set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used -// }}} -// {{{ hook -set g_balance_hook_primary_fuel 5 // hook monkeys set 0 -set g_balance_hook_primary_refire 0.2 // hook monkeys set 0 -set g_balance_hook_primary_animtime 0.3 // good shoot anim -set g_balance_hook_primary_hooked_time_max 0 // infinite -set g_balance_hook_primary_hooked_time_free 2 // 2s being hooked are free -set g_balance_hook_primary_hooked_fuel 5 // fuel per second hooked -set g_balance_hook_secondary_damage 25 // not much -set g_balance_hook_secondary_edgedamage 5 // not much -set g_balance_hook_secondary_radius 500 // LOTS -set g_balance_hook_secondary_force -2000 // LOTS -set g_balance_hook_secondary_ammo 30 // a whole pack -set g_balance_hook_secondary_lifetime 5 // infinite -set g_balance_hook_secondary_speed 0 // not much throwing -set g_balance_hook_secondary_gravity 5 // fast falling -set g_balance_hook_secondary_refire 3 // don't drop too many bombs... -set g_balance_hook_secondary_animtime 0.3 // good shoot anim -set g_balance_hook_secondary_power 3 // effect behaves like a square function -set g_balance_hook_secondary_duration 1.5 // effect runs for three seconds -set g_balance_hook_secondary_health 15 -set g_balance_hook_secondary_damageforcescale 0 -set g_balance_hook_switchdelay_drop 0.2 -set g_balance_hook_switchdelay_raise 0.2 -// }}} -// {{{ tuba -set g_balance_tuba_refire 0.05 -set g_balance_tuba_animtime 0.05 -set g_balance_tuba_attenuation 0.5 -set g_balance_tuba_volume 1 -set g_balance_tuba_fadetime 0.25 -set g_balance_tuba_damage 5 -set g_balance_tuba_edgedamage 0 -set g_balance_tuba_radius 200 -set g_balance_tuba_force 40 -set g_balance_tuba_pitchstep 6 -set g_balance_tuba_switchdelay_drop 0.2 -set g_balance_tuba_switchdelay_raise 0.2 -// }}} -// {{{ fireball // this is a superweapon -- lets make it behave as one. -set g_balance_fireball_primary_animtime 0.2 -set g_balance_fireball_primary_bfgdamage 100 -set g_balance_fireball_primary_bfgforce 0 -set g_balance_fireball_primary_bfgradius 1000 -set g_balance_fireball_primary_damage 200 -set g_balance_fireball_primary_damageforcescale 0 -set g_balance_fireball_primary_edgedamage 50 -set g_balance_fireball_primary_force 600 -set g_balance_fireball_primary_health 0 -set g_balance_fireball_primary_laserburntime 0.5 -set g_balance_fireball_primary_laserdamage 80 -set g_balance_fireball_primary_laseredgedamage 20 -set g_balance_fireball_primary_laserradius 256 -set g_balance_fireball_primary_lifetime 15 -set g_balance_fireball_primary_radius 200 -set g_balance_fireball_primary_refire 2 -set g_balance_fireball_primary_refire2 0 -set g_balance_fireball_primary_speed 1200 -set g_balance_fireball_primary_spread 0 -set g_balance_fireball_secondary_animtime 0.3 -set g_balance_fireball_secondary_damage 40 -set g_balance_fireball_secondary_damageforcescale 4 -set g_balance_fireball_secondary_damagetime 5 -set g_balance_fireball_secondary_force 100 -set g_balance_fireball_secondary_laserburntime 0.5 -set g_balance_fireball_secondary_laserdamage 50 -set g_balance_fireball_secondary_laseredgedamage 20 -set g_balance_fireball_secondary_laserradius 110 -set g_balance_fireball_secondary_lifetime 7 -set g_balance_fireball_secondary_refire 1.5 -set g_balance_fireball_secondary_speed 900 -set g_balance_fireball_secondary_speed_up 100 -set g_balance_fireball_secondary_speed_z 0 -set g_balance_fireball_secondary_spread 0 -set g_balance_fireball_switchdelay_drop 0.2 -set g_balance_fireball_switchdelay_raise 0.2 -// }}} diff --git a/balanceXonotic.cfg b/balanceXonotic.cfg deleted file mode 100644 index 1988015472..0000000000 --- a/balanceXonotic.cfg +++ /dev/null @@ -1,706 +0,0 @@ -g_mod_balance Xonotic - -// {{{ starting gear -set g_start_weapon_laser -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_shotgun -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_uzi -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_hagar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" // UNTIL IT CAN BE REMOVED FROM CODE -set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" -set g_balance_health_start 100 -set g_balance_armor_start 0 -set g_start_ammo_shells 15 -set g_start_ammo_nails 0 -set g_start_ammo_rockets 0 -set g_start_ammo_cells 0 -set g_start_ammo_fuel 0 -set g_warmup_start_health 100 "starting values when being in warmup-stage" -set g_warmup_start_armor 100 "starting values when being in warmup-stage" -set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage" -set g_warmup_start_ammo_nails 160 "starting values when being in warmup-stage" -set g_warmup_start_ammo_rockets 80 "starting values when being in warmup-stage" -set g_warmup_start_ammo_cells 90 "starting values when being in warmup-stage" -set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage" -set g_lms_start_health 200 -set g_lms_start_armor 200 -set g_lms_start_ammo_shells 60 -set g_lms_start_ammo_nails 320 -set g_lms_start_ammo_rockets 160 -set g_lms_start_ammo_cells 180 -set g_lms_start_ammo_fuel 0 -set g_balance_nix_roundtime 25 -set g_balance_nix_incrtime 1.6 -set g_balance_nix_ammo_shells 60 -set g_balance_nix_ammo_nails 320 -set g_balance_nix_ammo_rockets 160 -set g_balance_nix_ammo_cells 180 -set g_balance_nix_ammo_fuel 0 -set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume -set g_balance_nix_ammoincr_nails 6 -set g_balance_nix_ammoincr_rockets 2 -set g_balance_nix_ammoincr_cells 2 -set g_balance_nix_ammoincr_fuel 2 -// }}} - -// {{{ pickup items -set g_pickup_ammo_anyway 1 -set g_pickup_weapons_anyway 1 -set g_pickup_shells 15 -set g_pickup_shells_weapon 15 -set g_pickup_shells_max 60 -set g_pickup_nails 80 -set g_pickup_nails_weapon 80 -set g_pickup_nails_max 320 -set g_pickup_rockets 40 -set g_pickup_rockets_weapon 40 -set g_pickup_rockets_max 160 -set g_pickup_cells 30 -set g_pickup_cells_weapon 30 -set g_pickup_cells_max 180 -set g_pickup_fuel 50 -set g_pickup_fuel_weapon 50 -set g_pickup_fuel_jetpack 100 -set g_pickup_fuel_max 100 -set g_pickup_armorsmall 5 -set g_pickup_armorsmall_max 200 -set g_pickup_armorsmall_anyway 1 -set g_pickup_armormedium 25 -set g_pickup_armormedium_max 200 -set g_pickup_armormedium_anyway 1 -set g_pickup_armorbig 50 -set g_pickup_armorbig_max 200 -set g_pickup_armorbig_anyway 1 -set g_pickup_armorlarge 100 -set g_pickup_armorlarge_max 200 -set g_pickup_armorlarge_anyway 1 -set g_pickup_healthsmall 5 -set g_pickup_healthsmall_max 200 -set g_pickup_healthsmall_anyway 1 -set g_pickup_healthmedium 25 -set g_pickup_healthmedium_max 200 -set g_pickup_healthmedium_anyway 1 -set g_pickup_healthlarge 50 -set g_pickup_healthlarge_max 200 -set g_pickup_healthlarge_anyway 1 -set g_pickup_healthmega 100 -set g_pickup_healthmega_max 200 -set g_pickup_healthmega_anyway 1 -set g_pickup_respawntime_short 15 -set g_pickup_respawntime_medium 20 -set g_pickup_respawntime_long 30 -set g_pickup_respawntime_powerup 120 -set g_pickup_respawntime_weapon 10 -set g_pickup_respawntime_superweapon 120 -set g_pickup_respawntime_ammo 10 -set g_pickup_respawntimejitter_short 0 -set g_pickup_respawntimejitter_medium 0 -set g_pickup_respawntimejitter_long 0 -set g_pickup_respawntimejitter_powerup 30 -set g_pickup_respawntimejitter_weapon 0 -set g_pickup_respawntimejitter_superweapon 10 -set g_pickup_respawntimejitter_ammo 0 -// }}} - -// {{{ regen/rot -set g_balance_health_regen 0.08 -set g_balance_health_regenlinear 0.5 -set g_balance_pause_health_regen 5 -set g_balance_pause_health_regen_spawn 0 -set g_balance_health_rot 0.04 -set g_balance_health_rotlinear 0.75 -set g_balance_pause_health_rot 1 -set g_balance_pause_health_rot_spawn 5 -set g_balance_health_regenstable 100 -set g_balance_health_rotstable 100 -set g_balance_health_limit 999 -set g_balance_armor_regen 0 -set g_balance_armor_regenlinear 0 -set g_balance_armor_rot 0.04 -set g_balance_armor_rotlinear 0.75 -set g_balance_pause_armor_rot 1 -set g_balance_pause_armor_rot_spawn 5 -set g_balance_armor_regenstable 100 -set g_balance_armor_rotstable 100 -set g_balance_armor_limit 999 -set g_balance_armor_blockpercent 0.6 -set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)" -set g_balance_fuel_regenlinear 0 -set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter -set g_balance_fuel_rot 0.05 -set g_balance_fuel_rotlinear 0 -set g_balance_pause_fuel_rot 5 -set g_balance_pause_fuel_rot_spawn 10 -set g_balance_fuel_regenstable 50 -set g_balance_fuel_rotstable 100 -set g_balance_fuel_limit 999 -// }}} - -// {{{ misc -set g_balance_selfdamagepercent 0.65 -set g_weaponspeedfactor 1 "weapon projectile speed multiplier" -set g_weaponratefactor 1 "weapon fire rate multiplier" -set g_weapondamagefactor 1 "weapon damage multiplier" -set g_weaponforcefactor 1 "weapon force multiplier" -set g_weaponspreadfactor 1 "weapon spread multiplier" -set g_balance_firetransfer_time 0.9 -set g_balance_firetransfer_damage 0.8 -set g_throughfloor_damage 0.75 -set g_throughfloor_force 0.75 -set g_projectiles_damage 2 -// possible values: -// -2: absolutely no damage to projectiles (no exceptions) -// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines) -// 0: only damage from contents (lava/slime) or exceptions -// 1: only self damage or damage from contents or exceptions -// 2: allow all damage to projectiles normally -set g_projectiles_keep_owner 0 -set g_projectiles_newton_style 2 -// possible values: -// 0: absolute velocity projectiles (like Quake) -// 1: relative velocity projectiles, "Newtonian" (like Tribes 2) -// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard) -set g_projectiles_newton_style_2_minfactor 0.8 -set g_projectiles_newton_style_2_maxfactor 1.5 -set g_projectiles_spread_style 7 -// possible values: -// 0: forward + solid sphere (like Quake) - varies velocity -// 1: forward + flattened solid sphere -// 2: forward + solid circle -// 3: forward + normal distribution 3D - varies velocity -// 4: forward + normal distribution on a plane -// 5: forward + circle with 1-r falloff -// 6: forward + circle with 1-r^2 falloff -// 7: forward + circle with (1-r)(2-r) falloff -set g_balance_falldamage_deadminspeed 250 -set g_balance_falldamage_minspeed 900 -set g_balance_falldamage_factor 0.20 -set g_balance_falldamage_maxdamage 40 -set g_balance_damagepush_speedfactor 2.5 -set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage -set g_balance_contents_drowndelay 10 // time under water before a player begins drowning -set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning -set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava -set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime -set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime -set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap" -// }}} - -// {{{ powerups -set g_balance_powerup_invincible_takedamage 0.25 // only 1/4th damage is taken -set g_balance_powerup_invincible_time 30 -set g_balance_powerup_strength_damage 3 -set g_balance_powerup_strength_force 3 -set g_balance_powerup_strength_time 30 -set g_balance_powerup_strength_selfdamage 1.5 -set g_balance_powerup_strength_selfforce 1.5 -set g_balance_superweapons_time 30 -// }}} - -// {{{ jetpack/hook -set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack" -set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction" -set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)" -set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction" -set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" -set g_jetpack_fuel 8 "fuel per second for jetpack" -set g_jetpack_attenuation 2 "jetpack sound attenuation" - -set g_grappling_hook_tarzan 2 // 2: can also pull players -set g_balance_grapplehook_speed_fly 1800 -set g_balance_grapplehook_speed_pull 2000 -set g_balance_grapplehook_force_rubber 2000 -set g_balance_grapplehook_force_rubber_overstretch 1000 -set g_balance_grapplehook_length_min 50 -set g_balance_grapplehook_stretch 50 -set g_balance_grapplehook_airfriction 0.2 -set g_balance_grapplehook_health 50 -set g_balance_grapplehook_damagedbycontents 1 -set g_balance_grapplehook_refire 0.2 -// }}} - -// {{{ weapon properties -// {{{ laser -set g_balance_laser_primary_damage 25 -set g_balance_laser_primary_edgedamage 12.5 -set g_balance_laser_primary_force 300 -set g_balance_laser_primary_radius 70 -set g_balance_laser_primary_speed 6000 -set g_balance_laser_primary_spread 0 -set g_balance_laser_primary_refire 0.7 -set g_balance_laser_primary_animtime 0.2 -set g_balance_laser_primary_lifetime 5 -set g_balance_laser_primary_shotangle 0 -set g_balance_laser_primary_delay 0 -set g_balance_laser_primary_gauntlet 0 -set g_balance_laser_primary_force_zscale 1.2 -set g_balance_laser_primary_force_velocitybias 0 -set g_balance_laser_primary_force_other_scale 1 -set g_balance_laser_secondary 0 // when 1, a secondary laser mode exists -set g_balance_laser_secondary_damage 25 -set g_balance_laser_secondary_edgedamage 12.5 -set g_balance_laser_secondary_force 400 -set g_balance_laser_secondary_radius 70 -set g_balance_laser_secondary_speed 12000 -set g_balance_laser_secondary_spread 0 -set g_balance_laser_secondary_refire 0.7 -set g_balance_laser_secondary_animtime 0.2 -set g_balance_laser_secondary_lifetime 5 -set g_balance_laser_secondary_shotangle -90 -set g_balance_laser_secondary_delay 0 -set g_balance_laser_secondary_gauntlet 0 -set g_balance_laser_secondary_force_zscale 1.25 -set g_balance_laser_secondary_force_velocitybias 0 -set g_balance_laser_secondary_force_other_scale 1 -set g_balance_laser_switchdelay_drop 0.15 -set g_balance_laser_switchdelay_raise 0.15 -set g_balance_laser_reload_ammo 0 //default: 6 -set g_balance_laser_reload_time 2 -// }}} -// {{{ shotgun -set g_balance_shotgun_primary_bullets 14 -set g_balance_shotgun_primary_damage 4 -set g_balance_shotgun_primary_force 15 -set g_balance_shotgun_primary_spread 0.12 -set g_balance_shotgun_primary_refire 0.75 -set g_balance_shotgun_primary_animtime 0.2 -set g_balance_shotgun_primary_ammo 1 -set g_balance_shotgun_primary_solidpenetration 3.8 -set g_balance_shotgun_secondary 1 -set g_balance_shotgun_secondary_melee_delay 0.25 // 0.35 was too slow -set g_balance_shotgun_secondary_melee_range 120 -set g_balance_shotgun_secondary_melee_swing_side 120 -set g_balance_shotgun_secondary_melee_swing_up 30 -set g_balance_shotgun_secondary_melee_time 0.15 -set g_balance_shotgun_secondary_melee_traces 10 -set g_balance_shotgun_secondary_melee_no_doubleslap 1 -set g_balance_shotgun_secondary_melee_nonplayerdamage 40 -set g_balance_shotgun_secondary_melee_multihit 1 -set g_balance_shotgun_secondary_damage 80 -set g_balance_shotgun_secondary_force 200 -set g_balance_shotgun_secondary_refire 1.25 -set g_balance_shotgun_secondary_animtime 1 -set g_balance_shotgun_switchdelay_drop 0.2 -set g_balance_shotgun_switchdelay_raise 0.2 -set g_balance_shotgun_reload_ammo 0 //default: 5 -set g_balance_shotgun_reload_time 2 -// }}} -// {{{ uzi -set g_balance_uzi_mode 1 // Activates varible spread for sustained & burst mode secondary -set g_balance_uzi_spread_min 0.02 -set g_balance_uzi_spread_max 0.05 -set g_balance_uzi_spread_add 0.012 - -set g_balance_uzi_burst 3 // # of bullets in a burst (if set to 2 or more) -set g_balance_uzi_burst_animtime 0.3 -set g_balance_uzi_burst_refire 0.06 // refire between burst bullets -set g_balance_uzi_burst_refire2 0.45 // refire after burst -set g_balance_uzi_burst_spread 0.02 -set g_balance_uzi_burst_damage 25 -set g_balance_uzi_burst_force 20 -set g_balance_uzi_burst_ammo 3 - -set g_balance_uzi_first 1 -set g_balance_uzi_first_damage 14 -set g_balance_uzi_first_force 5 -set g_balance_uzi_first_spread 0.03 -set g_balance_uzi_first_refire 0.125 -set g_balance_uzi_first_ammo 1 - -set g_balance_uzi_sustained_damage 10 // 100 dps -set g_balance_uzi_sustained_force 5 -set g_balance_uzi_sustained_spread 0.03 -set g_balance_uzi_sustained_refire 0.1 -set g_balance_uzi_sustained_ammo 1 - -set g_balance_uzi_solidpenetration 13.1 - -set g_balance_uzi_switchdelay_drop 0.2 -set g_balance_uzi_switchdelay_raise 0.2 - -set g_balance_uzi_reload_ammo 60 //default: 30 -set g_balance_uzi_reload_time 2 -// }}} -// {{{ mortar -set g_balance_grenadelauncher_primary_type 0 -set g_balance_grenadelauncher_primary_damage 50 -set g_balance_grenadelauncher_primary_edgedamage 25 -set g_balance_grenadelauncher_primary_force 250 -set g_balance_grenadelauncher_primary_radius 120 -set g_balance_grenadelauncher_primary_speed 1900 -set g_balance_grenadelauncher_primary_speed_up 225 -set g_balance_grenadelauncher_primary_speed_z 0 -set g_balance_grenadelauncher_primary_spread 0 -set g_balance_grenadelauncher_primary_lifetime 5 -set g_balance_grenadelauncher_primary_lifetime2 1 -set g_balance_grenadelauncher_primary_refire 0.8 -set g_balance_grenadelauncher_primary_animtime 0.3 -set g_balance_grenadelauncher_primary_ammo 2 -set g_balance_grenadelauncher_primary_health 15 -set g_balance_grenadelauncher_primary_damageforcescale 0 -set g_balance_grenadelauncher_primary_remote_minbouncecnt 0 - -set g_balance_grenadelauncher_secondary_type 1 -set g_balance_grenadelauncher_secondary_damage 60 -set g_balance_grenadelauncher_secondary_edgedamage 30 -set g_balance_grenadelauncher_secondary_force 250 -set g_balance_grenadelauncher_secondary_radius 120 -set g_balance_grenadelauncher_secondary_speed 1400 -set g_balance_grenadelauncher_secondary_speed_up 150 -set g_balance_grenadelauncher_secondary_speed_z 0 -set g_balance_grenadelauncher_secondary_spread 0 -set g_balance_grenadelauncher_secondary_lifetime 5 -set g_balance_grenadelauncher_secondary_lifetime_bounce 0.5 -set g_balance_grenadelauncher_secondary_lifetime_stick 0 -set g_balance_grenadelauncher_secondary_refire 0.7 -set g_balance_grenadelauncher_secondary_animtime 0.3 -set g_balance_grenadelauncher_secondary_ammo 2 -set g_balance_grenadelauncher_secondary_health 30 -set g_balance_grenadelauncher_secondary_damageforcescale 4 -set g_balance_grenadelauncher_secondary_remote_detonateprimary 0 - -set g_balance_grenadelauncher_bouncefactor 0.5 -set g_balance_grenadelauncher_bouncestop 0.075 - -set g_balance_grenadelauncher_switchdelay_drop 0.2 -set g_balance_grenadelauncher_switchdelay_raise 0.2 - -set g_balance_grenadelauncher_reload_ammo 0 //default: 12 -set g_balance_grenadelauncher_reload_time 2 -// }}} -// {{{ electro -set g_balance_electro_lightning 0 -set g_balance_electro_primary_damage 40 -set g_balance_electro_primary_edgedamage 20 -set g_balance_electro_primary_force 200 -set g_balance_electro_primary_force_up 0 -set g_balance_electro_primary_radius 100 -set g_balance_electro_primary_comboradius 300 -set g_balance_electro_primary_speed 2500 -set g_balance_electro_primary_spread 0 -set g_balance_electro_primary_lifetime 5 -set g_balance_electro_primary_refire 0.6 -set g_balance_electro_primary_animtime 0.3 -set g_balance_electro_primary_ammo 4 -set g_balance_electro_primary_range 0 -set g_balance_electro_primary_falloff_mindist 255 // 0.3 * radius -set g_balance_electro_primary_falloff_maxdist 850 -set g_balance_electro_primary_falloff_halflifedist 425 -set g_balance_electro_secondary_damage 40 -set g_balance_electro_secondary_edgedamage 20 -set g_balance_electro_secondary_force 50 -set g_balance_electro_secondary_radius 150 -set g_balance_electro_secondary_speed 1000 -set g_balance_electro_secondary_speed_up 200 -set g_balance_electro_secondary_speed_z 0 -set g_balance_electro_secondary_spread 0.04 -set g_balance_electro_secondary_lifetime 4 -set g_balance_electro_secondary_refire 0.2 -set g_balance_electro_secondary_refire2 1.6 -set g_balance_electro_secondary_animtime 0.2 -set g_balance_electro_secondary_ammo 2 -set g_balance_electro_secondary_health 5 -set g_balance_electro_secondary_damageforcescale 4 -set g_balance_electro_secondary_damagedbycontents 1 -set g_balance_electro_secondary_count 3 -set g_balance_electro_secondary_bouncefactor 0.3 -set g_balance_electro_secondary_bouncestop 0.05 -set g_balance_electro_combo_damage 50 -set g_balance_electro_combo_edgedamage 25 -set g_balance_electro_combo_force 120 -set g_balance_electro_combo_radius 150 -set g_balance_electro_combo_comboradius 300 -set g_balance_electro_combo_speed 2000 -set g_balance_electro_combo_safeammocheck 1 -set g_balance_electro_switchdelay_drop 0.2 -set g_balance_electro_switchdelay_raise 0.2 -set g_balance_electro_reload_ammo 0 //default: 20 -set g_balance_electro_reload_time 2 -// }}} -// {{{ crylink -set g_balance_crylink_primary_damage 12 -set g_balance_crylink_primary_edgedamage 6 -set g_balance_crylink_primary_force -50 -set g_balance_crylink_primary_radius 80 -set g_balance_crylink_primary_speed 2000 -set g_balance_crylink_primary_spread 0.08 -set g_balance_crylink_primary_shots 6 -set g_balance_crylink_primary_bounces 1 -set g_balance_crylink_primary_refire 0.7 -set g_balance_crylink_primary_animtime 0.3 -set g_balance_crylink_primary_ammo 3 -set g_balance_crylink_primary_bouncedamagefactor 0.5 -set g_balance_crylink_primary_joindelay 0.1 -set g_balance_crylink_primary_joinspread 0.2 -set g_balance_crylink_primary_joinexplode 1 -set g_balance_crylink_primary_joinexplode_damage 0 -set g_balance_crylink_primary_joinexplode_edgedamage 0 -set g_balance_crylink_primary_joinexplode_radius 0 -set g_balance_crylink_primary_joinexplode_force 0 -set g_balance_crylink_primary_linkexplode 1 - -set g_balance_crylink_primary_middle_lifetime 5 // range: 35000 full, fades to 70000 -set g_balance_crylink_primary_middle_fadetime 5 -set g_balance_crylink_primary_other_lifetime 5 -set g_balance_crylink_primary_other_fadetime 5 - -set g_balance_crylink_secondary 1 -set g_balance_crylink_secondary_damage 10 -set g_balance_crylink_secondary_edgedamage 5 -set g_balance_crylink_secondary_force -250 -set g_balance_crylink_secondary_radius 100 -set g_balance_crylink_secondary_speed 3000 -set g_balance_crylink_secondary_spread 0.01 -set g_balance_crylink_secondary_spreadtype 1 -set g_balance_crylink_secondary_shots 5 -set g_balance_crylink_secondary_bounces 0 -set g_balance_crylink_secondary_refire 0.7 -set g_balance_crylink_secondary_animtime 0.2 -set g_balance_crylink_secondary_ammo 2 -set g_balance_crylink_secondary_bouncedamagefactor 0.5 -set g_balance_crylink_secondary_joindelay 0 -set g_balance_crylink_secondary_joinspread 0 -set g_balance_crylink_secondary_joinexplode 0 -set g_balance_crylink_secondary_joinexplode_damage 0 -set g_balance_crylink_secondary_joinexplode_edgedamage 0 -set g_balance_crylink_secondary_joinexplode_radius 0 -set g_balance_crylink_secondary_joinexplode_force 0 -set g_balance_crylink_secondary_linkexplode 1 - -set g_balance_crylink_secondary_middle_lifetime 5 // range: 35000 full, fades to 70000 -set g_balance_crylink_secondary_middle_fadetime 5 -set g_balance_crylink_secondary_line_lifetime 5 -set g_balance_crylink_secondary_line_fadetime 5 - -set g_balance_crylink_switchdelay_drop 0.2 -set g_balance_crylink_switchdelay_raise 0.2 - -set g_balance_crylink_reload_ammo 0 //default: 10 -set g_balance_crylink_reload_time 2 -// }}} -// {{{ nex -set g_balance_nex_primary_damage 80 -set g_balance_nex_primary_force 400 -set g_balance_nex_primary_refire 1.5 -set g_balance_nex_primary_animtime 0.6 -set g_balance_nex_primary_ammo 6 -set g_balance_nex_primary_damagefalloff_mindist 0 // 1000 For tZork ;3 -set g_balance_nex_primary_damagefalloff_maxdist 0 // 3000 -set g_balance_nex_primary_damagefalloff_halflife 0 // 1500 -set g_balance_nex_primary_damagefalloff_forcehalflife 0 // 1500 - -set g_balance_nex_secondary 0 -set g_balance_nex_secondary_charge 0 -set g_balance_nex_secondary_charge_rate 0.1 -set g_balance_nex_secondary_chargepool 0 -set g_balance_nex_secondary_chargepool_regen 0.15 -set g_balance_nex_secondary_chargepool_pause_regen 1 -set g_balance_nex_secondary_chargepool_pause_health_regen 1 -set g_balance_nex_secondary_damage 0 -set g_balance_nex_secondary_force 0 -set g_balance_nex_secondary_refire 0 -set g_balance_nex_secondary_animtime 0 -set g_balance_nex_secondary_ammo 2 -set g_balance_nex_secondary_damagefalloff_mindist 0 -set g_balance_nex_secondary_damagefalloff_maxdist 0 -set g_balance_nex_secondary_damagefalloff_halflife 0 -set g_balance_nex_secondary_damagefalloff_forcehalflife 0 - -set g_balance_nex_charge 1 -set g_balance_nex_charge_mindmg 40 -set g_balance_nex_charge_start 0.5 -set g_balance_nex_charge_rate 0.4 -set g_balance_nex_charge_animlimit 0.5 -set g_balance_nex_charge_limit 1 -set g_balance_nex_charge_rot_rate 0 -set g_balance_nex_charge_rot_pause 0 // Dont rot down until this long after release of charge button -set g_balance_nex_charge_shot_multiplier 0 -set g_balance_nex_charge_velocity_rate 0 -set g_balance_nex_charge_minspeed 400 -set g_balance_nex_charge_maxspeed 800 - -set g_balance_nex_switchdelay_drop 0.3 -set g_balance_nex_switchdelay_raise 0.25 - -set g_balance_nex_reload_ammo 0 //default: 25 -set g_balance_nex_reload_time 2 -// }}} -// {{{ minstanex -set g_balance_minstanex_refire 1 -set g_balance_minstanex_animtime 0.3 -set g_balance_minstanex_ammo 10 -set g_balance_minstanex_laser_ammo 0 -set g_balance_minstanex_laser_animtime 0.3 -set g_balance_minstanex_laser_refire 0.7 -set g_balance_minstanex_switchdelay_drop 0.2 -set g_balance_minstanex_switchdelay_raise 0.2 -set g_balance_minstanex_reload_ammo 0 //default: 50 -set g_balance_minstanex_reload_time 2 -// }}} -// {{{ hagar -set g_balance_hagar_primary_damage 25 -set g_balance_hagar_primary_edgedamage 12.5 -set g_balance_hagar_primary_force 100 -set g_balance_hagar_primary_health 15 -set g_balance_hagar_primary_damageforcescale 0 -set g_balance_hagar_primary_radius 65 -set g_balance_hagar_primary_spread 0.03 -set g_balance_hagar_primary_speed 2500 -set g_balance_hagar_primary_lifetime 5 -set g_balance_hagar_primary_refire 0.16667 // 6 rockets per second -set g_balance_hagar_primary_ammo 1 -set g_balance_hagar_secondary 1 -set g_balance_hagar_secondary_load 1 -set g_balance_hagar_secondary_load_speed 0.5 -set g_balance_hagar_secondary_load_spread 0.075 -set g_balance_hagar_secondary_load_spread_bias 0.5 -set g_balance_hagar_secondary_load_max 4 -set g_balance_hagar_secondary_load_hold 4 -set g_balance_hagar_secondary_load_releasedeath 0 -set g_balance_hagar_secondary_load_abort 0 -set g_balance_hagar_secondary_load_linkexplode 0 -set g_balance_hagar_secondary_load_animtime 0.2 -set g_balance_hagar_secondary_damage 40 -set g_balance_hagar_secondary_edgedamage 20 -set g_balance_hagar_secondary_force 75 -set g_balance_hagar_secondary_health 15 -set g_balance_hagar_secondary_damageforcescale 0 -set g_balance_hagar_secondary_radius 80 -set g_balance_hagar_secondary_spread 0.05 -set g_balance_hagar_secondary_speed 2500 -set g_balance_hagar_secondary_lifetime_min 10 -set g_balance_hagar_secondary_lifetime_rand 0 -set g_balance_hagar_secondary_refire 0.5 -set g_balance_hagar_secondary_ammo 1 -set g_balance_hagar_switchdelay_drop 0.2 -set g_balance_hagar_switchdelay_raise 0.2 -set g_balance_hagar_reload_ammo 0 //default: 25 -set g_balance_hagar_reload_time 2 -// }}} -// {{{ rocketlauncher -set g_balance_rocketlauncher_damage 70 -set g_balance_rocketlauncher_edgedamage 35 -set g_balance_rocketlauncher_force 450 -set g_balance_rocketlauncher_radius 110 -set g_balance_rocketlauncher_speed 1300 -set g_balance_rocketlauncher_speedaccel 1300 -set g_balance_rocketlauncher_speedstart 1000 -set g_balance_rocketlauncher_lifetime 10 -set g_balance_rocketlauncher_refire 1.2 -set g_balance_rocketlauncher_animtime 0.4 -set g_balance_rocketlauncher_ammo 4 -set g_balance_rocketlauncher_health 30 // 30 // 5 hitpoints above maximum laser value -- this way lasers can't blow it up, but grenadelauncher still can most the time. -set g_balance_rocketlauncher_damageforcescale 1 // low damage force scale so that it can still be affected by other hits, but not so much that it does a 90 degree turn -set g_balance_rocketlauncher_detonatedelay 0.02 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time -set g_balance_rocketlauncher_guiderate 70 // max degrees per second -set g_balance_rocketlauncher_guideratedelay 0.01 // immediate -set g_balance_rocketlauncher_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic) -set g_balance_rocketlauncher_guidedelay 0.2 // delay before guiding kicks in -set g_balance_rocketlauncher_guidestop 0 // stop guiding when firing again -set g_balance_rocketlauncher_remote_damage 70 -set g_balance_rocketlauncher_remote_edgedamage 35 -set g_balance_rocketlauncher_remote_radius 110 -set g_balance_rocketlauncher_remote_force 400 -set g_balance_rocketlauncher_switchdelay_drop 0.3 -set g_balance_rocketlauncher_switchdelay_raise 0.2 -set g_balance_rocketlauncher_reload_ammo 0 //default: 25 -set g_balance_rocketlauncher_reload_time 2 -// }}} -// {{{ porto -set g_balance_porto_primary_refire 1.5 -set g_balance_porto_primary_animtime 0.3 -set g_balance_porto_primary_speed 1000 -set g_balance_porto_primary_lifetime 5 -set g_balance_porto_secondary 1 -set g_balance_porto_secondary_refire 1.5 -set g_balance_porto_secondary_animtime 0.3 -set g_balance_porto_secondary_speed 1000 -set g_balance_porto_secondary_lifetime 5 -set g_balance_porto_switchdelay_drop 0.2 -set g_balance_porto_switchdelay_raise 0.2 -set g_balance_portal_health 200 // these get recharged whenever the portal is used -set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used -// }}} -// {{{ hook -set g_balance_hook_primary_fuel 5 // hook monkeys set 0 -set g_balance_hook_primary_refire 0.2 // hook monkeys set 0 -set g_balance_hook_primary_animtime 0.3 // good shoot anim -set g_balance_hook_primary_hooked_time_max 0 // infinite -set g_balance_hook_primary_hooked_time_free 2 // 2s being hooked are free -set g_balance_hook_primary_hooked_fuel 5 // fuel per second hooked -set g_balance_hook_secondary_damage 25 // not much -set g_balance_hook_secondary_edgedamage 5 // not much -set g_balance_hook_secondary_radius 500 // LOTS -set g_balance_hook_secondary_force -2000 // LOTS -set g_balance_hook_secondary_ammo 30 // a whole pack -set g_balance_hook_secondary_lifetime 5 // infinite -set g_balance_hook_secondary_speed 0 // not much throwing -set g_balance_hook_secondary_gravity 5 // fast falling -set g_balance_hook_secondary_refire 3 // don't drop too many bombs... -set g_balance_hook_secondary_animtime 0.3 // good shoot anim -set g_balance_hook_secondary_power 3 // effect behaves like a square function -set g_balance_hook_secondary_duration 1.5 // effect runs for three seconds -set g_balance_hook_secondary_health 15 -set g_balance_hook_secondary_damageforcescale 0 -set g_balance_hook_switchdelay_drop 0.2 -set g_balance_hook_switchdelay_raise 0.2 -// }}} -// {{{ tuba -set g_balance_tuba_refire 0.05 -set g_balance_tuba_animtime 0.05 -set g_balance_tuba_attenuation 0.5 -set g_balance_tuba_volume 1 -set g_balance_tuba_fadetime 0.25 -set g_balance_tuba_damage 5 -set g_balance_tuba_edgedamage 0 -set g_balance_tuba_radius 200 -set g_balance_tuba_force 40 -set g_balance_tuba_pitchstep 6 -set g_balance_tuba_switchdelay_drop 0.2 -set g_balance_tuba_switchdelay_raise 0.2 -// }}} -// {{{ fireball // this is a superweapon -- lets make it behave as one. -set g_balance_fireball_primary_animtime 0.2 -set g_balance_fireball_primary_bfgdamage 100 -set g_balance_fireball_primary_bfgforce 0 -set g_balance_fireball_primary_bfgradius 1000 -set g_balance_fireball_primary_damage 200 -set g_balance_fireball_primary_damageforcescale 0 -set g_balance_fireball_primary_edgedamage 50 -set g_balance_fireball_primary_force 600 -set g_balance_fireball_primary_health 0 -set g_balance_fireball_primary_laserburntime 0.5 -set g_balance_fireball_primary_laserdamage 80 -set g_balance_fireball_primary_laseredgedamage 20 -set g_balance_fireball_primary_laserradius 256 -set g_balance_fireball_primary_lifetime 15 -set g_balance_fireball_primary_radius 200 -set g_balance_fireball_primary_refire 2 -set g_balance_fireball_primary_refire2 0 -set g_balance_fireball_primary_speed 1200 -set g_balance_fireball_primary_spread 0 -set g_balance_fireball_secondary_animtime 0.3 -set g_balance_fireball_secondary_damage 40 -set g_balance_fireball_secondary_damageforcescale 4 -set g_balance_fireball_secondary_damagetime 5 -set g_balance_fireball_secondary_force 100 -set g_balance_fireball_secondary_laserburntime 0.5 -set g_balance_fireball_secondary_laserdamage 50 -set g_balance_fireball_secondary_laseredgedamage 20 -set g_balance_fireball_secondary_laserradius 110 -set g_balance_fireball_secondary_lifetime 7 -set g_balance_fireball_secondary_refire 1.5 -set g_balance_fireball_secondary_speed 900 -set g_balance_fireball_secondary_speed_up 100 -set g_balance_fireball_secondary_speed_z 0 -set g_balance_fireball_secondary_spread 0 -set g_balance_fireball_switchdelay_drop 0.2 -set g_balance_fireball_switchdelay_raise 0.2 -// }}} diff --git a/check-cvars.sh b/check-cvars.sh index 78985b8194..031613a7bf 100755 --- a/check-cvars.sh +++ b/check-cvars.sh @@ -23,7 +23,7 @@ check_files() done } -check_files "balanceXonotic.cfg" "balance*.cfg" "/^seta? g_/" +check_files "balance-xonotic.cfg" "balance-*.cfg" "/^seta? g_/" check_files "_hud_descriptions.cfg" "hud_*.cfg" "/^seta? hud_/" if $errord; then diff --git a/crosshairs.cfg b/crosshairs.cfg index c6a9e837b0..c94d3a5fb7 100644 --- a/crosshairs.cfg +++ b/crosshairs.cfg @@ -41,107 +41,29 @@ seta crosshair_color_special 1 "special color handling for crosshair... 1 = per- seta crosshair_color_special_rainbow_delay 0.1 seta crosshair_color_special_rainbow_brightness 20 "color brightness of the random crosshair colors" - -// =============================== -// Per weapon crosshair settings -// =============================== -// main settings +// per-weapon crosshairs seta crosshair_per_weapon 1 "when 1, each gun will display a different crosshair" -// per weapon settings -seta crosshair_laser "" "crosshair to display when wielding the laser" -seta crosshair_laser_color "1 0.35 0.2" "crosshair color to display when wielding the laser" -seta crosshair_laser_alpha 0.75 "crosshair alpha value to display when wielding the laser" -seta crosshair_laser_size 0.4 "crosshair size when wielding the laser" -seta crosshair_shotgun "" "crosshair to display when wielding the shotgun" -seta crosshair_shotgun_color "0.7 0.7 0.7" "crosshair color to display when wielding the shotgun" -seta crosshair_shotgun_alpha 1.1 "crosshair alpha value to display when wielding the shotgun" -seta crosshair_shotgun_size 0.65 "crosshair size when wielding the shotgun" -seta crosshair_uzi "" "crosshair to display when wielding the machinegun" -seta crosshair_uzi_color "0.4 0.9 0.35" "crosshair color to display when wielding the machinegun" -seta crosshair_uzi_alpha 0.9 "crosshair alpha value to display when wielding the machinegun" -seta crosshair_uzi_size 0.6 "crosshair size when wielding the machinegun" -seta crosshair_grenadelauncher "" "crosshair to display when wielding the mortar" -seta crosshair_grenadelauncher_color "1 0.15 0" "crosshair color to display when wielding the mortar" -seta crosshair_grenadelauncher_alpha 1.15 "crosshair alpha value to display when wielding the mortar" -seta crosshair_grenadelauncher_size 0.7 "crosshair size when wielding the mortar" -seta crosshair_minelayer "" "crosshair to display when wielding the mortar" -seta crosshair_minelayer_color "0.75 0.75 0" "crosshair color to display when wielding the mortar" -seta crosshair_minelayer_alpha 1.15 "crosshair alpha value to display when wielding the mortar" -seta crosshair_minelayer_size 0.9 "crosshair size when wielding the mortar" -seta crosshair_electro "" "crosshair to display when wielding the electro" -seta crosshair_electro_color "0.35 0.5 1" "crosshair color to display when wielding the electro" -seta crosshair_electro_alpha 1 "crosshair alpha value to display when wielding the electro" -seta crosshair_electro_size 0.5 "crosshair size when wielding the electro" -seta crosshair_crylink "" "crosshair to display when wielding the crylink" -seta crosshair_crylink_color "0.85 0.25 1" "crosshair color to display when wielding the crylink" -seta crosshair_crylink_alpha 0.85 "crosshair alpha value to display when wielding the crylink" -seta crosshair_crylink_size 0.4 "crosshair size when wielding the crylink" -seta crosshair_nex "" "crosshair to display when wielding the nex gun" -seta crosshair_nex_color "0 0.9 1" "crosshair color to display when wielding the nex gun" -seta crosshair_nex_alpha 0.85 "crosshair alpha value to display when wielding the nex gun" -seta crosshair_nex_size 0.65 "crosshair size when wielding the nex gun" -seta crosshair_hagar "" "crosshair to display when wielding the hagar" -seta crosshair_hagar_color "0.85 0.5 0.35" "crosshair color to display when wielding the hagar" -seta crosshair_hagar_alpha 1 "crosshair alpha value to display when wielding the hagar" -seta crosshair_hagar_size 0.8 "crosshair size when wielding the hagar" -seta crosshair_rocketlauncher "" "crosshair to display when wielding the rocketlauncher" -seta crosshair_rocketlauncher_color "1 0.75 0.2" "crosshair color to display when wielding the rocketlauncher" -seta crosshair_rocketlauncher_alpha 1 "crosshair alpha value to display when wielding the rocketlauncher" -seta crosshair_rocketlauncher_size 0.5875 "crosshair size when wielding the rocketlauncher" -seta crosshair_porto "" "crosshair to display when wielding the porto" -seta crosshair_porto_color "0.5 1 0.5" "crosshair color to display when wielding the porto" -seta crosshair_porto_alpha 0.85 "crosshair alpha value to display when wielding the porto" -seta crosshair_porto_size 0.6 "crosshair size when wielding the porto" -seta crosshair_minstanex "" "crosshair to display when wielding the minstanex gun" -seta crosshair_minstanex_color "0.65 0.65 1" "crosshair color to display when wielding the minstanex gun" -seta crosshair_minstanex_alpha 1 "crosshair alpha value to display when wielding the minstanex gun" -seta crosshair_minstanex_size 0.4 "crosshair size when wielding the minstanex gun" -seta crosshair_hook "" "crosshair to display when wielding the hook" -seta crosshair_hook_color "0.65 1 0.85" "crosshair color to display when wielding the hook" -seta crosshair_hook_alpha 0.85 "crosshair alpha value to display when wielding the hook" -seta crosshair_hook_size 0.5 "crosshair size when wielding the hook" -seta crosshair_hlac "" "crosshair to display when wielding the H.L.A.C" -seta crosshair_hlac_color "1 0.65 0.2" "crosshair color to display when wielding the H.L.A.C." -seta crosshair_hlac_alpha 1 "crosshair alpha value to display when wielding the H.L.A.C." -seta crosshair_hlac_size 0.6 "crosshair size when wielding the H.L.A.C." -seta crosshair_seeker "" "crosshair to display when wielding the TAG Seeker" -seta crosshair_seeker_color "1 0.35 0.35" "crosshair color to display when wielding the TAG seeker" -seta crosshair_seeker_alpha 0.9 "crosshair alpha value to display when wielding the TAG seeker" -seta crosshair_seeker_size 0.8 "crosshair size when wielding the TAG seeker" -seta crosshair_rifle "" "crosshair to display when wielding the rifle" -seta crosshair_rifle_color "0.85 0.5 0.25" "crosshair color to display when wielding the rifle" -seta crosshair_rifle_alpha 1 "crosshair alpha value to display when wielding the rifle" -seta crosshair_rifle_size 0.5 "crosshair size when wielding the rifle" -seta crosshair_tuba "" "crosshair to display when wielding the tuba" -seta crosshair_tuba_color "0.85 0.5 0.25" "crosshair color to display when wielding the tuba" -seta crosshair_tuba_alpha 1 "crosshair alpha value to display when wielding the tuba" -seta crosshair_tuba_size 1 "crosshair size when wielding the tuba" -seta crosshair_fireball "" "crosshair to display when wielding the fireball" -seta crosshair_fireball_color "0.2 1.0 0.2" "crosshair color to display when wielding the fireball" -seta crosshair_fireball_alpha 1 "crosshair alpha value to display when wielding the fireball" -seta crosshair_fireball_size 1 "crosshair size when wielding the fireball" - // ========================= // Crosshair ring settings // ========================= -// ring around crosshair, used for various purposes (such as indicating bullets left in clip, nex charge) +// ring around crosshair, used for various purposes (such as indicating bullets left in clip, vortex charge) seta crosshair_ring 1 "main cvar to enable or disable normal crosshair rings" seta crosshair_ring_inner 0 "allow inner rings to be drawn too" seta crosshair_ring_size 2 "ring size" seta crosshair_ring_alpha 0.2 "ring alpha" -// nexgun ring -seta crosshair_ring_nex 1 "draw a ring showing the current charge of the nexgun" -seta crosshair_ring_nex_alpha 0.15 -seta crosshair_ring_nex_inner_alpha 0.15 -seta crosshair_ring_nex_inner_color_red 0.8 -seta crosshair_ring_nex_inner_color_green 0 -seta crosshair_ring_nex_inner_color_blue 0 -seta crosshair_ring_nex_currentcharge_scale 30 -seta crosshair_ring_nex_currentcharge_movingavg_rate 0.05 +// vortex ring // WEAPONTODO: Make this part of the crosshair line in REGISTER_WEAPON +seta crosshair_ring_vortex 1 "draw a ring showing the current charge of the vortex" +seta crosshair_ring_vortex_alpha 0.15 +seta crosshair_ring_vortex_inner_alpha 0.15 +seta crosshair_ring_vortex_inner_color_red 0.8 +seta crosshair_ring_vortex_inner_color_green 0 +seta crosshair_ring_vortex_inner_color_blue 0 +seta crosshair_ring_vortex_currentcharge_scale 30 +seta crosshair_ring_vortex_currentcharge_movingavg_rate 0.05 // minelayer ring seta crosshair_ring_minelayer 1 diff --git a/defaultXDF.cfg b/defaultXDF.cfg index 5d31426651..e4ba26af8d 100644 --- a/defaultXDF.cfg +++ b/defaultXDF.cfg @@ -3,7 +3,7 @@ // ================ exec defaultXonotic.cfg -exec balanceXDF.cfg +exec balance-xdf.cfg exec physicsXDF.cfg // general gameplay diff --git a/defaultXPM.cfg b/defaultXPM.cfg index 4a55a5ee7c..be3fd4119b 100644 --- a/defaultXPM.cfg +++ b/defaultXPM.cfg @@ -3,11 +3,11 @@ // ================== exec defaultXonotic.cfg -exec balanceXPM.cfg +exec balance-xpm.cfg // general gameplay set g_norecoil 1 -set g_shootfromcenter 1 // hit where you point at with the crosshair (almost so, no shooteye because it's really ugly) +set g_shootfromeye 1 // hit where you point at with the crosshair (promoders don't care about ugliness) set g_balance_kill_antispam 0 set g_forced_respawn 1 set teamplay_mode 2 // friendly fire and self damage diff --git a/defaultXonotic.cfg b/defaultXonotic.cfg index 0884799287..d1d268a4a2 100644 --- a/defaultXonotic.cfg +++ b/defaultXonotic.cfg @@ -57,7 +57,7 @@ _cl_playerskin 0 seta cl_reticle 1 "control for toggling whether ANY zoom reticles are shown" seta cl_reticle_stretch 0 "whether to stretch reticles so they fit the screen (breaks image proportions)" -seta cl_reticle_item_nex 1 "draw aiming reticle for the nex weapon's zoom, 0 disables and values between 0 and 1 change alpha" +seta cl_reticle_item_vortex 1 "draw aiming reticle for the vortex weapon's zoom, 0 disables and values between 0 and 1 change alpha" seta cl_reticle_item_normal 1 "draw reticle when zooming with the zoom button, 0 disables and values between 0 and 1 change alpha" fov 100 seta cl_velocityzoom 0 "velocity based zooming of fov, negative values zoom out" @@ -260,8 +260,8 @@ set sv_timeout_number 2 "how many timeouts one player is allowed to call (gets r set sv_timeout_leadtime 4 "how long the players will be informed that a timeout was called before it starts, in seconds" set sv_timeout_resumetime 3 "how long the remaining timeout-time will be after a player called the timein command" -set g_allow_oldnexbeam 0 "If enabled, clients are allowed to use old v2.3 Nexgun beam" -seta cl_particles_oldnexbeam 0 "Uses the old v2.3 Nexgun beam instead of the new beam, only works if server allows it (g_allow_oldnexbeam 1)" +set g_allow_oldvortexbeam 0 "If enabled, clients are allowed to use old v2.3 Vortex beam" +seta cl_particles_oldvortexbeam 0 "Uses the old v2.3 Vortex beam instead of the new beam, only works if server allows it (g_allow_oldvortexbeam 1)" set g_telefrags 1 "telefragging, i.e. killing someone who stands in the way of someone who is teleporting" set g_telefrags_teamplay 1 "never telefrag team mates" @@ -363,9 +363,9 @@ set bot_ai_keyboard_threshold 0.57 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 "minstanex nex rifle electro rocketlauncher grenadelauncher hagar hlac crylink laser uzi fireball seeker shotgun tuba minelayer" "Desired weapons for far distances ordered by priority" -set bot_ai_custom_weapon_priority_mid "minstanex rocketlauncher nex fireball seeker grenadelauncher electro uzi crylink hlac hagar shotgun laser rifle tuba minelayer" "Desired weapons for middle distances ordered by priority" -set bot_ai_custom_weapon_priority_close "minstanex shotgun nex uzi hlac tuba seeker hagar crylink grenadelauncher electro rocketlauncher laser fireball rifle minelayer" "Desired weapons for close distances ordered by priority" +set bot_ai_custom_weapon_priority_far "vaporizer vortex rifle electro devastator mortar hagar hlac crylink blaster machinegun fireball seeker shotgun 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 crylink hlac hagar shotgun blaster rifle tuba minelayer arc shockwave" "Desired weapons for middle distances ordered by priority" +set bot_ai_custom_weapon_priority_close "vaporizer shotgun vortex machinegun hlac tuba seeker hagar crylink mortar electro devastator blaster fireball rifle minelayer arc shockwave" "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" @@ -419,7 +419,7 @@ set g_use_ammunition 1 "if set to 0 all weapons have unlimited ammunition" set g_pickup_items -1 "if set to 0 all items (health, armor, ammo, weapons...) are removed from the map, if 1 they are forced to spawn" set g_weaponarena "0" "put in a list of weapons to enable a weapon arena mode, or try \"all\" or \"most\"" set g_weaponarena_random "0" "if set to a number, only that weapon count is given on every spawn (randomly)" -set g_weaponarena_random_with_laser "1" "additionally, always provide the laser in random weapon arena games" +set g_weaponarena_random_with_blaster "1" "additionally, always provide the blaster in random weapon arena games" set g_midair 0 "if set to 1 you can only apply damage to your opponent while he is airborne" set g_midair_shieldtime 0.3 "number of seconds you are still invincible since you lost contact to the ground" set g_spawnpoints_auto_move_out_of_solid 0 "if set to 1 you will see a warning if a spawn point was placed inside a solid" @@ -490,7 +490,7 @@ seta timelimit_suddendeath 5 "number of minutes suddendeath mode lasts after all set g_tdm 0 "Team Deathmatch: the team who kills their opponents most often wins" set g_tdm_on_dm_maps 0 "when this is set, all DM maps automatically support TDM" -seta teamplay_mode 4 "default teamplay setting in team games. 1 = no friendly fire, self damage. 2 = friendly fire and self damage enabled. 3 = no friendly fire, but self damage enabled. 4 = obey the following four cvars" +seta teamplay_mode 4 "default teamplay setting in team games. 1 = no friendly fire, self damage. 2 = friendly fire and self damage enabled. 3 = no friendly fire, but self damage enabled. 4 = obey the cvars g_mirrordamage*, g_friendlyfire* and g_teamdamage_threshold*" seta g_mirrordamage 0.700000 "for teamplay 4: mirror damage factor" seta g_mirrordamage_virtual 1 "for teamplay 4: do not actually apply mirror damage, just show graphics effect for it" seta g_friendlyfire 0.500000 "for teamplay 4: fiendly fire factor" @@ -511,8 +511,6 @@ set g_bloodloss 0 "amount of health below which blood loss occurs" set g_footsteps 1 "serverside footstep sounds" -set g_deathglow 1.25 "when enabled, players stop glowing after they die (the value specifies glow fading speed)" - set g_multijump 0 "Number of multiple jumps to allow (jumping again in the air), -1 allows for infinite jumps" set g_multijump_add 0 "0 = make the current z velocity equal to jumpvelocity, 1 = add jumpvelocity to the current z velocity" set g_multijump_speed -999999 "Minimum vertical speed a player must have in order to jump again" @@ -1147,14 +1145,14 @@ sv_allowdownloads 0 // download protocol is evil set g_jump_grunt 0 "Do you make a grunting noise every time you jump? Is it the same grunting noise every time?" -seta cl_weaponpriority "minstanex nex fireball grenadelauncher uzi hagar rifle electro rocketlauncher crylink minelayer shotgun hlac tuba laser porto seeker hook" "weapon priority list" +seta cl_weaponpriority "vaporizer vortex fireball mortar machinegun hagar rifle arc electro devastator crylink minelayer shotgun 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 "rocketlauncher grenadelauncher hagar seeker fireball" "use impulse 200 for prev gun from this list, 210 for best gun, 220 for next gun. Default value: explosives" -seta cl_weaponpriority1 "minstanex nex crylink hlac electro laser" "use impulse 201 for prev gun from this list, 211 for best gun, 221 for next gun. Default value: energy" -seta cl_weaponpriority2 "minstanex nex rifle" "use impulse 202 for prev gun from this list, 212 for best gun, 222 for next gun. Default value: hitscan exact" -seta cl_weaponpriority3 "minstanex nex rifle uzi shotgun" "use impulse 203 for prev gun from this list, 213 for best gun, 223 for next gun. Default value: hitscan all" -seta cl_weaponpriority4 "grenadelauncher minelayer hlac hagar crylink seeker shotgun" "use impulse 204 for prev gun from this list, 214 for best gun, 224 for next gun. Default value: spam weapons" -seta cl_weaponpriority5 "laser hook porto" "use impulse 205 for prev gun from this list, 215 for best gun, 225 for next gun. Default value: weapons for moving" +seta cl_weaponpriority0 "devastator mortar hagar seeker fireball" "use impulse 200 for prev gun from this list, 210 for best gun, 220 for next gun. Default value: explosives" +seta cl_weaponpriority1 "vaporizer vortex crylink hlac arc electro blaster shockwave" "use impulse 201 for prev gun from this list, 211 for best gun, 221 for next gun. Default value: energy" +seta cl_weaponpriority2 "vaporizer vortex rifle" "use impulse 202 for prev gun from this list, 212 for best gun, 222 for next gun. Default value: hitscan exact" +seta cl_weaponpriority3 "vaporizer vortex rifle machinegun shotgun" "use impulse 203 for prev gun from this list, 213 for best gun, 223 for next gun. Default value: hitscan all" +seta cl_weaponpriority4 "mortar minelayer hlac hagar crylink seeker shotgun" "use impulse 204 for prev gun from this list, 214 for best gun, 224 for next gun. Default value: spam weapons" +seta cl_weaponpriority5 "blaster shockwave hook porto" "use impulse 205 for prev gun from this list, 215 for best gun, 225 for next gun. Default value: weapons for moving" seta cl_weaponpriority6 "" "use impulse 206 for prev gun from this list, 216 for best gun, 226 for next gun" seta cl_weaponpriority7 "" "use impulse 207 for prev gun from this list, 217 for best gun, 227 for next gun" seta cl_weaponpriority8 "" "use impulse 208 for prev gun from this list, 218 for best gun, 228 for next gun" @@ -1380,25 +1378,7 @@ volume 1 // sucks less than the old one cl_decals_newsystem 1 -// NOTE: this only replaces weapons on the map -// use g_start_weapon_* to also replace the on-startup weapons! -// example: g_weaponreplace_nex "nex minstanex", then Nexes become MinstaNexes 50% of the times -// set the cvars to "0" to totally disable a weapon -set g_weaponreplace_laser "" -set g_weaponreplace_shotgun "" -set g_weaponreplace_uzi "" -set g_weaponreplace_grenadelauncher "" -set g_weaponreplace_electro "" -set g_weaponreplace_crylink "" -set g_weaponreplace_nex "" -set g_weaponreplace_hagar "" -set g_weaponreplace_rocketlauncher "" -set g_weaponreplace_porto "" -set g_weaponreplace_minstanex "" -set g_weaponreplace_hook "" -set g_weaponreplace_tuba "" -set g_weaponreplace_fireball "" -set sv_q3acompat_machineshotgunswap 0 "shorthand for swapping uzi and shotgun (for Q3A map compatibility in mapinfo files)" +set sv_q3acompat_machineshotgunswap 0 "shorthand for swapping machinegun and shotgun (for Q3A map compatibility in mapinfo files)" set g_movement_highspeed 1 "movement speed modification factor (only changes movement when above maxspeed)" @@ -1552,8 +1532,7 @@ scr_loadingscreen_scale_base 1 scr_loadingscreen_scale_limit 2 // other config files -exec mutator_new_toys.cfg // run BEFORE balance to make sure balance wins -exec balanceXonotic.cfg +exec balance-xonotic.cfg exec effects-normal.cfg exec physicsX.cfg exec turrets.cfg diff --git a/effectinfo.txt b/effectinfo.txt index 782521447a..63d8088865 100644 --- a/effectinfo.txt +++ b/effectinfo.txt @@ -1405,7 +1405,7 @@ liquidfriction 0.8 velocityoffset 0 0 80 originjitter 16 16 16 velocityjitter 424 424 624 -// derbis +// debris effect grenade_explode notunderwater count 12 @@ -2272,7 +2272,7 @@ liquidfriction 0.8 velocityoffset 0 0 80 originjitter 16 16 16 velocityjitter 424 424 624 -// derbis +// debris effect rocket_explode notunderwater count 12 @@ -5500,7 +5500,7 @@ velocityjitter 250 250 150 velocitymultiplier 0.2 sizeincrease 100 stretchfactor 7.6 -// derbis +// debris effect spiderbot_minigun_impact notunderwater count 3 @@ -6248,7 +6248,7 @@ velocityoffset 0 0 370 originjitter 160 160 160 velocityjitter 924 924 924 stretchfactor 0.7 -// derbis +// debris effect explosion_big notunderwater count 16 @@ -7412,7 +7412,6 @@ velocityjitter 64 64 64 //lightshadow 1 // heal ray muzzleflash - effect healray_muzzleflash countabsolute 1 type smoke @@ -8382,7 +8381,7 @@ airfriction 30 originjitter 50 50 50 velocityjitter 320 320 320 rotate -180 180 -9 9 -// fire streched +// fire stretched effect nade_blue_explode count 8 type spark @@ -8455,7 +8454,7 @@ liquidfriction 0.8 velocityoffset 0 0 80 originjitter 16 16 16 velocityjitter 424 424 624 -// derbis +// debris effect nade_blue_explode notunderwater count 12 @@ -8504,7 +8503,7 @@ airfriction 30 originjitter 50 50 50 velocityjitter 320 320 320 rotate -180 180 -9 9 -// fire streched +// fire stretched effect nade_red_explode count 8 type spark @@ -8577,7 +8576,7 @@ liquidfriction 0.8 velocityoffset 0 0 80 originjitter 16 16 16 velocityjitter 424 424 624 -// derbis +// debris effect nade_red_explode notunderwater count 8 @@ -8624,7 +8623,7 @@ airfriction 30 originjitter 50 50 50 velocityjitter 320 320 320 rotate -180 180 -9 9 -// fire streched +// fire stretched effect nade_yellow_explode count 8 type spark @@ -8697,7 +8696,7 @@ liquidfriction 0.8 velocityoffset 0 0 80 originjitter 16 16 16 velocityjitter 424 424 624 -// derbis +// debris effect nade_yellow_explode notunderwater count 8 @@ -8744,7 +8743,7 @@ airfriction 30 originjitter 50 50 50 velocityjitter 320 320 320 rotate -180 180 -9 9 -// fire streched +// fire stretched effect nade_pink_explode count 8 type spark @@ -8817,7 +8816,7 @@ liquidfriction 0.8 velocityoffset 0 0 80 originjitter 16 16 16 velocityjitter 424 424 624 -// derbis +// debris effect nade_pink_explode notunderwater count 8 @@ -8864,7 +8863,7 @@ airfriction 30 originjitter 50 50 50 velocityjitter 320 320 320 rotate -180 180 -9 9 -// fire streched +// fire stretched effect nade_explode count 8 type spark @@ -8937,7 +8936,7 @@ liquidfriction 0.8 velocityoffset 0 0 80 originjitter 16 16 16 velocityjitter 424 424 624 -// derbis +// debris effect nade_explode notunderwater count 8 @@ -8949,3 +8948,240 @@ alpha 644 956 2500 originjitter 64 64 64 velocityjitter 324 324 324 rotate -180 180 -100 100 + +// shockwave_attack +// used nowhere in code +effect shockwave_attack // glow and light + //countabsolute 1 + //type smoke + //color 0xcc0000 0xff0000 + //tex 65 65 + //size 10 15 + //alpha 256 512 6280 + //airfriction 10 + //sizeincrease 1.5 + //stretchfactor 2 + //lightradius 200 + //lightradiusfade 2000 + //lightcolor 3 0.1 0.1 +effect shockwave_attack // electricity + count 1 + type spark + color 0xb44215 0xff0000 + tex 43 43 + size 5 7 + bounce 0 + alpha 4096 4096 20000 + airfriction 1 + originjitter 2 2 2 + velocityjitter 10 10 10 + velocitymultiplier 10 + sizeincrease 1.5 + stretchfactor 2.3 + rotate -180 180 4000 -4000 +effect shockwave_attack // fire + count 1 + type spark + color 0xff4200 0xff0000 + tex 8 15 + size 7 9 + bounce 0 + alpha 4096 4096 20000 + airfriction 1 + originjitter 2 2 2 + velocityjitter 10 10 10 + velocitymultiplier 10 + sizeincrease 1.5 + stretchfactor 2 + +// ARC +// used in qcsrc/server/w_arc.qc +effect arc_lightning // impact decal + countabsolute 1 + type decal + tex 16 32 // fps killer, spamming decals like that + size 16 16 + alpha 32 32 0 + originjitter 2 2 2 + lightradius 50 + lightradiusfade 200 + lightcolor 3.125 4.375 10 + +effect arc_lightning // impact sparks + type static + count 100 + tex 71 74 + rotate 0 360 -36000 36000 + stretchfactor 1 + size 0 5 + sizeincrease -5 + color 0xDDFDFF 0xFDFDFF + alpha 256 256 512 + originjitter 20 20 20 + velocityjitter 250 250 250 + velocitymultiplier 100 + airfriction 110 + +effect arc_lightning // impact sparks (underwater) + type static + underwater + count 100 + tex 71 74 + rotate 0 360 -36000 36000 + stretchfactor 1 + size 0 5 + sizeincrease -5 + color 0xDDFDFF 0xFDFDFF + alpha 256 256 512 + originjitter 20 20 20 + velocityjitter 250 250 250 + // underwater + orientation spark + velocitymultiplier 20 + airfriction 5 + liquidfriction 5 + +effect arc_beam // beam core + type beam + countabsolute 1 + tex 200 200 + color 0xFFFFFF 0xFFFFFF + size 4 4 + alpha 512 512 0 + time 0.005 0.005 + +effect arc_beam // sparks on beam + type spark + stretchfactor 1 + rotate 0 360 360 1000 + time 0.05 0.05 + color 0xfafad2 0xffffff + alpha 256 256 16384 + airfriction -10 + originoffset 0 0 0 + relativeoriginoffset 10 0 0 + originjitter 3 3 3 + velocityoffset 0 0 0 + velocityjitter 100 100 100 + velocitymultiplier 200 + //lightcolor 0 0 0 + trailspacing 10 + tex 71 74 + +effect arc_beam // sparks on beam (underwater) + type spark + underwater + stretchfactor 1 + rotate 0 360 360 1000 + time 0.001 0.001 + color 0xfafad2 0xffffff + alpha 256 256 16384 + liquidfriction -10 + originoffset 0 0 0 + relativeoriginoffset 10 0 0 + originjitter 30 30 30 + velocityoffset 0 0 0 + velocityjitter 100 100 100 + velocitymultiplier 200 + //lightcolor 0 0 0 + trailspacing 10 + tex 71 74 + +// ARC healing effect +effect arc_beam_heal // bubble this... + type static + trailspacing 1500 + tex 74 74 + color 0x20FF20 0x40FF40 + size 0.1 0.1 + sizeincrease 20 + alpha 2048 2048 256 + //time 0.8 0.8 + airfriction -20 + type smoke + relativeoriginoffset 5 0 0 + relativevelocityoffset 100 0 0 + velocityjitter 3 3 3 + velocitymultiplier 200 + +effect arc_beam_healimpact // healing "aura" + type smoke + countabsolute 1 + tex 33 33 + size 32 32 + sizeincrease -1000 + color 0x00ff00 0x84c52f + alpha 40 40 350 + velocitymultiplier 44 + lightradius 20 + lightradiusfade 150 + lightcolor 0 4.375 0 + +effect arc_beam_healimpact // rising "smoke" + type smoke + count 15 + color 0x00ff00 0x84c52f + tex 40 40 + size 0.5 1 + alpha 200 456 512 + airfriction 3 + gravity -2 + velocityjitter 120 120 420 + rotate -180 180 -90 90 + +effect arc_smoke // arc heat smoke (notunderwater) + notunderwater + count 2 + type smoke + tex 0 8 + color 0x4c453f 0x2a241f + size 5 10 + sizeincrease 10 + alpha 32 64 48 + gravity -0.125 + originjitter 0 0 0 + velocityjitter 0 0 16 + airfriction 1 + rotate 0 360 -180 180 + +effect arc_smoke // arc heat bubbles (underwater) + underwater + count 2 + type bubble + tex 62 62 + color 0x404040 0x808080 + size 0.1 1 + alpha 170 256 64 + gravity -0.125 + bounce 1.5 + liquidfriction 0.25 + originjitter 6 6 6 + velocityjitter 16 16 16 + +effect arc_overheat // arc overheat electric bolts + tex 43 43 + count 24 + type spark + color 0xffffff 0x9271fb + size 6 12 + sizeincrease -24 + alpha 128 128 292 + gravity -0.4 + airfriction 5 + liquidfriction 10 + velocityjitter 256 256 256 + originjitter 10 10 10 + +effect arc_overheat_fire // arc overheat bouncing sparks + count 1 + type spark + color 0x4444ff 0xeeeeff + size 0.4 1 + alpha 0 256 640 + gravity 1 + bounce 1.5 + velocityoffset 0 0 80 + velocityjitter 92 92 92 + originjitter 6 6 6 + liquidfriction 5 + velocitymultiplier 80 diff --git a/gamemodes.cfg b/gamemodes.cfg index ec140d30dd..c31f7f9013 100644 --- a/gamemodes.cfg +++ b/gamemodes.cfg @@ -225,10 +225,10 @@ set g_assault 0 "Assault: attack the enemy base as fast as you can, then defend // ============ // clan arena // ============ -set g_ca 0 "Clan Arena: Played in rounds, once you're dead you're out! The team with survivors wins the round." -set g_ca_point_limit 10 "point limit 10 is standard for clan arena" -set g_ca_point_leadlimit 0 -set g_ca_spectate_enemies 0 "Allow spectating enemy player by dead player during clan arena games." +set g_ca 0 "Clan Arena: Played in rounds, once you're dead you're out! The team with survivors wins the round" +seta g_ca_point_limit -1 "Clan Arena point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +seta g_ca_point_leadlimit -1 "Clan Arena point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" +set g_ca_spectate_enemies 0 "Allow spectating enemy player by dead player during clan arena games" set g_ca_warmup 10 "how long the players will have time to run around the map before the round starts" set g_ca_damage2score_multiplier 0.01 set g_ca_round_timelimit 180 "round time limit in seconds" diff --git a/gfx/hud/default/weaponarc.tga b/gfx/hud/default/weaponarc.tga new file mode 100644 index 0000000000..fc48a57f41 Binary files /dev/null and b/gfx/hud/default/weaponarc.tga differ diff --git a/gfx/hud/luminos/weaponarc.tga b/gfx/hud/luminos/weaponarc.tga new file mode 100644 index 0000000000..fc48a57f41 Binary files /dev/null and b/gfx/hud/luminos/weaponarc.tga differ diff --git a/models/domination/dom_axe.tga b/models/domination/dom_axe.tga new file mode 100644 index 0000000000..d6e94cf07e Binary files /dev/null and b/models/domination/dom_axe.tga differ diff --git a/models/domination/dom_axe_glow.tga b/models/domination/dom_axe_glow.tga new file mode 100644 index 0000000000..d6e94cf07e Binary files /dev/null and b/models/domination/dom_axe_glow.tga differ diff --git a/models/domination/dom_bolt.tga b/models/domination/dom_bolt.tga new file mode 100644 index 0000000000..d5d36ee4c8 Binary files /dev/null and b/models/domination/dom_bolt.tga differ diff --git a/models/domination/dom_bolt_glow.tga b/models/domination/dom_bolt_glow.tga new file mode 100644 index 0000000000..d5d36ee4c8 Binary files /dev/null and b/models/domination/dom_bolt_glow.tga differ diff --git a/models/sprites/make-sprites.sh b/models/sprites/make-sprites.sh index 9f08c21f9b..27971f9a02 100644 --- a/models/sprites/make-sprites.sh +++ b/models/sprites/make-sprites.sh @@ -174,23 +174,6 @@ sprite nb-ball "Ball" e8d8a0 000000 0.0 sprite ka-ball "Ball" 00ffff 000000 0.0 sprite ka-ballcarrier "Ball carrier" ff0000 000000 0.0 -sprite wpn-laser "Laser" ff8080 000000 0.0 # bright red -sprite wpn-shotgun "Shotgun" 804000 000000 0.0 # brown -sprite wpn-uzi "Machine Gun" ffff00 000000 0.0 # yellow -sprite wpn-gl "Mortar" ff0000 000000 0.0 # red -sprite wpn-electro "Electro" 0080ff 000000 0.0 # bluish cyan -sprite wpn-crylink "Crylink" ff80ff 000000 0.0 # bright pink -sprite wpn-nex "Nex" 00ffff 000000 0.0 # cyan -sprite wpn-hagar "Hagar" ffff80 000000 0.0 # bright yellow -sprite wpn-rl "Rocket Launcher" ffff00 000000 0.0 # yellow -sprite wpn-porto "Port-O-Launch" 808080 000000 0.0 # grey -sprite wpn-minstanex "Minstanex" 80ffff 000000 0.0 # bright cyan -sprite wpn-hookgun "Hook" 008000 000000 0.0 # dark green -sprite wpn-fireball "Fireball" ff8000 000000 0.0 # orange -sprite wpn-hlac "HLAC" 00ff00 000000 0.0 # green -sprite wpn-campingrifle "Rifle" 80ff00 000000 0.0 # orange -sprite wpn-minelayer "Mine Layer" ccff00 000000 0.0 # yellowish orange - sprite dom-neut "Control point" 00ffff 000000 0.0 sprite dom-red "Control point" ff0000 000000 0.0 sprite dom-blue "Control point" 0050ff 000000 0.0 diff --git a/models/weapons/g_arc.md3 b/models/weapons/g_arc.md3 new file mode 100644 index 0000000000..8783739641 Binary files /dev/null and b/models/weapons/g_arc.md3 differ diff --git a/models/weapons/h_arc.iqm b/models/weapons/h_arc.iqm new file mode 100644 index 0000000000..9c4a29dbbc Binary files /dev/null and b/models/weapons/h_arc.iqm differ diff --git a/models/weapons/h_arc.iqm.framegroups b/models/weapons/h_arc.iqm.framegroups new file mode 100644 index 0000000000..1c95207bde --- /dev/null +++ b/models/weapons/h_arc.iqm.framegroups @@ -0,0 +1,6 @@ +1 8 20 0 // fire +9 23 20 0 // fire2 +32 200 20 1 // idle +232 40 20 0 // reload +// compile opts used in the iqm exporter (apparently needed to prevent insane ram usage): +// fire:1:8, fire2:1:23, idle:1:200, reload:1:40 diff --git a/models/weapons/v_arc.md3 b/models/weapons/v_arc.md3 new file mode 100644 index 0000000000..7415aba604 Binary files /dev/null and b/models/weapons/v_arc.md3 differ diff --git a/models/weapons/w_crylink.zym b/models/weapons/w_crylink.zym deleted file mode 100644 index 4aea5b8614..0000000000 Binary files a/models/weapons/w_crylink.zym and /dev/null differ diff --git a/models/weapons/w_electro.zym b/models/weapons/w_electro.zym deleted file mode 100644 index 70cbd174e8..0000000000 Binary files a/models/weapons/w_electro.zym and /dev/null differ diff --git a/models/weapons/w_gl.zym b/models/weapons/w_gl.zym deleted file mode 100644 index 5f28a0a1de..0000000000 Binary files a/models/weapons/w_gl.zym and /dev/null differ diff --git a/models/weapons/w_hagar.zym b/models/weapons/w_hagar.zym deleted file mode 100644 index d8d78710fe..0000000000 Binary files a/models/weapons/w_hagar.zym and /dev/null differ diff --git a/models/weapons/w_laser.zym b/models/weapons/w_laser.zym deleted file mode 100644 index 459b932ac2..0000000000 Binary files a/models/weapons/w_laser.zym and /dev/null differ diff --git a/models/weapons/w_nex.zym b/models/weapons/w_nex.zym deleted file mode 100644 index 26ae924768..0000000000 Binary files a/models/weapons/w_nex.zym and /dev/null differ diff --git a/models/weapons/w_rl.zym b/models/weapons/w_rl.zym deleted file mode 100644 index 6d3f70193f..0000000000 Binary files a/models/weapons/w_rl.zym and /dev/null differ diff --git a/models/weapons/w_shotgun.zym b/models/weapons/w_shotgun.zym deleted file mode 100644 index 031fa8609e..0000000000 Binary files a/models/weapons/w_shotgun.zym and /dev/null differ diff --git a/models/weapons/w_uzi.zym b/models/weapons/w_uzi.zym deleted file mode 100644 index f869237482..0000000000 Binary files a/models/weapons/w_uzi.zym and /dev/null differ diff --git a/mutator_new_toys.cfg b/mutator_new_toys.cfg deleted file mode 100644 index 9835f41ddb..0000000000 --- a/mutator_new_toys.cfg +++ /dev/null @@ -1,166 +0,0 @@ -set g_new_toys 0 "Mutator 'New Toys': enable extra fun guns" -set g_new_toys_autoreplace 2 "0: never replace, 1: always auto replace guns by available new toys, 2: randomly auto replace guns by available new toys" - -set g_weaponreplace_hlac "" -set g_weaponreplace_minelayer "" -set g_weaponreplace_rifle "" -set g_weaponreplace_seeker "" - -set g_start_weapon_hlac -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" // UNTIL IT CAN BE REMOVED FROM CODE -set g_start_weapon_minelayer -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" -set g_start_weapon_rifle -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" // UNTIL IT CAN BE REMOVED FROM CODE -set g_start_weapon_seeker -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" - -// {{{ hlac -set g_balance_hlac_primary_spread_min 0.01 -set g_balance_hlac_primary_spread_max 0.25 -set g_balance_hlac_primary_spread_add 0.0045 -set g_balance_hlac_primary_spread_crouchmod 0.25 - -set g_balance_hlac_primary_damage 18 -set g_balance_hlac_primary_edgedamage 9 -set g_balance_hlac_primary_force 90 -set g_balance_hlac_primary_radius 70 -set g_balance_hlac_primary_speed 9000 -set g_balance_hlac_primary_lifetime 5 - -set g_balance_hlac_primary_refire 0.15 -set g_balance_hlac_primary_animtime 0.4 -set g_balance_hlac_primary_ammo 1 - -set g_balance_hlac_secondary 1 -set g_balance_hlac_secondary_spread 0.15 -set g_balance_hlac_secondary_spread_crouchmod 0.5 - -set g_balance_hlac_secondary_damage 15 -set g_balance_hlac_secondary_edgedamage 7.5 -set g_balance_hlac_secondary_force 90 -set g_balance_hlac_secondary_radius 70 -set g_balance_hlac_secondary_speed 9000 -set g_balance_hlac_secondary_lifetime 5 - -set g_balance_hlac_secondary_refire 1 -set g_balance_hlac_secondary_animtime 0.3 -set g_balance_hlac_secondary_ammo 10 -set g_balance_hlac_secondary_shots 6 - -set g_balance_hlac_switchdelay_drop 0.2 -set g_balance_hlac_switchdelay_raise 0.2 - -set g_balance_hlac_reload_ammo 0 //default: 20 -set g_balance_hlac_reload_time 2 -// }}} -// {{{ minelayer -set g_balance_minelayer_damage 40 -set g_balance_minelayer_edgedamage 20 -set g_balance_minelayer_force 250 -set g_balance_minelayer_radius 175 -set g_balance_minelayer_proximityradius 150 -set g_balance_minelayer_speed 1000 -set g_balance_minelayer_lifetime 10 -set g_balance_minelayer_lifetime_countdown 0.5 -set g_balance_minelayer_refire 1.5 -set g_balance_minelayer_animtime 0.4 -set g_balance_minelayer_ammo 4 -set g_balance_minelayer_health 15 -set g_balance_minelayer_limit 3 // 0 disables the limit -set g_balance_minelayer_protection 0 // don't explode if the mine would hurt the owner or a team mate -set g_balance_minelayer_damageforcescale 0 -set g_balance_minelayer_detonatedelay -1 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time -set g_balance_minelayer_time 0.5 -set g_balance_minelayer_remote_damage 45 -set g_balance_minelayer_remote_edgedamage 40 -set g_balance_minelayer_remote_radius 200 -set g_balance_minelayer_remote_force 300 -set g_balance_minelayer_switchdelay_drop 0.2 -set g_balance_minelayer_switchdelay_raise 0.2 -set g_balance_minelayer_reload_ammo 0 //default: 15 -set g_balance_minelayer_reload_time 2 -// }}} -// {{{ rifle -set g_balance_rifle_bursttime 0 -set g_balance_rifle_primary_tracer 1 -set g_balance_rifle_primary_shots 1 -set g_balance_rifle_primary_damage 80 -set g_balance_rifle_primary_spread 0 -set g_balance_rifle_primary_force 100 -set g_balance_rifle_primary_refire 1.2 -set g_balance_rifle_primary_animtime 0.4 -set g_balance_rifle_primary_ammo 10 -set g_balance_rifle_primary_solidpenetration 62.2 -set g_balance_rifle_primary_burstcost 0 -set g_balance_rifle_primary_bullethail 0 // empty magazine on shot -set g_balance_rifle_secondary 1 -set g_balance_rifle_secondary_reload 0 -set g_balance_rifle_secondary_tracer 0 -set g_balance_rifle_secondary_shots 4 -set g_balance_rifle_secondary_damage 20 -set g_balance_rifle_secondary_spread 0.04 -set g_balance_rifle_secondary_force 50 -set g_balance_rifle_secondary_refire 0.9 -set g_balance_rifle_secondary_animtime 0.3 -set g_balance_rifle_secondary_ammo 10 -set g_balance_rifle_secondary_solidpenetration 15.5 -set g_balance_rifle_secondary_burstcost 0 -set g_balance_rifle_secondary_bullethail 0 // empty magazine on shot -set g_balance_rifle_switchdelay_drop 0.2 -set g_balance_rifle_switchdelay_raise 0.2 -set g_balance_rifle_reload_ammo 80 //default: 80 -set g_balance_rifle_reload_time 2 -// }}} -// {{{ seeker -set g_balance_seeker_type 0 // 0 = old seeker, 1 = new seeker -set g_balance_seeker_flac_ammo 1 -set g_balance_seeker_flac_animtime 0.1 -set g_balance_seeker_flac_damage 15 -set g_balance_seeker_flac_edgedamage 10 -set g_balance_seeker_flac_force 50 -set g_balance_seeker_flac_lifetime 0.1 -set g_balance_seeker_flac_lifetime_rand 0.05 -set g_balance_seeker_flac_radius 100 -set g_balance_seeker_flac_refire 0.1 -set g_balance_seeker_flac_speed 3000 -set g_balance_seeker_flac_speed_up 1000 -set g_balance_seeker_flac_speed_z 0 -set g_balance_seeker_flac_spread 0.4 -set g_balance_seeker_tag_ammo 1 -set g_balance_seeker_tag_animtime 0.2 -set g_balance_seeker_tag_damageforcescale 4 -set g_balance_seeker_tag_health 5 -set g_balance_seeker_tag_lifetime 15 -set g_balance_seeker_tag_refire 0.75 // LOG: 0.7 -> 0.75 -set g_balance_seeker_tag_speed 5000 -set g_balance_seeker_tag_spread 0 -set g_balance_seeker_tag_tracker_lifetime 10 -set g_balance_seeker_missile_accel 1400 -set g_balance_seeker_missile_ammo 2 -set g_balance_seeker_missile_animtime 0.2 -set g_balance_seeker_missile_count 3 // LOG: 8 -> 3 -set g_balance_seeker_missile_damage 30 // LOG: 15 -> 30 -set g_balance_seeker_missile_damageforcescale 4 -set g_balance_seeker_missile_decel 1400 -set g_balance_seeker_missile_delay 0.25 -set g_balance_seeker_missile_edgedamage 10 -set g_balance_seeker_missile_force 150 // LOG: 100 -> 150 -set g_balance_seeker_missile_health 5 -set g_balance_seeker_missile_lifetime 15 -set g_balance_seeker_missile_proxy 0 -set g_balance_seeker_missile_proxy_delay 0.2 -set g_balance_seeker_missile_proxy_maxrange 45 -set g_balance_seeker_missile_radius 80 -set g_balance_seeker_missile_refire 0.5 -set g_balance_seeker_missile_smart 1 -set g_balance_seeker_missile_smart_mindist 800 -set g_balance_seeker_missile_smart_trace_max 2500 -set g_balance_seeker_missile_smart_trace_min 1000 -set g_balance_seeker_missile_speed 700 -set g_balance_seeker_missile_speed_up 300 -set g_balance_seeker_missile_speed_z 0 -set g_balance_seeker_missile_speed_max 1300 // LOG: 1400 -> 1300 -set g_balance_seeker_missile_spread 0 -set g_balance_seeker_missile_turnrate 0.65 -set g_balance_seeker_switchdelay_drop 0.2 -set g_balance_seeker_switchdelay_raise 0.2 -set g_balance_seeker_reload_ammo 0 //default: 15 -set g_balance_seeker_reload_time 2 -// End new seeker diff --git a/mutators.cfg b/mutators.cfg index 63a4356b82..c6716585c3 100644 --- a/mutators.cfg +++ b/mutators.cfg @@ -231,6 +231,13 @@ set g_campcheck_damage 100 set g_campcheck_distance 1800 +// ========== +// new toys +// ========== +set g_new_toys 0 "Mutator 'New Toys': enable extra fun guns" +set g_new_toys_autoreplace 2 "0: never replace, 1: always auto replace guns by available new toys, 2: randomly auto replace guns by available new toys" + + // ======= // buffs // ======= @@ -251,7 +258,7 @@ set g_buffs_resistance_blockpercent 0.7 "damage reduction multiplier, higher val set g_buffs_medic 1 "medic buff: increased regeneration speed, extra health, chance to survive a fatal attack" set g_buffs_medic_survive_chance 0.6 "multiplier chance of player surviving a fatal hit" set g_buffs_medic_survive_health 5 "amount of health player survives with after taking a fatal hit" -set g_buffs_medic_rot 0.7 "health rot rate multiplier" +set g_buffs_medic_rot 0.2 "health rot rate multiplier" set g_buffs_medic_max 1.5 "stable health medic limit multiplier" set g_buffs_medic_regen 1.7 "health medic rate multiplier" set g_buffs_vengeance 1 "vengeance buff: attackers also take damage" diff --git a/physicsX.cfg b/physicsX.cfg index c90020fdfa..e03a6d4171 100644 --- a/physicsX.cfg +++ b/physicsX.cfg @@ -1,7 +1,7 @@ g_mod_physics Xonotic // current Xonotic physics -sv_gravity 720 +sv_gravity 800 sv_maxspeed 360 sv_maxairspeed 360 @@ -19,12 +19,12 @@ sv_stepheight 31 // jump duration == 2*sv_jumpvelocity / sv_gravity // in this case: 0.6888888888 (thus either 20 or 21 frames) // jump height == sv_jumpvelocity^2 / (2*sv_gravity) -// in this case: 42.71 +// in this case: 42.25 // player: 24+45 qu -// total: 111.71qu +// total: 111.25qu // this is smaller than 112 qu, so a 112 qu high corridor (7 of 8 grid units in // the 16 grid, and the 8th unit used for wall/floor) just lets a player jump! -sv_jumpvelocity 248 +sv_jumpvelocity 260 sv_wateraccelerate -1 sv_waterfriction -1 sv_airaccel_sideways_friction 0 diff --git a/qcsrc/client/Defs.qc b/qcsrc/client/Defs.qc index b89a177f04..24a5fbfc0b 100644 --- a/qcsrc/client/Defs.qc +++ b/qcsrc/client/Defs.qc @@ -223,8 +223,8 @@ string shortmapname; float tempdb; float ClientProgsDB; vector hook_shotorigin[4]; -vector electro_shotorigin[4]; -vector gauntlet_shotorigin[4]; +vector lightning_shotorigin[4]; + #ifdef BLURTEST float blurtest_time0, blurtest_time1, blurtest_radius, blurtest_power; @@ -251,7 +251,7 @@ float w_deathtype, w_issilent, w_random; vector w_org, w_backoff; float rifle_scope; -float nex_scope; +float vortex_scope; float minelayer_maxmines; @@ -262,7 +262,7 @@ float bgmtime; string weaponorder_byimpulse; string weaponorder_bypriority; -float nex_charge_movingavg; +float vortex_charge_movingavg; float serverflags; diff --git a/qcsrc/client/Main.qc b/qcsrc/client/Main.qc index c0e37ae621..d1fe46d235 100644 --- a/qcsrc/client/Main.qc +++ b/qcsrc/client/Main.qc @@ -53,7 +53,7 @@ void CSQC_Init(void) check_unacceptable_compiler_bugs(); #ifdef WATERMARK - printf(_("^4CSQC Build information: ^1%s\n"), WATERMARK); + dprintf("^4CSQC Build information: ^1%s\n", WATERMARK); #endif float i; @@ -96,6 +96,8 @@ void CSQC_Init(void) for(i = 0; i < MAX_HUD_FIELDS; ++i) hud_title[i] = strzone("(null)"); + Cmd_HUD_SetFields(0); + postinit = false; calledhooks = 0; @@ -125,7 +127,6 @@ void CSQC_Init(void) Hook_Precache(); GibSplash_Precache(); Casings_Precache(); - DamageInfo_Precache(); Vehicles_Precache(); turrets_precache(); Tuba_Precache(); @@ -133,8 +134,8 @@ void CSQC_Init(void) if(autocvar_cl_reticle) { - if(autocvar_cl_reticle_item_normal) { precache_pic("gfx/reticle_normal"); } - if(autocvar_cl_reticle_item_nex) { precache_pic("gfx/reticle_nex"); } + precache_pic("gfx/reticle_normal"); + // weapon reticles are precached in weapon files } get_mi_min_max_texcoords(1); // try the CLEVER way first @@ -316,8 +317,6 @@ void Porto_Init(); void TrueAim_Init(); void PostInit(void) { - localcmd(strcat("\nscoreboard_columns_set ", autocvar_scoreboard_columns, ";\n")); - entity playerchecker; playerchecker = spawn(); playerchecker.think = Playerchecker_Think; @@ -606,6 +605,27 @@ void Ent_Nagger() warmup_stage = (nags & 16); } +void Ent_EliminatedPlayers() +{ + float sf, i, j, b, f; + + sf = ReadByte(); + if(sf & 1) + { + for(j = 0; j < maxclients; ++j) + if(playerslots[j]) + playerslots[j].eliminated = 1; + for(i = 1; i <= maxclients; i += 8) + { + f = ReadByte(); + for(j = i-1, b = 1; b < 256; b *= 2, ++j) + if (!(f & b)) + if(playerslots[j]) + playerslots[j].eliminated = 0; + } + } +} + void Ent_RandomSeed() { float s; @@ -804,6 +824,7 @@ void CSQC_Ent_Update(float bIsNewEntity) case ENT_CLIENT_RAINSNOW: Ent_RainOrSnow(); break; case ENT_CLIENT_LASER: Ent_Laser(); break; case ENT_CLIENT_NAGGER: Ent_Nagger(); break; + case ENT_CLIENT_ELIMINATEDPLAYERS: Ent_EliminatedPlayers(); break; case ENT_CLIENT_WAYPOINT: Ent_WaypointSprite(); break; case ENT_CLIENT_RADARLINK: Ent_RadarLink(); break; case ENT_CLIENT_PROJECTILE: Ent_Projectile(); break; @@ -823,8 +844,7 @@ void CSQC_Ent_Update(float bIsNewEntity) case ENT_CLIENT_WARPZONE_TELEPORTED: WarpZone_Teleported_Read(bIsNewEntity); break; case ENT_CLIENT_TRIGGER_MUSIC: Ent_ReadTriggerMusic(); break; case ENT_CLIENT_HOOK: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_HOOK); break; - case ENT_CLIENT_LGBEAM: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_LGBEAM); break; - case ENT_CLIENT_GAUNTLET: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_GAUNTLET); break; + case ENT_CLIENT_ARC_BEAM: Ent_ReadArcBeam(bIsNewEntity); break; case ENT_CLIENT_ACCURACY: Ent_ReadAccuracy(); break; case ENT_CLIENT_AUXILIARYXHAIR: Net_AuXair2(bIsNewEntity); break; case ENT_CLIENT_TURRET: ent_turret(); break; @@ -838,7 +858,7 @@ void CSQC_Ent_Update(float bIsNewEntity) default: //error(strcat(_("unknown entity type in CSQC_Ent_Update: %d\n"), self.enttype)); - error(sprintf(_("Unknown entity type in CSQC_Ent_Update (enttype: %d, edict: %d, classname: %s)\n"), self.enttype, num_for_edict(self), self.classname)); + error(sprintf("Unknown entity type in CSQC_Ent_Update (enttype: %d, edict: %d, classname: %s)\n", self.enttype, num_for_edict(self), self.classname)); break; } @@ -942,11 +962,15 @@ void Ent_ScoresInfo() HUD_ModIcons_SetFunc(); for(i = 0; i < MAX_SCORE; ++i) { + if(scores_label[i]) + strunzone(scores_label[i]); scores_label[i] = strzone(ReadString()); scores_flags[i] = ReadByte(); } for(i = 0; i < MAX_TEAMSCORE; ++i) { + if(teamscores_label[i]) + strunzone(teamscores_label[i]); teamscores_label[i] = strzone(ReadString()); teamscores_flags[i] = ReadByte(); } @@ -964,14 +988,10 @@ void Ent_Init() hook_shotorigin[1] = decompressShotOrigin(ReadInt24_t()); hook_shotorigin[2] = decompressShotOrigin(ReadInt24_t()); hook_shotorigin[3] = decompressShotOrigin(ReadInt24_t()); - electro_shotorigin[0] = decompressShotOrigin(ReadInt24_t()); - electro_shotorigin[1] = decompressShotOrigin(ReadInt24_t()); - electro_shotorigin[2] = decompressShotOrigin(ReadInt24_t()); - electro_shotorigin[3] = decompressShotOrigin(ReadInt24_t()); - gauntlet_shotorigin[0] = decompressShotOrigin(ReadInt24_t()); - gauntlet_shotorigin[1] = decompressShotOrigin(ReadInt24_t()); - gauntlet_shotorigin[2] = decompressShotOrigin(ReadInt24_t()); - gauntlet_shotorigin[3] = decompressShotOrigin(ReadInt24_t()); + arc_shotorigin[0] = decompressShotOrigin(ReadInt24_t()); + arc_shotorigin[1] = decompressShotOrigin(ReadInt24_t()); + arc_shotorigin[2] = decompressShotOrigin(ReadInt24_t()); + arc_shotorigin[3] = decompressShotOrigin(ReadInt24_t()); if(forcefog) strunzone(forcefog); @@ -979,12 +999,12 @@ void Ent_Init() armorblockpercent = ReadByte() / 255.0; - g_balance_grenadelauncher_bouncefactor = ReadCoord(); - g_balance_grenadelauncher_bouncestop = ReadCoord(); + g_balance_mortar_bouncefactor = ReadCoord(); + g_balance_mortar_bouncestop = ReadCoord(); g_balance_electro_secondary_bouncefactor = ReadCoord(); g_balance_electro_secondary_bouncestop = ReadCoord(); - nex_scope = !ReadByte(); + vortex_scope = !ReadByte(); rifle_scope = !ReadByte(); serverflags = ReadByte(); @@ -1181,7 +1201,7 @@ void Net_WeaponComplain() if(complain_weapon_name) strunzone(complain_weapon_name); - complain_weapon_name = strzone(ReadString()); + complain_weapon_name = strzone(WEP_NAME(complain_weapon)); complain_weapon_type = ReadByte(); @@ -1225,16 +1245,16 @@ float CSQC_Parse_TempEntity() Net_ReadRace(); bHandled = true; break; - case TE_CSQC_NEXGUNBEAMPARTICLE: - Net_ReadNexgunBeamParticle(); + case TE_CSQC_VORTEXBEAMPARTICLE: + Net_ReadVortexBeamParticle(); bHandled = true; break; case TE_CSQC_TEAMNAGGER: Net_TeamNagger(); bHandled = true; break; - case TE_CSQC_LIGHTNINGARC: - Net_ReadLightningarc(); + case TE_CSQC_ARC: + Net_ReadArc(); bHandled = true; break; case TE_CSQC_PINGPLREPORT: @@ -1253,6 +1273,10 @@ float CSQC_Parse_TempEntity() cl_notice_read(); bHandled = true; break; + case TE_CSQC_SHOCKWAVEPARTICLE: + Net_ReadShockwaveParticle(); + bHandled = true; + break; default: // No special logic for this temporary entity; return 0 so the engine can handle it bHandled = false; @@ -1302,7 +1326,7 @@ string getcommandkey(string text, string command) return text; } else if (autocvar_hud_showbinds > 1) - return sprintf(_("%s (%s)"), text, keys); + return sprintf("%s (%s)", text, keys); else return keys; } diff --git a/qcsrc/client/View.qc b/qcsrc/client/View.qc index 166ea07f09..890bf76621 100644 --- a/qcsrc/client/View.qc +++ b/qcsrc/client/View.qc @@ -104,7 +104,7 @@ vector GetCurrentFov(float fov) zoomdir = button_zoom; if(hud == HUD_NORMAL) - if((activeweapon == WEP_NEX && nex_scope) || (activeweapon == WEP_RIFLE && rifle_scope)) // do NOT use switchweapon here + if((activeweapon == WEP_VORTEX && vortex_scope) || (activeweapon == WEP_RIFLE && rifle_scope)) // do NOT use switchweapon here zoomdir += button_attack2; if(spectatee_status > 0 || isdemo()) { @@ -162,13 +162,13 @@ vector GetCurrentFov(float fov) else setsensitivityscale(1); - makevectors(view_angles); - if(autocvar_cl_velocityzoom && autocvar_cl_velocityzoom_type) // _type = 0 disables velocity zoom too { if(intermission) { curspeed = 0; } else { + + makevectors(view_angles); v = pmove_vel; if(csqcplayer) v = csqcplayer.velocity; @@ -269,7 +269,7 @@ float EnemyHitCheck() float TrueAimCheck() { - float nudge = 1; // added to traceline target and subtracted from result + float nudge = 1; // added to traceline target and subtracted from result TOOD(divVerent): do we still need this? Doesn't the engine do this now for us? vector vecs, trueaimpoint, w_shotorg; vector mi, ma, dv; float shottype; @@ -280,15 +280,15 @@ float TrueAimCheck() ta = trueaim; mv = MOVE_NOMONSTERS; - switch(activeweapon) + switch(activeweapon) // WEAPONTODO { case WEP_TUBA: // no aim case WEP_PORTO: // shoots from eye case WEP_HOOK: // no trueaim - case WEP_GRENADE_LAUNCHER: // toss curve + case WEP_MORTAR: // toss curve return SHOTTYPE_HITWORLD; - case WEP_NEX: - case WEP_MINSTANEX: + case WEP_VORTEX: + case WEP_VAPORIZER: mv = MOVE_NORMAL; break; case WEP_RIFLE: @@ -300,7 +300,7 @@ float TrueAimCheck() return EnemyHitCheck(); } break; - case WEP_ROCKET_LAUNCHER: // projectile has a size! + case WEP_DEVASTATOR: // projectile has a size! mi = '-3 -3 -3'; ma = '3 3 3'; break; @@ -365,6 +365,7 @@ float camera_mode; const float CAMERA_FREE = 1; const float CAMERA_CHASE = 2; float reticle_type; +string reticle_image; string NextFrameCommand; void CSQC_SPIDER_HUD(); void CSQC_RAPTOR_HUD(); @@ -379,7 +380,7 @@ float spectatee_status_prev; // for preventing hitsound when switching spectatee float damage_dealt_total, damage_dealt_total_prev; float typehit_time, typehit_time_prev; float hitindication_crosshair_size; -float use_nex_chargepool; +float use_vortex_chargepool; float myhealth, myhealth_prev; float myhealth_flash; @@ -869,50 +870,64 @@ void CSQC_UpdateView(float w, float h) R_EndPolygon(); } - // Draw the aiming reticle for weapons that use it - // reticle_type is changed to the item we are zooming / aiming with, to decide which reticle to use - // It must be a persisted float for fading out to work properly (you let go of the zoom button for - // the view to go back to normal, so reticle_type would become 0 as we fade out) - if(spectatee_status || is_dead || hud != HUD_NORMAL) - reticle_type = 0; // prevent reticle from showing during the respawn zoom effect or for spectators - else if((activeweapon == WEP_NEX || activeweapon == WEP_RIFLE || activeweapon == WEP_MINSTANEX) && (button_zoom || zoomscript_caught)) - reticle_type = 2; // nex zoom - else if(button_zoom || zoomscript_caught) - reticle_type = 1; // normal zoom - else if((activeweapon == WEP_NEX) && button_attack2) - reticle_type = 2; // nex zoom - - if(reticle_type && autocvar_cl_reticle) + if(autocvar_cl_reticle) { - if(autocvar_cl_reticle_stretch) + // Draw the aiming reticle for weapons that use it + // reticle_type is changed to the item we are zooming / aiming with, to decide which reticle to use + // It must be a persisted float for fading out to work properly (you let go of the zoom button for + // the view to go back to normal, so reticle_type would become 0 as we fade out) + if(spectatee_status || is_dead || hud != HUD_NORMAL) { - reticle_size_x = vid_conwidth; - reticle_size_y = vid_conheight; - reticle_pos_x = 0; - reticle_pos_y = 0; + // no zoom reticle while dead + reticle_type = 0; } - else + else if(WEP_ACTION(activeweapon, WR_ZOOMRETICLE) && autocvar_cl_reticle_weapon) { - reticle_size_x = max(vid_conwidth, vid_conheight); - reticle_size_y = max(vid_conwidth, vid_conheight); - reticle_pos_x = (vid_conwidth - reticle_size_x) / 2; - reticle_pos_y = (vid_conheight - reticle_size_y) / 2; + if(reticle_image != "") { reticle_type = 2; } + else { reticle_type = 0; } } - - f = current_zoomfraction; - if(zoomscript_caught) - f = 1; - if(autocvar_cl_reticle_item_normal) + else if(button_zoom || zoomscript_caught) { - if(reticle_type == 1 && f) - drawpic(reticle_pos, "gfx/reticle_normal", reticle_size, '1 1 1', f * autocvar_cl_reticle_item_normal, DRAWFLAG_NORMAL); + // normal zoom + reticle_type = 1; } - if(autocvar_cl_reticle_item_nex) + + if(reticle_type) { - if(reticle_type == 2 && f) - drawpic(reticle_pos, "gfx/reticle_nex", reticle_size, '1 1 1', f * autocvar_cl_reticle_item_nex, DRAWFLAG_NORMAL); + if(autocvar_cl_reticle_stretch) + { + reticle_size_x = vid_conwidth; + reticle_size_y = vid_conheight; + reticle_pos_x = 0; + reticle_pos_y = 0; + } + else + { + reticle_size_x = max(vid_conwidth, vid_conheight); + reticle_size_y = max(vid_conwidth, vid_conheight); + reticle_pos_x = (vid_conwidth - reticle_size_x) / 2; + reticle_pos_y = (vid_conheight - reticle_size_y) / 2; + } + + if(zoomscript_caught) + f = 1; + else + f = current_zoomfraction; + + if(f) + { + switch(reticle_type) + { + case 1: drawpic(reticle_pos, "gfx/reticle_normal", reticle_size, '1 1 1', f * autocvar_cl_reticle_normal_alpha, DRAWFLAG_NORMAL); break; + case 2: drawpic(reticle_pos, reticle_image, reticle_size, '1 1 1', f * autocvar_cl_reticle_weapon_alpha, DRAWFLAG_NORMAL); break; + } + } } } + else + { + if(reticle_type != 0) { reticle_type = 0; } + } // improved polyblend @@ -1287,42 +1302,39 @@ void CSQC_UpdateView(float w, float h) shottype = SHOTTYPE_HITWORLD; vector wcross_color = '0 0 0', wcross_size = '0 0 0'; - string wcross_wep = "", wcross_name; + string wcross_name = ""; float wcross_scale, wcross_blur; - if (autocvar_crosshair_per_weapon || (autocvar_crosshair_color_special == 1)) + if(autocvar_crosshair_per_weapon || (autocvar_crosshair_color_special == 1)) { e = get_weaponinfo(switchingweapon); - if (e && e.netname != "") + if(e) { - wcross_wep = e.netname; if(autocvar_crosshair_per_weapon) { - wcross_resolution *= cvar(strcat("crosshair_", wcross_wep, "_size")); - if (wcross_resolution == 0) - return; - wcross_alpha *= cvar(strcat("crosshair_", wcross_wep, "_alpha")); - if (wcross_alpha == 0) - return; - - wcross_style = cvar_string(strcat("crosshair_", wcross_wep)); - if(wcross_style == "" || wcross_style == "0") - wcross_style = wcross_wep; + // WEAPONTODO: access these through some general settings (with non-balance config settings) + //wcross_resolution *= cvar(strcat("crosshair_", wcross_wep, "_size")); + //if (wcross_resolution == 0) + //return; + + //wcross_style = cvar_string(strcat("crosshair_", wcross_wep)); + wcross_resolution *= e.w_crosshair_size; + wcross_name = e.w_crosshair; } } } - //printf("crosshair style: %s\n", wcross_style); - wcross_name = strcat("gfx/crosshair", wcross_style); + if(wcross_name == "") + wcross_name = strcat("gfx/crosshair", wcross_style); // MAIN CROSSHAIR COLOR DECISION switch(autocvar_crosshair_color_special) { case 1: // crosshair_color_per_weapon { - if(wcross_wep != "") + if(e) { - wcross_color = stov(cvar_string(sprintf("crosshair_%s_color", wcross_wep))); + wcross_color = e.wpcolor; break; } else { goto normalcolor; } @@ -1508,32 +1520,34 @@ void CSQC_UpdateView(float w, float h) weapon_clipload = getstati(STAT_WEAPON_CLIPLOAD); weapon_clipsize = getstati(STAT_WEAPON_CLIPSIZE); - float nex_charge, nex_chargepool; - nex_charge = getstatf(STAT_NEX_CHARGE); - nex_chargepool = getstatf(STAT_NEX_CHARGEPOOL); + float vortex_charge, vortex_chargepool; + vortex_charge = getstatf(STAT_VORTEX_CHARGE); + vortex_chargepool = getstatf(STAT_VORTEX_CHARGEPOOL); + + float arc_heat = getstatf(STAT_ARC_HEAT); - if(nex_charge_movingavg == 0) // this should only happen if we have just loaded up the game - nex_charge_movingavg = nex_charge; + if(vortex_charge_movingavg == 0) // this should only happen if we have just loaded up the game + vortex_charge_movingavg = vortex_charge; // handle the values - if (autocvar_crosshair_ring && activeweapon == WEP_NEX && nex_charge && autocvar_crosshair_ring_nex) // ring around crosshair representing velocity-dependent damage for the nex + if (autocvar_crosshair_ring && activeweapon == WEP_VORTEX && vortex_charge && autocvar_crosshair_ring_vortex) // ring around crosshair representing velocity-dependent damage for the vortex { - if (nex_chargepool || use_nex_chargepool) { - use_nex_chargepool = 1; - ring_inner_value = nex_chargepool; + if (vortex_chargepool || use_vortex_chargepool) { + use_vortex_chargepool = 1; + ring_inner_value = vortex_chargepool; } else { - nex_charge_movingavg = (1 - autocvar_crosshair_ring_nex_currentcharge_movingavg_rate) * nex_charge_movingavg + autocvar_crosshair_ring_nex_currentcharge_movingavg_rate * nex_charge; - ring_inner_value = bound(0, autocvar_crosshair_ring_nex_currentcharge_scale * (nex_charge - nex_charge_movingavg), 1); + vortex_charge_movingavg = (1 - autocvar_crosshair_ring_vortex_currentcharge_movingavg_rate) * vortex_charge_movingavg + autocvar_crosshair_ring_vortex_currentcharge_movingavg_rate * vortex_charge; + ring_inner_value = bound(0, autocvar_crosshair_ring_vortex_currentcharge_scale * (vortex_charge - vortex_charge_movingavg), 1); } - ring_inner_alpha = autocvar_crosshair_ring_nex_inner_alpha; - ring_inner_rgb = eX * autocvar_crosshair_ring_nex_inner_color_red + eY * autocvar_crosshair_ring_nex_inner_color_green + eZ * autocvar_crosshair_ring_nex_inner_color_blue; + 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 = nex_charge; - ring_alpha = autocvar_crosshair_ring_nex_alpha; + ring_value = vortex_charge; + ring_alpha = autocvar_crosshair_ring_vortex_alpha; ring_rgb = wcross_color; ring_image = "gfx/crosshair_ring_nexgun.tga"; } @@ -1566,6 +1580,14 @@ void CSQC_UpdateView(float w, float h) else ring_image = "gfx/crosshair_ring.tga"; } + else if ( autocvar_crosshair_ring && autocvar_crosshair_ring_arc && arc_heat && activeweapon == WEP_ARC ) + { + ring_value = arc_heat; + ring_alpha = (1-arc_heat)*autocvar_crosshair_ring_arc_cold_alpha + + arc_heat*autocvar_crosshair_ring_arc_hot_alpha; + ring_rgb = (1-arc_heat)*wcross_color + arc_heat*autocvar_crosshair_ring_arc_hot_color; + ring_image = "gfx/crosshair_ring.tga"; + } // if in weapon switch animation, fade ring out/in if(autocvar_crosshair_effect_time > 0) diff --git a/qcsrc/client/autocvars.qh b/qcsrc/client/autocvars.qh index 55fbebd7f3..b03a67a92f 100644 --- a/qcsrc/client/autocvars.qh +++ b/qcsrc/client/autocvars.qh @@ -57,13 +57,14 @@ float autocvar_cl_nogibs; float autocvar_cl_orthoview; float autocvar_cl_orthoview_nofog; float autocvar_cl_particlegibs; -float autocvar_cl_particles_oldnexbeam; +float autocvar_cl_particles_oldvortexbeam; float autocvar_cl_particles_quality; float autocvar_cl_projectiles_sloppy; float autocvar_cl_readpicture_force; var float autocvar_cl_reticle = 1; -float autocvar_cl_reticle_item_nex; -float autocvar_cl_reticle_item_normal; +var float autocvar_cl_reticle_normal_alpha = 1; +var float autocvar_cl_reticle_weapon = 1; +var float autocvar_cl_reticle_weapon_alpha = 1; float autocvar_cl_reticle_stretch; float autocvar_cl_spawn_event_particles; var float autocvar_cl_spawn_event_sound = 1; @@ -125,14 +126,18 @@ float autocvar_crosshair_ring_minelayer; float autocvar_crosshair_ring_minelayer_alpha; float autocvar_crosshair_ring_hagar; float autocvar_crosshair_ring_hagar_alpha; -float autocvar_crosshair_ring_nex; -float autocvar_crosshair_ring_nex_alpha; -float autocvar_crosshair_ring_nex_currentcharge_movingavg_rate; -float autocvar_crosshair_ring_nex_currentcharge_scale; -float autocvar_crosshair_ring_nex_inner_alpha; -float autocvar_crosshair_ring_nex_inner_color_blue; -float autocvar_crosshair_ring_nex_inner_color_green; -float autocvar_crosshair_ring_nex_inner_color_red; +var float autocvar_crosshair_ring_vortex = 1; +var float autocvar_crosshair_ring_vortex_alpha = 0.15; +var float autocvar_crosshair_ring_vortex_currentcharge_movingavg_rate = 0.05; +var float autocvar_crosshair_ring_vortex_currentcharge_scale = 30; +var float autocvar_crosshair_ring_vortex_inner_alpha = 0.15; +var float autocvar_crosshair_ring_vortex_inner_color_blue = 0; +var float autocvar_crosshair_ring_vortex_inner_color_green = 0; +var float autocvar_crosshair_ring_vortex_inner_color_red = 0.8; +var float autocvar_crosshair_ring_arc = 1; +var vector autocvar_crosshair_ring_arc_hot_color = '1 0 0'; +var float autocvar_crosshair_ring_arc_cold_alpha = 0.2; +var float autocvar_crosshair_ring_arc_hot_alpha = 0.5; float autocvar_crosshair_ring_size; float autocvar_crosshair_ring_reload; float autocvar_crosshair_ring_reload_alpha; @@ -324,6 +329,7 @@ float autocvar_hud_panel_weapons_ammo; float autocvar_hud_panel_weapons_ammo_alpha; string autocvar_hud_panel_weapons_ammo_color; float autocvar_hud_panel_weapons_ammo_full_cells; +float autocvar_hud_panel_weapons_ammo_full_plasma; float autocvar_hud_panel_weapons_ammo_full_fuel; float autocvar_hud_panel_weapons_ammo_full_nails; float autocvar_hud_panel_weapons_ammo_full_rockets; diff --git a/qcsrc/client/csqcmodel_hooks.qc b/qcsrc/client/csqcmodel_hooks.qc index 120b6658df..f29139e46e 100644 --- a/qcsrc/client/csqcmodel_hooks.qc +++ b/qcsrc/client/csqcmodel_hooks.qc @@ -470,8 +470,14 @@ void CSQCModel_Effects_PreUpdate(void) self.effects = self.csqcmodel_effects; self.modelflags = self.csqcmodel_modelflags; } +void Reset_ArcBeam(void); void CSQCModel_Effects_PostUpdate(void) { + if (self == csqcplayer) { + if (self.csqcmodel_teleported) { + Reset_ArcBeam(); + } + } self.csqcmodel_effects = self.effects; self.csqcmodel_modelflags = self.modelflags; self.effects = 0; @@ -655,16 +661,15 @@ void CSQCModel_Hook_PreDraw(float isplayer) self.anim_saveframe1time = self.anim_frame1time; self.anim_saveframe2 = self.anim_frame2; self.anim_saveframe2time = self.anim_frame2time; - if(sf) - { - CSQCModel_InterpolateAnimation_2To4_PreNote(sf | CSQCMODEL_PROPERTY_LERPFRAC); - self.lerpfrac = (doblend ? 0.5 : 0); - self.frame = self.anim_frame; - self.frame1time = self.anim_frame1time; - self.frame2 = self.anim_frame2; - self.frame2time = self.anim_frame2time; - CSQCModel_InterpolateAnimation_2To4_Note(sf | CSQCMODEL_PROPERTY_LERPFRAC, FALSE); - } + // Note: we always consider lerpfrac "changed", as it uses fixed values every time anyway. + // This ensures that .frame etc. are always written. + CSQCModel_InterpolateAnimation_2To4_PreNote(sf | CSQCMODEL_PROPERTY_LERPFRAC); + self.lerpfrac = (doblend ? 0.5 : 0); + self.frame = self.anim_frame; + self.frame1time = self.anim_frame1time; + self.frame2 = self.anim_frame2; + self.frame2time = self.anim_frame2time; + CSQCModel_InterpolateAnimation_2To4_Note(sf | CSQCMODEL_PROPERTY_LERPFRAC, FALSE); CSQCModel_InterpolateAnimation_2To4_Do(); if(doblend) { diff --git a/qcsrc/client/damage.qc b/qcsrc/client/damage.qc index cc3653db4d..64e12620da 100644 --- a/qcsrc/client/damage.qc +++ b/qcsrc/client/damage.qc @@ -80,27 +80,22 @@ void DamageEffect(vector hitorg, float dmg, float type, float specnum) } life = bound(autocvar_cl_damageeffect_lifetime_min, dmg * autocvar_cl_damageeffect_lifetime, autocvar_cl_damageeffect_lifetime_max); - specstr = species_prefix(specnum); - type = DEATH_WEAPONOF(type); - e = get_weaponinfo(type); - effectname = strcat("damage_", e.netname); + effectname = get_weaponinfo(DEATH_WEAPONOF(type)).netname; - // if damage was dealt with a bullet weapon, our effect is blood - // since blood is species dependent, include the species tag - if(type == WEP_SHOTGUN || type == WEP_UZI || type == WEP_RIFLE) + if(substring(effectname, strlen(effectname) - 5, 5) == "BLOOD") { if(self.isplayermodel) { - effectname = strcat(effectname, "_", specstr); - effectname = substring(effectname, 0, strlen(effectname) - 1); // remove the _ symbol at the end of the species tag + specstr = species_prefix(specnum); + specstr = substring(specstr, 0, strlen(specstr) - 1); + effectname = strreplace("BLOOD", specstr, effectname); } - else - return; // objects don't bleed + else { return; } // objects don't bleed } e = spawn(); - setmodel(e, "null"); // necessary to attach and read origin // samual: FIXME: this is weird, is there some better way to do this? + setmodel(e, "null"); // necessary to attach and read origin setattachment(e, self, gettaginfo_name); // attach to the given bone e.classname = "damage"; e.owner = self; @@ -344,19 +339,12 @@ void Ent_DamageInfo(float isNew) w_random = prandom(); traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, world); - if(trace_fraction < 1 && hitwep != WEP_NEX && hitwep != WEP_MINSTANEX) + if(trace_fraction < 1 && hitwep != WEP_VORTEX && hitwep != WEP_VAPORIZER) w_backoff = trace_plane_normal; else w_backoff = -1 * normalize(force); setorigin(self, w_org + w_backoff * 2); // for sound() calls - (get_weaponinfo(hitwep)).weapon_func(WR_IMPACTEFFECT); + if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) { WEP_ACTION(hitwep, WR_IMPACTEFFECT); } } } - -void DamageInfo_Precache() -{ - float i; - for(i = WEP_FIRST; i <= WEP_LAST; ++i) - (get_weaponinfo(i)).weapon_func(WR_PRECACHE); -} diff --git a/qcsrc/client/effects.qc b/qcsrc/client/effects.qc index 8c237aade9..c35a3a94a7 100644 --- a/qcsrc/client/effects.qc +++ b/qcsrc/client/effects.qc @@ -95,3 +95,4 @@ void Net_ReadLightningarc() } } +void Net_ReadArc() { Net_ReadLightningarc(); } diff --git a/qcsrc/client/hook.qc b/qcsrc/client/hook.qc index 889e75d267..8b2ffca19c 100644 --- a/qcsrc/client/hook.qc +++ b/qcsrc/client/hook.qc @@ -75,11 +75,8 @@ void Draw_GrapplingHook() case ENT_CLIENT_HOOK: vs = hook_shotorigin[s]; break; - case ENT_CLIENT_LGBEAM: - vs = electro_shotorigin[s]; - break; - case ENT_CLIENT_GAUNTLET: - vs = gauntlet_shotorigin[s]; + case ENT_CLIENT_ARC_BEAM: + vs = lightning_shotorigin[s]; break; } @@ -92,8 +89,7 @@ void Draw_GrapplingHook() a = view_origin + view_forward * vs_x + view_right * -vs_y + view_up * vs_z; b = self.origin; break; - case ENT_CLIENT_LGBEAM: - case ENT_CLIENT_GAUNTLET: + case ENT_CLIENT_ARC_BEAM: if(self.HookRange) b = view_origin + view_forward * self.HookRange; else @@ -113,8 +109,7 @@ void Draw_GrapplingHook() a = self.velocity; b = self.origin; break; - case ENT_CLIENT_LGBEAM: - case ENT_CLIENT_GAUNTLET: + case ENT_CLIENT_ARC_BEAM: a = self.origin; b = self.velocity; break; @@ -138,18 +133,12 @@ void Draw_GrapplingHook() default: tex = "particles/hook_white"; rgb = getcsqcplayercolor(self.sv_entnum); break; } break; - case ENT_CLIENT_LGBEAM: + case ENT_CLIENT_ARC_BEAM: // todo intensity = bound(0.2, 1 + Noise_Pink(self, frametime) * 1 + Noise_Burst(self, frametime, 0.03) * 0.3, 2); offset = Noise_Brown(self, frametime) * 10; tex = "particles/lgbeam"; rgb = '1 1 1'; break; - case ENT_CLIENT_GAUNTLET: - intensity = 1; - offset = Noise_White(self, frametime); - tex = "particles/gauntletbeam"; - rgb = '1 1 1'; - break; } Draw_GrapplingHook_trace_callback_tex = tex; @@ -176,8 +165,7 @@ void Draw_GrapplingHook() self.drawmask = 0; } break; - case ENT_CLIENT_LGBEAM: - case ENT_CLIENT_GAUNTLET: + case ENT_CLIENT_ARC_BEAM: setorigin(self, a); // beam origin! break; } @@ -187,11 +175,8 @@ void Draw_GrapplingHook() default: case ENT_CLIENT_HOOK: break; - case ENT_CLIENT_LGBEAM: - pointparticles(particleeffectnum("electro_lightning"), trace_endpos, normalize(atrans - trace_endpos), frametime * intensity); - break; - case ENT_CLIENT_GAUNTLET: - pointparticles(particleeffectnum("gauntlet_lightning"), b, normalize(a - b), frametime * intensity); + case ENT_CLIENT_ARC_BEAM: + pointparticles(particleeffectnum("electro_lightning"), trace_endpos, normalize(atrans - trace_endpos), frametime * intensity); // todo: new effect break; } } @@ -222,10 +207,9 @@ void Ent_ReadHook(float bIsNew, float type) { default: case ENT_CLIENT_HOOK: - case ENT_CLIENT_GAUNTLET: self.HookRange = 0; break; - case ENT_CLIENT_LGBEAM: + case ENT_CLIENT_ARC_BEAM: self.HookRange = ReadCoord(); break; } @@ -259,12 +243,9 @@ void Ent_ReadHook(float bIsNew, float type) setmodel(self, "models/hook.md3"); self.drawmask = MASK_NORMAL; break; - case ENT_CLIENT_LGBEAM: + case ENT_CLIENT_ARC_BEAM: sound (self, CH_SHOTS_SINGLE, "weapons/lgbeam_fly.wav", VOL_BASE, ATTEN_NORM); break; - case ENT_CLIENT_GAUNTLET: - sound (self, CH_SHOTS_SINGLE, "weapons/gauntletbeam_fly.wav", VOL_BASE, ATTEN_NORM); - break; } } @@ -274,7 +255,6 @@ void Ent_ReadHook(float bIsNew, float type) void Hook_Precache() { precache_sound("weapons/lgbeam_fly.wav"); - precache_sound("weapons/gauntletbeam_fly.wav"); precache_model("models/hook.md3"); } diff --git a/qcsrc/client/hud.qc b/qcsrc/client/hud.qc index ab61eb3c3e..b928aa512a 100644 --- a/qcsrc/client/hud.qc +++ b/qcsrc/client/hud.qc @@ -218,9 +218,9 @@ string MakeRaceString(float cp, float mytime, float histime, float lapdelta, str if(histime < 0) return strcat(col, cpname); else if(hisname == "") - return strcat(col, sprintf(_("%s (%s)"), cpname, timestr)); + return strcat(col, sprintf("%s (%s)", cpname, timestr)); else - return strcat(col, sprintf(_("%s (%s %s)"), cpname, timestr, strcat(hisname, col, lapstr))); + return strcat(col, sprintf("%s (%s %s)", cpname, timestr, strcat(hisname, col, lapstr))); } // Check if the given name already exist in race rankings? In that case, where? (otherwise return 0) @@ -408,42 +408,6 @@ float weaponorder_cmp(float i, float j, entity pass) return aj - ai; // the string is in REVERSE order (higher prio at the right is what we want, but higher prio first is the string) } -float GetAmmoStat(float i) -{ - switch(i) - { - case 0: return STAT_SHELLS; - case 1: return STAT_NAILS; - case 2: return STAT_ROCKETS; - case 3: return STAT_CELLS; - case 4: return STAT_FUEL; - default: return -1; - } -} - -float GetAmmoTypeForWep(float i) -{ - switch(i) - { - case WEP_SHOTGUN: return 0; - case WEP_UZI: return 1; - case WEP_GRENADE_LAUNCHER: return 2; - case WEP_MINE_LAYER: return 2; - case WEP_ELECTRO: return 3; - case WEP_CRYLINK: return 3; - case WEP_HLAC: return 3; - case WEP_MINSTANEX: return 3; - case WEP_NEX: return 3; - case WEP_RIFLE: return 1; - case WEP_HAGAR: return 2; - case WEP_ROCKET_LAUNCHER: return 2; - case WEP_SEEKER: return 2; - case WEP_FIREBALL: return 4; - case WEP_HOOK: return 3; - default: return -1; - } -} - void HUD_Weapons(void) { // declarations @@ -460,7 +424,7 @@ void HUD_Weapons(void) float timein_effect_length = autocvar_hud_panel_weapons_timeout_speed_in; //? 0.375 : 0); float timeout_effect_length = autocvar_hud_panel_weapons_timeout_speed_out; //? 0.75 : 0); - float ammo_type, ammo_full; + float ammo_full; float barsize_x = 0, barsize_y = 0, baroffset_x = 0, baroffset_y = 0; vector ammo_color = '1 0 1'; float ammo_alpha = 1; @@ -764,7 +728,7 @@ void HUD_Weapons(void) if(weapons_stat & WepSet_FromWeapon(self.weapon)) { // draw the weapon image - drawpic_aspect_skin(weapon_pos, strcat("weapon", self.netname), weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawpic_aspect_skin(weapon_pos, self.model2, weapon_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); // draw weapon label string switch(autocvar_hud_panel_weapons_label) @@ -778,7 +742,7 @@ void HUD_Weapons(void) break; case 3: // weapon name - drawstring(weapon_pos, self.netname, '1 1 0' * 0.5 * weapon_size_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring(weapon_pos, strtolower(self.message), '1 1 0' * 0.5 * weapon_size_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); break; default: // nothing @@ -786,21 +750,20 @@ void HUD_Weapons(void) } // draw ammo status bar - if(autocvar_hud_panel_weapons_ammo && self.weapon != WEP_TUBA && self.weapon != WEP_LASER && self.weapon != WEP_PORTO) + if(autocvar_hud_panel_weapons_ammo && (self.ammo_field != ammo_none)) { - a = 0; - ammo_type = GetAmmoTypeForWep(self.weapon); - if(ammo_type != -1) - a = getstati(GetAmmoStat(ammo_type)); // how much ammo do we have? + a = getstati(GetAmmoStat(self.ammo_field)); // how much ammo do we have? if(a > 0) { - switch(ammo_type) { - case 0: ammo_full = autocvar_hud_panel_weapons_ammo_full_shells; break; - case 1: ammo_full = autocvar_hud_panel_weapons_ammo_full_nails; break; - case 2: ammo_full = autocvar_hud_panel_weapons_ammo_full_rockets; break; - case 3: ammo_full = autocvar_hud_panel_weapons_ammo_full_cells; break; - case 4: ammo_full = autocvar_hud_panel_weapons_ammo_full_fuel; break; + switch(self.ammo_field) + { + case ammo_shells: ammo_full = autocvar_hud_panel_weapons_ammo_full_shells; break; + case ammo_nails: ammo_full = autocvar_hud_panel_weapons_ammo_full_nails; break; + case ammo_rockets: ammo_full = autocvar_hud_panel_weapons_ammo_full_rockets; break; + case ammo_cells: ammo_full = autocvar_hud_panel_weapons_ammo_full_cells; break; + case ammo_plasma: ammo_full = autocvar_hud_panel_weapons_ammo_full_plasma; break; + case ammo_fuel: ammo_full = autocvar_hud_panel_weapons_ammo_full_fuel; break; default: ammo_full = 60; } @@ -808,15 +771,25 @@ void HUD_Weapons(void) weapon_pos_x + baroffset_x, weapon_pos_y + baroffset_y, barsize_x * bound(0, a/ammo_full, 1), - barsize_y); - drawpic_aspect_skin(weapon_pos, "weapon_ammo", weapon_size, ammo_color, ammo_alpha, DRAWFLAG_NORMAL); + barsize_y + ); + + drawpic_aspect_skin( + weapon_pos, + "weapon_ammo", + weapon_size, + ammo_color, + ammo_alpha, + DRAWFLAG_NORMAL + ); + drawresetcliparea(); } } } else // draw a "ghost weapon icon" if you don't have the weapon { - drawpic_aspect_skin(weapon_pos, strcat("weapon", self.netname), weapon_size, '0 0 0', panel_fg_alpha * 0.5, DRAWFLAG_NORMAL); + drawpic_aspect_skin(weapon_pos, self.model2, weapon_size, '0 0 0', panel_fg_alpha * 0.5, DRAWFLAG_NORMAL); } // draw the complain message @@ -858,34 +831,6 @@ void HUD_Weapons(void) } // Ammo (#1) -// -// TODO: macro -float GetAmmoItemCode(float i) -{ - switch(i) - { - case 0: return IT_SHELLS; - case 1: return IT_NAILS; - case 2: return IT_ROCKETS; - case 3: return IT_CELLS; - case 4: return IT_FUEL; - default: return -1; - } -} - -string GetAmmoPicture(float i) -{ - switch(i) - { - case 0: return "ammo_shells"; - case 1: return "ammo_bullets"; - case 2: return "ammo_rockets"; - case 3: return "ammo_cells"; - case 4: return "ammo_fuel"; - default: return ""; - } -} - void DrawNadeScoreBar(vector myPos, vector mySize, vector color) { @@ -934,16 +879,30 @@ void DrawAmmoNades(vector myPos, vector mySize, float draw_expanding, float expa } } -void DrawAmmoItem(vector myPos, vector mySize, float itemcode, float currently_selected, float infinite_ammo) +void DrawAmmoItem(vector myPos, vector mySize, .float ammotype, float currently_selected, float infinite_ammo) { - float a; - if(autocvar__hud_configure) + float a = 0; + if(ammotype != ammo_none) { - currently_selected = (itemcode == 2); //rockets always selected - a = 31 + mod(itemcode*93, 128); + if(autocvar__hud_configure) + { + currently_selected = (ammotype == ammo_rockets); //rockets always selected + a = 60; + } + else + { + // how much ammo do we have of this ammotype? + a = getstati(GetAmmoStat(ammotype)); + } } else - a = getstati(GetAmmoStat(itemcode)); // how much ammo do we have of type itemcode? + { + #if 0 + infinite_ammo = TRUE; + #else + return; // just don't draw infinite ammo at all. + #endif + } vector color; if(infinite_ammo) @@ -971,7 +930,7 @@ void DrawAmmoItem(vector myPos, vector mySize, float itemcode, float currently_s picpos = myPos; } - if (currently_selected) + if(currently_selected) drawpic_aspect_skin(myPos, "ammo_current_bg", mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); if(a > 0 && autocvar_hud_panel_ammo_progressbar) @@ -985,9 +944,9 @@ void DrawAmmoItem(vector myPos, vector mySize, float itemcode, float currently_s drawstring_aspect(numpos, ftos(a), eX * (2/3) * mySize_x + eY * mySize_y, '0 0 0', panel_fg_alpha * theAlpha * 0.5, DRAWFLAG_NORMAL); } if(a > 0 || infinite_ammo) - drawpic_aspect_skin(picpos, GetAmmoPicture(itemcode), '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL); + drawpic_aspect_skin(picpos, GetAmmoPicture(ammotype), '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL); else // "ghost" ammo icon - drawpic_aspect_skin(picpos, GetAmmoPicture(itemcode), '1 1 0' * mySize_y, '0 0 0', panel_fg_alpha * theAlpha * 0.5, DRAWFLAG_NORMAL); + drawpic_aspect_skin(picpos, GetAmmoPicture(ammotype), '1 1 0' * mySize_y, '0 0 0', panel_fg_alpha * theAlpha * 0.5, DRAWFLAG_NORMAL); } float nade_prevstatus; @@ -1023,7 +982,6 @@ void HUD_Ammo(void) float total_ammo_count; vector ammo_size; - float AMMO_COUNT = 4; if (autocvar_hud_panel_ammo_onlycurrent) total_ammo_count = 1; else @@ -1068,31 +1026,24 @@ void HUD_Ammo(void) ammo_size_y = newSize; } - float i, stat_items, currently_selected, infinite_ammo; - infinite_ammo = FALSE; - + float i; + float infinite_ammo = (getstati(STAT_ITEMS, 0, 24) & IT_UNLIMITED_WEAPON_AMMO); row = column = 0; - - if (autocvar_hud_panel_ammo_onlycurrent) + if(autocvar_hud_panel_ammo_onlycurrent) { if(autocvar__hud_configure) { - DrawAmmoItem(pos, ammo_size, 2, true, FALSE); //show rockets + DrawAmmoItem(pos, ammo_size, ammo_rockets, TRUE, FALSE); } else { - stat_items = getstati(STAT_ITEMS, 0, 24); - if (stat_items & IT_UNLIMITED_WEAPON_AMMO) - infinite_ammo = TRUE; - for (i = 0; i < AMMO_COUNT; ++i) { - currently_selected = stat_items & GetAmmoItemCode(i); - if (currently_selected) - { - DrawAmmoItem(pos, ammo_size, i, true, infinite_ammo); - break; - } - } - } + DrawAmmoItem( + pos, + ammo_size, + (get_weaponinfo(switchweapon)).ammo_field, + TRUE, + infinite_ammo + ); ++row; if(row >= rows) @@ -1100,15 +1051,24 @@ void HUD_Ammo(void) row = 0; column = column + 1; } + } } else { - stat_items = getstati(STAT_ITEMS, 0, 24); - if (stat_items & IT_UNLIMITED_WEAPON_AMMO) - infinite_ammo = TRUE; - for (i = 0; i < AMMO_COUNT; ++i) { - currently_selected = stat_items & GetAmmoItemCode(i); - DrawAmmoItem(pos + eX * column * (ammo_size_x + offset_x) + eY * row * (ammo_size_y + offset_y), ammo_size, i, currently_selected, infinite_ammo); + .float ammotype; + row = column = 0; + for(i = 0; i < AMMO_COUNT; ++i) + { + if(i == 4) continue; // fuel + ammotype = GetAmmoFieldFromNum(i); + DrawAmmoItem( + pos + eX * column * (ammo_size_x + offset_x) + eY * row * (ammo_size_y + offset_y), + ammo_size, + ammotype, + ((get_weaponinfo(switchweapon)).ammo_field == ammotype), + infinite_ammo + ); + ++row; if(row >= rows) { @@ -1791,7 +1751,7 @@ void HUD_Notify(void) { attacker = sprintf(_("Player %d"), count + 1); victim = sprintf(_("Player %d"), count + 2); - icon = strcat("weapon", get_weaponinfo(min(WEP_FIRST + count * 2, WEP_LAST)).netname); + icon = get_weaponinfo(min(WEP_FIRST + count * 2, WEP_LAST)).model2; alpha = bound(0, 1.2 - count / entry_count, 1); } else @@ -4266,9 +4226,9 @@ void HUD_CenterPrint (void) { float r; r = random(); - if (r > 0.9) + if (r > 0.75) centerprint_generic(floor(r*1000), strcat(sprintf("^3Countdown message at time %s", seconds_tostring(time)), ", seconds left: ^COUNT"), 1, 10); - else if (r > 0.8) + else if (r > 0.5) centerprint_generic(0, sprintf("^1Multiline message at time %s that\n^1lasts longer than normal", seconds_tostring(time)), 20, 0); else centerprint_hud(sprintf("Message at time %s", seconds_tostring(time))); @@ -4360,7 +4320,8 @@ void HUD_CenterPrint (void) else // Expiring soon, so fade it out. a = (centerprint_expire_time[j] - time) / max(0.0001, autocvar_hud_panel_centerprint_fade_out); - if (a <= 0.5/255.0) // Guaranteed invisible - don't show. + // while counting down show it anyway in order to hold the current message position + if (a <= 0.5/255.0 && centerprint_countdown_num[j] == 0) // Guaranteed invisible - don't show. continue; if (a > 1) a = 1; @@ -4647,7 +4608,7 @@ void HUD_Main (void) } } if (warning) - print(_("Automatically fixed wrong/missing panel numbers in _hud_panelorder\n")); + dprint("Automatically fixed wrong/missing panel numbers in _hud_panelorder\n"); cvar_set("_hud_panelorder", s); if(hud_panelorder_prev) diff --git a/qcsrc/client/main.qh b/qcsrc/client/main.qh index 815c20a33e..c9aa2fb40f 100644 --- a/qcsrc/client/main.qh +++ b/qcsrc/client/main.qh @@ -86,6 +86,7 @@ entity teamslots[17]; // 17 teams (including "spectator team") .float gotscores; .entity owner; .float ready; +.float eliminated; .void(void) draw; .void(void) draw2d; @@ -136,8 +137,8 @@ float calledhooks; .float ping, ping_packetloss, ping_movementloss; -float g_balance_grenadelauncher_bouncefactor; -float g_balance_grenadelauncher_bouncestop; +float g_balance_mortar_bouncefactor; +float g_balance_mortar_bouncestop; float g_balance_electro_secondary_bouncefactor; float g_balance_electro_secondary_bouncestop; float g_trueaim_minrange; diff --git a/qcsrc/client/movetypes.qc b/qcsrc/client/movetypes.qc index b536797ce4..868f05e269 100644 --- a/qcsrc/client/movetypes.qc +++ b/qcsrc/client/movetypes.qc @@ -223,10 +223,10 @@ float _Movetype_UnstickEntity() // SV_UnstickEntity if(!_Movetype_TestEntityPosition('0 0 -1' * i)) goto success; if(!_Movetype_TestEntityPosition('0 0 1' * i)) goto success; } - dprintf(_("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n"), num_for_edict(self), self.classname, vtos(self.move_origin)); + dprintf("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.move_origin)); return FALSE; :success - dprintf(_("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n"), num_for_edict(self), self.classname, vtos(self.move_origin)); + dprintf("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.move_origin)); _Movetype_LinkEdict(TRUE); return TRUE; } diff --git a/qcsrc/client/particles.qc b/qcsrc/client/particles.qc index 8751cc0c55..f16519ab5c 100644 --- a/qcsrc/client/particles.qc +++ b/qcsrc/client/particles.qc @@ -225,7 +225,7 @@ void Ent_RainOrSnow() self.draw = Draw_Snow; } -void Net_ReadNexgunBeamParticle() +void Net_ReadVortexBeamParticle() { vector shotorg, endpos; float charge; @@ -239,7 +239,7 @@ void Net_ReadNexgunBeamParticle() charge = sqrt(charge); // divide evenly among trail spacing and alpha particles_alphamin = particles_alphamax = particles_fade = charge; - if (autocvar_cl_particles_oldnexbeam && (getstati(STAT_ALLOW_OLDNEXBEAM) || isdemo())) + if (autocvar_cl_particles_oldvortexbeam && (getstati(STAT_ALLOW_OLDVORTEXBEAM) || isdemo())) WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum("TE_TEI_G3"), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE); else WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum("nex_beam"), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE); diff --git a/qcsrc/client/progs.src b/qcsrc/client/progs.src index 86a8b43395..90f445c517 100644 --- a/qcsrc/client/progs.src +++ b/qcsrc/client/progs.src @@ -21,8 +21,7 @@ Defs.qc ../common/buffs.qh ../common/test.qh ../common/counting.qh -../common/items.qh -../common/explosion_equation.qh +../common/weapons/weapons.qh // TODO ../common/mapinfo.qh ../common/command/markup.qh ../common/command/rpn.qh @@ -59,11 +58,12 @@ vehicles/vehicles.qh ../csqcmodellib/common.qh ../csqcmodellib/cl_model.qh ../csqcmodellib/cl_player.qh -projectile.qh +weapons/projectile.qh // TODO player_skeleton.qh sortlist.qc miscfunctions.qc +../server/t_items.qh ../server/t_items.qc teamradar.qc @@ -78,7 +78,7 @@ rubble.qc hook.qc particles.qc laser.qc -projectile.qc +weapons/projectile.qc // TODO gibs.qc damage.qc casings.qc @@ -112,9 +112,7 @@ noise.qc ../common/command/rpn.qc ../common/command/generic.qc ../common/mapinfo.qc -../common/items.qc -../server/w_all.qc -../common/explosion_equation.qc +../common/weapons/weapons.qc // TODO ../common/urllib.qc command/cl_cmd.qc diff --git a/qcsrc/client/projectile.qc b/qcsrc/client/projectile.qc deleted file mode 100644 index 8ff5e0f911..0000000000 --- a/qcsrc/client/projectile.qc +++ /dev/null @@ -1,505 +0,0 @@ -.vector iorigin1, iorigin2; -.float spawntime; -.vector trail_oldorigin; -.float trail_oldtime; -.float fade_time, fade_rate; - -void SUB_Stop() -{ - self.move_velocity = self.move_avelocity = '0 0 0'; - self.move_movetype = MOVETYPE_NONE; -} - -.float alphamod; -.float count; // set if clientside projectile -.float cnt; // sound index -.float gravity; -.float snd_looping; -.float silent; - -void Projectile_ResetTrail(vector to) -{ - self.trail_oldorigin = to; - self.trail_oldtime = time; -} - -void Projectile_DrawTrail(vector to) -{ - vector from; - float t0; - - from = self.trail_oldorigin; - t0 = self.trail_oldtime; - self.trail_oldorigin = to; - self.trail_oldtime = time; - - // force the effect even for stationary firemine - if(self.cnt == PROJECTILE_FIREMINE) - if(from == to) - from_z += 1; - - if (self.traileffect) - { - particles_alphamin = particles_alphamax = particles_fade = sqrt(self.alpha); - boxparticles(self.traileffect, self, from, to, self.velocity, self.velocity, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE | PARTICLES_DRAWASTRAIL); - } -} - -void Projectile_Draw() -{ - vector rot; - vector trailorigin; - float f; - float drawn; - float t; - float a; - - f = self.move_flags; - - if(self.count & 0x80) - { - //self.move_flags &= ~FL_ONGROUND; - if(self.move_movetype == MOVETYPE_NONE || self.move_movetype == MOVETYPE_FLY) - Movetype_Physics_NoMatchServer(); - // the trivial movetypes do not have to match the - // server's ticrate as they are ticrate independent - // NOTE: this assumption is only true if MOVETYPE_FLY - // projectiles detonate on impact. If they continue - // moving, we might still be ticrate dependent. - else - Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy); - if(!(self.move_flags & FL_ONGROUND)) - if(self.velocity != '0 0 0') - self.move_angles = self.angles = vectoangles(self.velocity); - } - else - { - InterpolateOrigin_Do(); - } - - if(self.count & 0x80) - { - drawn = (time >= self.spawntime - 0.02); - t = max(time, self.spawntime); - } - else - { - drawn = (self.iflags & IFLAG_VALID); - t = time; - } - - if(!(f & FL_ONGROUND)) - { - rot = '0 0 0'; - switch(self.cnt) - { - /* - case PROJECTILE_GRENADE: - rot = '-2000 0 0'; // forward - break; - */ - case PROJECTILE_GRENADE_BOUNCING: - rot = '0 -1000 0'; // sideways - break; - case PROJECTILE_HOOKBOMB: - rot = '1000 0 0'; // forward - break; - default: - break; - } - - if(Nade_IDFromProjectile(self.cnt) != 0) - rot = self.avelocity; - - self.angles = AnglesTransform_ToAngles(AnglesTransform_Multiply(AnglesTransform_FromAngles(self.angles), rot * (t - self.spawntime))); - } - - vector ang; - ang = self.angles; - ang_x = -ang_x; - makevectors(ang); - - a = 1 - (time - self.fade_time) * self.fade_rate; - self.alpha = bound(0, self.alphamod * a, 1); - if(self.alpha <= 0) - drawn = 0; - self.renderflags = 0; - - trailorigin = self.origin; - switch(self.cnt) - { - case PROJECTILE_GRENADE: - case PROJECTILE_GRENADE_BOUNCING: - trailorigin += v_right * 1 + v_forward * -10; - break; - default: - break; - } - - if(Nade_IDFromProjectile(self.cnt) != 0) - trailorigin += v_up * 4; - - if(drawn) - Projectile_DrawTrail(trailorigin); - else - Projectile_ResetTrail(trailorigin); - - self.drawmask = 0; - - if(!drawn) - return; - - switch(self.cnt) - { - // Possibly add dlights here. - default: - break; - } - - self.drawmask = MASK_NORMAL; -} - -void loopsound(entity e, float ch, string samp, float vol, float attn) -{ - if(self.silent) - return; - - sound(e, ch, samp, vol, attn); - e.snd_looping = ch; -} - -void Ent_RemoveProjectile() -{ - if(self.count & 0x80) - { - tracebox(self.origin, self.mins, self.maxs, self.origin + self.velocity * 0.05, MOVE_NORMAL, self); - Projectile_DrawTrail(trace_endpos); - } -} - -void Ent_Projectile() -{ - float f; - - // projectile properties: - // kind (interpolated, or clientside) - // - // modelindex - // origin - // scale - // if clientside: - // velocity - // gravity - // soundindex (hardcoded list) - // effects - // - // projectiles don't send angles, because they always follow the velocity - - f = ReadByte(); - self.count = (f & 0x80); - self.iflags = (self.iflags & IFLAG_INTERNALMASK) | IFLAG_AUTOANGLES | IFLAG_ANGLES | IFLAG_ORIGIN; - self.solid = SOLID_TRIGGER; - //self.effects = EF_NOMODELFLAGS; - - // this should make collisions with bmodels more exact, but it leads to - // projectiles no longer being able to lie on a bmodel - self.move_nomonsters = MOVE_WORLDONLY; - if(f & 0x40) - self.move_flags |= FL_ONGROUND; - else - self.move_flags &= ~FL_ONGROUND; - - if(!self.move_time) - { - // for some unknown reason, we don't need to care for - // sv_gameplayfix_delayprojectiles here. - self.move_time = time; - self.spawntime = time; - } - else - self.move_time = max(self.move_time, time); - - if(!(self.count & 0x80)) - InterpolateOrigin_Undo(); - - if(f & 1) - { - self.origin_x = ReadCoord(); - self.origin_y = ReadCoord(); - self.origin_z = ReadCoord(); - setorigin(self, self.origin); - if(self.count & 0x80) - { - self.velocity_x = ReadCoord(); - self.velocity_y = ReadCoord(); - self.velocity_z = ReadCoord(); - if(f & 0x10) - self.gravity = ReadCoord(); - else - self.gravity = 0; // none - self.move_origin = self.origin; - self.move_velocity = self.velocity; - } - - if(time == self.spawntime || (self.count & 0x80) || (f & 0x08)) - { - self.trail_oldorigin = self.origin; - if(!(self.count & 0x80)) - InterpolateOrigin_Reset(); - } - - if(f & 0x20) - { - self.fade_time = time + ReadByte() * ticrate; - self.fade_rate = 1 / (ReadByte() * ticrate); - } - else - { - self.fade_time = 0; - self.fade_rate = 0; - } - - self.team = ReadByte() - 1; - } - - if(f & 2) - { - self.cnt = ReadByte(); - - self.silent = (self.cnt & 0x80); - self.cnt = (self.cnt & 0x7F); - - self.scale = 1; - self.traileffect = 0; - switch(self.cnt) - { - case PROJECTILE_ELECTRO: setmodel(self, "models/ebomb.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break; - case PROJECTILE_ROCKET: setmodel(self, "models/rocket.md3");self.traileffect = particleeffectnum("TR_ROCKET"); self.scale = 2; break; - case PROJECTILE_CRYLINK: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break; - case PROJECTILE_CRYLINK_BOUNCING: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break; - case PROJECTILE_ELECTRO_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break; - case PROJECTILE_GRENADE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break; - case PROJECTILE_GRENADE_BOUNCING: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break; - case PROJECTILE_MINE: setmodel(self, "models/mine.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break; - case PROJECTILE_LASER: setmodel(self, "models/laser.mdl");self.traileffect = particleeffectnum(""); break; - case PROJECTILE_HLAC: setmodel(self, "models/hlac_bullet.md3");self.traileffect = particleeffectnum(""); break; - case PROJECTILE_PORTO_RED: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break; - case PROJECTILE_PORTO_BLUE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break; - case PROJECTILE_HOOKBOMB: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_KNIGHTSPIKE"); break; - case PROJECTILE_HAGAR: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break; - case PROJECTILE_HAGAR_BOUNCING: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break; - case PROJECTILE_NAPALM_FOUNTAIN: //self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("torch_small"); break; - case PROJECTILE_FIREBALL: self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("fireball"); break; // particle effect is good enough - case PROJECTILE_FIREMINE: self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("firemine"); break; // particle effect is good enough - case PROJECTILE_TAG: setmodel(self, "models/laser.mdl"); self.traileffect = particleeffectnum("TR_ROCKET"); break; - case PROJECTILE_FLAC: setmodel(self, "models/hagarmissile.mdl"); self.scale = 0.4; self.traileffect = particleeffectnum("TR_SEEKER"); break; - case PROJECTILE_SEEKER: setmodel(self, "models/tagrocket.md3"); self.traileffect = particleeffectnum("TR_SEEKER"); break; - - case PROJECTILE_MAGE_SPIKE: setmodel(self, "models/ebomb.mdl"); self.traileffect = particleeffectnum("TR_VORESPIKE"); break; - case PROJECTILE_SHAMBLER_LIGHTNING: setmodel(self, "models/ebomb.mdl"); self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break; - - case PROJECTILE_RAPTORBOMB: setmodel(self, "models/vehicles/clusterbomb.md3"); self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break; - case PROJECTILE_RAPTORBOMBLET: setmodel(self, "models/vehicles/bomblet.md3"); self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break; - case PROJECTILE_RAPTORCANNON: setmodel(self, "models/plasmatrail.mdl"); self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break; - - case PROJECTILE_SPIDERROCKET: setmodel(self, "models/vehicles/rocket02.md3"); self.traileffect = particleeffectnum("spiderbot_rocket_thrust"); break; - case PROJECTILE_WAKIROCKET: setmodel(self, "models/vehicles/rocket01.md3"); self.traileffect = particleeffectnum("wakizashi_rocket_thrust"); break; - case PROJECTILE_WAKICANNON: setmodel(self, "models/laser.mdl"); self.traileffect = particleeffectnum(""); break; - - case PROJECTILE_BUMBLE_GUN: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break; - case PROJECTILE_BUMBLE_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break; - - default: - if(Nade_IDFromProjectile(self.cnt) != 0) { setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum(Nade_TrailEffect(self.cnt, self.team)); break; } - error("Received invalid CSQC projectile, can't work with this!"); - break; - } - - self.mins = '0 0 0'; - self.maxs = '0 0 0'; - self.colormod = '0 0 0'; - self.move_touch = SUB_Stop; - self.move_movetype = MOVETYPE_TOSS; - self.alphamod = 1; - - switch(self.cnt) - { - case PROJECTILE_ELECTRO: - // only new engines support sound moving with object - loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTEN_NORM); - self.mins = '0 0 -4'; - self.maxs = '0 0 -4'; - self.move_movetype = MOVETYPE_BOUNCE; - self.move_touch = func_null; - self.move_bounce_factor = g_balance_electro_secondary_bouncefactor; - self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop; - break; - case PROJECTILE_ROCKET: - loopsound(self, CH_SHOTS_SINGLE, "weapons/rocket_fly.wav", VOL_BASE, ATTEN_NORM); - self.mins = '-3 -3 -3'; - self.maxs = '3 3 3'; - break; - case PROJECTILE_GRENADE: - self.mins = '-3 -3 -3'; - self.maxs = '3 3 3'; - break; - case PROJECTILE_GRENADE_BOUNCING: - self.mins = '-3 -3 -3'; - self.maxs = '3 3 3'; - self.move_movetype = MOVETYPE_BOUNCE; - self.move_touch = func_null; - self.move_bounce_factor = g_balance_grenadelauncher_bouncefactor; - self.move_bounce_stopspeed = g_balance_grenadelauncher_bouncestop; - break; - case PROJECTILE_SHAMBLER_LIGHTNING: - self.mins = '-8 -8 -8'; - self.maxs = '8 8 8'; - self.scale = 2.5; - self.avelocity = randomvec() * 720; - break; - case PROJECTILE_MINE: - self.mins = '-4 -4 -4'; - self.maxs = '4 4 4'; - break; - case PROJECTILE_PORTO_RED: - self.colormod = '2 1 1'; - self.alphamod = 0.5; - self.move_movetype = MOVETYPE_BOUNCE; - self.move_touch = func_null; - break; - case PROJECTILE_PORTO_BLUE: - self.colormod = '1 1 2'; - self.alphamod = 0.5; - self.move_movetype = MOVETYPE_BOUNCE; - self.move_touch = func_null; - break; - case PROJECTILE_HAGAR_BOUNCING: - self.move_movetype = MOVETYPE_BOUNCE; - self.move_touch = func_null; - break; - case PROJECTILE_CRYLINK_BOUNCING: - self.move_movetype = MOVETYPE_BOUNCE; - self.move_touch = func_null; - break; - case PROJECTILE_NAPALM_FOUNTAIN: - case PROJECTILE_FIREBALL: - loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly2.wav", VOL_BASE, ATTEN_NORM); - self.mins = '-16 -16 -16'; - self.maxs = '16 16 16'; - break; - case PROJECTILE_FIREMINE: - loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly.wav", VOL_BASE, ATTEN_NORM); - self.move_movetype = MOVETYPE_BOUNCE; - self.move_touch = func_null; - self.mins = '-4 -4 -4'; - self.maxs = '4 4 4'; - break; - case PROJECTILE_TAG: - self.mins = '-2 -2 -2'; - self.maxs = '2 2 2'; - break; - case PROJECTILE_FLAC: - self.mins = '-2 -2 -2'; - self.maxs = '2 2 2'; - break; - case PROJECTILE_SEEKER: - loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM); - self.mins = '-4 -4 -4'; - self.maxs = '4 4 4'; - break; - case PROJECTILE_RAPTORBOMB: - self.mins = '-3 -3 -3'; - self.maxs = '3 3 3'; - break; - case PROJECTILE_RAPTORBOMBLET: - break; - case PROJECTILE_RAPTORCANNON: - break; - case PROJECTILE_SPIDERROCKET: - loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM); - break; - case PROJECTILE_WAKIROCKET: - loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM); - break; - /* - case PROJECTILE_WAKICANNON: - break; - case PROJECTILE_BUMBLE_GUN: - // only new engines support sound moving with object - loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTEN_NORM); - self.mins = '0 0 -4'; - self.maxs = '0 0 -4'; - self.move_movetype = MOVETYPE_BOUNCE; - self.move_touch = func_null; - self.move_bounce_factor = g_balance_electro_secondary_bouncefactor; - self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop; - break; - */ - default: - break; - } - - if(Nade_IDFromProjectile(self.cnt) != 0) - { - self.mins = '-16 -16 -16'; - self.maxs = '16 16 16'; - self.colormod = Nade_Color(Nade_IDFromProjectile(self.cnt)); - self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - self.move_movetype = MOVETYPE_BOUNCE; - self.move_touch = func_null; - self.scale = 1.5; - self.avelocity = randomvec() * 720; - - if(Nade_IDFromProjectile(self.cnt) == NADE_TYPE_TRANSLOCATE) - self.solid = SOLID_TRIGGER; - } - - setsize(self, self.mins, self.maxs); - } - - if(self.gravity) - { - if(self.move_movetype == MOVETYPE_FLY) - self.move_movetype = MOVETYPE_TOSS; - if(self.move_movetype == MOVETYPE_BOUNCEMISSILE) - self.move_movetype = MOVETYPE_BOUNCE; - } - else - { - if(self.move_movetype == MOVETYPE_TOSS) - self.move_movetype = MOVETYPE_FLY; - if(self.move_movetype == MOVETYPE_BOUNCE) - self.move_movetype = MOVETYPE_BOUNCEMISSILE; - } - - if(!(self.count & 0x80)) - InterpolateOrigin_Note(); - - self.draw = Projectile_Draw; - self.entremove = Ent_RemoveProjectile; -} - -void Projectile_Precache() -{ - precache_model("models/ebomb.mdl"); - precache_model("models/elaser.mdl"); - precache_model("models/grenademodel.md3"); - precache_model("models/mine.md3"); - precache_model("models/hagarmissile.mdl"); - precache_model("models/hlac_bullet.md3"); - precache_model("models/laser.mdl"); - precache_model("models/plasmatrail.mdl"); - precache_model("models/rocket.md3"); - precache_model("models/tagrocket.md3"); - precache_model("models/tracer.mdl"); - precache_model("models/sphere/sphere.md3"); - - precache_model("models/weapons/v_ok_grenade.md3"); - - precache_sound("weapons/electro_fly.wav"); - precache_sound("weapons/rocket_fly.wav"); - precache_sound("weapons/fireball_fly.wav"); - precache_sound("weapons/fireball_fly2.wav"); - precache_sound("weapons/tag_rocket_fly.wav"); - -} diff --git a/qcsrc/client/projectile.qh b/qcsrc/client/projectile.qh deleted file mode 100644 index 70c8ba0dfc..0000000000 --- a/qcsrc/client/projectile.qh +++ /dev/null @@ -1,3 +0,0 @@ -.float traileffect; -void Projectile_ResetTrail(vector to); -void Projectile_DrawTrail(vector to); diff --git a/qcsrc/client/scoreboard.qc b/qcsrc/client/scoreboard.qc index 6db31ffed1..d1c7fb9f1e 100644 --- a/qcsrc/client/scoreboard.qc +++ b/qcsrc/client/scoreboard.qc @@ -287,19 +287,15 @@ void Cmd_HUD_Help() "other gamemodes except DM.\n")); } -string HUD_DefaultColumnLayout() -{ - return strcat( // fteqcc sucks - "ping pl name | ", - "-teams,race,lms/kills +freezetag/kills -teams,lms/deaths +freezetag/deaths -teams,lms,race,ka/suicides +freezetag/suicides -race,dm,tdm,ka,freezetag/frags ", // tdm already has this in "score" - "+tdm/kills +tdm/deaths +tdm/suicides ", - "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns ", - "+lms/lives +lms/rank ", - "+kh/caps +kh/pushes +kh/destroyed ", - "?+race/laps ?+race/time ?+race/fastest ", - "+as/objectives +nexball/faults +nexball/goals +ka/pickups +ka/bckills +ka/bctime +freezetag/revivals ", - "-lms,race,nexball/score"); -} +#define HUD_DefaultColumnLayout() \ +"ping pl name | " \ +"-teams,race,lms/kills +ft,tdm/kills -teams,lms/deaths +ft,tdm/deaths -teams,lms,race,ka/suicides +ft,tdm/suicides -race,dm,tdm,ka,ft/frags " /* tdm already has this in "score" */ \ +"+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns " \ +"+lms/lives +lms/rank " \ +"+kh/caps +kh/pushes +kh/destroyed " \ +"?+race/laps ?+race/time ?+race/fastest " \ +"+as/objectives +nb/faults +nb/goals +ka/pickups +ka/bckills +ka/bctime +ft/revivals " \ +"-lms,race,nb/score" void Cmd_HUD_SetFields(float argc) { @@ -308,6 +304,16 @@ void Cmd_HUD_SetFields(float argc) float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0; float missing; + if(!gametype) + { + // set up a temporary scoreboard layout + // no layout can be properly set up until score_info data haven't been received + argc = tokenizebyseparator("0 1 ping pl name | score", " "); + ps_primary = 0; + scores_label[ps_primary] = strzone("score"); + scores_flags[ps_primary] = SFL_ALLOW_HIDE; + } + // TODO: re enable with gametype dependant cvars? if(argc < 3) // no arguments provided argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " "); @@ -670,7 +676,7 @@ string HUD_FixScoreboardColumnWidth(float i, string str) return str; } -void HUD_PrintScoreboardItem(vector pos, entity pl, float is_self, float pl_number) +void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, float pl_number) { vector tmp, rgb; rgb = Team_ColorRGB(pl.team); @@ -684,18 +690,17 @@ void HUD_PrintScoreboardItem(vector pos, entity pl, float is_self, float pl_numb rgb_y = autocvar_scoreboard_color_bg_g + 0.5; rgb_z = autocvar_scoreboard_color_bg_b + 0.5; } - // Layout: - tmp_x = sbwidth; - tmp_y = hud_fontsize_y * 1.25; - tmp_z = 0; - + vector h_pos = pos - '1 1 0'; + vector h_size = item_size + '2 0 0'; // alternated rows highlighting if(is_self) - drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL); + drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL); else if((scoreboard_highlight) && (!mod(pl_number,2))) - drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL); + drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL); + tmp_x = item_size_x; tmp_y = 0; + tmp_z = 0; for(i = 0; i < hud_num_fields; ++i) { @@ -794,6 +799,9 @@ void HUD_PrintScoreboardItem(vector pos, entity pl, float is_self, float pl_numb pos_x -= hud_size[i] + hud_fontsize_x; } } + + if(pl.eliminated) + drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL); } /* @@ -907,6 +915,10 @@ vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_siz pos_y += 1.25 * hud_fontsize_y; // skip the header pos_y += autocvar_scoreboard_border_thickness; + // item size + tmp_x = sbwidth; + tmp_y = hud_fontsize_y * 1.25; + // fill the table and draw the rows i = 0; if (teamplay) @@ -914,7 +926,7 @@ vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_siz { if(pl.team != tm.team) continue; - HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), i); + HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i); pos_y += 1.25 * hud_fontsize_y; ++i; } @@ -923,7 +935,7 @@ vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_siz { if(pl.team == NUM_SPECTATOR) continue; - HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), i); + HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i); pos_y += 1.25 * hud_fontsize_y; ++i; } @@ -955,7 +967,7 @@ float average_accuracy; vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size) { float i; - float weapon_cnt = WEP_COUNT - 3; // either minstanex/nex are hidden, no port-o-launch, no tuba + float weapon_cnt = WEP_COUNT - 3; // either vaporizer/vortex are hidden, no port-o-launch, no tuba float rows; if(autocvar_scoreboard_accuracy_doublerows) rows = 2; @@ -998,7 +1010,7 @@ vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size) if(rows == 2) pos_x += weapon_width / 2; - if(switchweapon == WEP_MINSTANEX) + if(switchweapon == WEP_VAPORIZER) g_instagib = 1; // TODO: real detection for instagib? float weapon_stats; @@ -1012,7 +1024,7 @@ vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size) self = get_weaponinfo(i); if (!self.weapon) continue; - if ((i == WEP_NEX && g_instagib) || i == WEP_PORTO || (i == WEP_MINSTANEX && !g_instagib) || i == WEP_TUBA) // skip port-o-launch, vortex || vaporizer and tuba + if ((i == WEP_VORTEX && g_instagib) || i == WEP_PORTO || (i == WEP_VAPORIZER && !g_instagib) || i == WEP_TUBA) // skip port-o-launch, vortex || vaporizer and tuba continue; weapon_stats = weapon_accuracy[i-WEP_FIRST]; @@ -1023,14 +1035,14 @@ vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size) weapon_alpha = 0.2 * scoreboard_alpha_fg; // weapon icon - drawpic_aspect_skin(pos, strcat("weapon", self.netname), '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL); + drawpic_aspect_skin(pos, self.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL); // the accuracy if(weapon_stats >= 0) { weapons_with_stats += 1; average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy string s; - s = sprintf(_("%d%%"), weapon_stats*100); + s = sprintf("%d%%", weapon_stats*100); float padding; padding = (weapon_width - stringwidth(s, FALSE, '1 0 0' * fontsize)) / 2; // center the accuracy value @@ -1330,12 +1342,16 @@ void HUD_DrawScoreboard() float specs; specs = 0; tmp = pos; + vector item_size; + item_size_x = sbwidth; + item_size_y = hud_fontsize_y * 1.25; + item_size_z = 0; for(pl = players.sort_next; pl; pl = pl.sort_next) { if(pl.team != NUM_SPECTATOR) continue; pos_y += 1.25 * hud_fontsize_y; - HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), specs); + HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs); ++specs; } diff --git a/qcsrc/client/shownames.qc b/qcsrc/client/shownames.qc index 5deba45c41..3c89f312b0 100644 --- a/qcsrc/client/shownames.qc +++ b/qcsrc/client/shownames.qc @@ -22,8 +22,6 @@ void Draw_ShowNames(entity ent) if(!(autocvar_hud_shownames_self && autocvar_chase_active)) return; - makevectors(view_angles); - if(ent.sameteam || (!ent.sameteam && autocvar_hud_shownames_enemies)) { ent.origin_z += autocvar_hud_shownames_offset; diff --git a/qcsrc/client/target_music.qc b/qcsrc/client/target_music.qc index fa116dc731..1d90a664c3 100644 --- a/qcsrc/client/target_music.qc +++ b/qcsrc/client/target_music.qc @@ -91,7 +91,7 @@ void Net_TargetMusic() sound(e, CH_BGM_SINGLE, e.noise, 0, ATTEN_NONE); if(getsoundtime(e, CH_BGM_SINGLE) < 0) { - printf(_("Cannot initialize sound %s\n"), e.noise); + dprintf("Cannot initialize sound %s\n", e.noise); strunzone(e.noise); e.noise = string_null; } @@ -180,7 +180,7 @@ void Ent_ReadTriggerMusic() sound(self, CH_BGM_SINGLE, self.noise, 0, ATTEN_NONE); if(getsoundtime(self, CH_BGM_SINGLE) < 0) { - printf(_("Cannot initialize sound %s\n"), self.noise); + dprintf("Cannot initialize sound %s\n", self.noise); strunzone(self.noise); self.noise = string_null; } diff --git a/qcsrc/client/waypointsprites.qc b/qcsrc/client/waypointsprites.qc index 7ee34672c5..c99fc5d51c 100644 --- a/qcsrc/client/waypointsprites.qc +++ b/qcsrc/client/waypointsprites.qc @@ -217,31 +217,20 @@ float spritelookupblinkvalue(string s) } vector spritelookupcolor(string s, vector def) { + if(substring(s, 0, 4) == "wpn-") + return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).wpcolor); + switch(s) { case "keycarrier-friend": return '0 1 0'; - case "wpn-laser": return '1 0.5 0.5'; - case "wpn-shotgun": return '0.5 0.25 0'; - case "wpn-uzi": return '1 1 0'; - case "wpn-gl": return '1 0 0'; - case "wpn-electro": return '0 0.5 1'; - case "wpn-crylink": return '1 0.5 1'; - case "wpn-nex": return '0.5 1 1'; - case "wpn-hagar": return '1 1 0.5'; - case "wpn-rl": return '1 1 0'; - case "wpn-porto": return '0.5 0.5 0.5'; - case "wpn-minstanex": return '0.5 1 1'; - case "wpn-hookgun": return '0 0.5 0'; - case "wpn-fireball": return '1 0.5 0'; - case "wpn-hlac": return '0 1 0'; - case "wpn-campingrifle": return '0.5 1 0'; - case "wpn-minelayer": return '0.75 1 0'; default: return def; } } string spritelookuptext(string s) { + if(substring(s, 0, 4) == "wpn-") { return (get_weaponinfo(stof(substring(s, 4, strlen(s)))).message); } if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); } + switch(s) { case "as-push": return _("Push"); @@ -282,22 +271,6 @@ string spritelookuptext(string s) case "nb-ball": return _("Ball"); case "ka-ball": return _("Ball"); case "ka-ballcarrier": return _("Ball carrier"); - case "wpn-laser": return _("Laser"); - case "wpn-shotgun": return _("Shotgun"); - case "wpn-uzi": return _("Machine Gun"); - case "wpn-gl": return _("Mortar"); - case "wpn-electro": return _("Electro"); - case "wpn-crylink": return _("Crylink"); - case "wpn-nex": return _("Nex"); - case "wpn-hagar": return _("Hagar"); - case "wpn-rl": return _("Rocket Launcher"); - case "wpn-porto": return _("Port-O-Launch"); - case "wpn-minstanex": return _("Minstanex"); - case "wpn-hookgun": return _("Hook"); - case "wpn-fireball": return _("Fireball"); - case "wpn-hlac": return _("HLAC"); - case "wpn-campingrifle": return _("Rifle"); - case "wpn-minelayer": return _("Mine Layer"); case "dom-neut": return _("Control point"); case "dom-red": return _("Control point"); case "dom-blue": return _("Control point"); diff --git a/qcsrc/client/weapons/projectile.qc b/qcsrc/client/weapons/projectile.qc new file mode 100644 index 0000000000..0d2bce9439 --- /dev/null +++ b/qcsrc/client/weapons/projectile.qc @@ -0,0 +1,511 @@ +.vector iorigin1, iorigin2; +.float spawntime; +.vector trail_oldorigin; +.float trail_oldtime; +.float fade_time, fade_rate; + +void SUB_Stop() +{ + self.move_velocity = self.move_avelocity = '0 0 0'; + self.move_movetype = MOVETYPE_NONE; +} + +.float alphamod; +.float count; // set if clientside projectile +.float cnt; // sound index +.float gravity; +.float snd_looping; +.float silent; + +void Projectile_ResetTrail(vector to) +{ + self.trail_oldorigin = to; + self.trail_oldtime = time; +} + +void Projectile_DrawTrail(vector to) +{ + vector from; + float t0; + + from = self.trail_oldorigin; + t0 = self.trail_oldtime; + self.trail_oldorigin = to; + self.trail_oldtime = time; + + // force the effect even for stationary firemine + if(self.cnt == PROJECTILE_FIREMINE) + if(from == to) + from_z += 1; + + if (self.traileffect) + { + particles_alphamin = particles_alphamax = particles_fade = sqrt(self.alpha); + boxparticles(self.traileffect, self, from, to, self.velocity, self.velocity, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE | PARTICLES_DRAWASTRAIL); + } +} + +.float proj_time; +void Projectile_Draw() +{ + vector rot; + vector trailorigin; + float f; + float drawn; + float t; + float a; + float dt = time - self.proj_time; + + self.proj_time = time; + if(dt <= 0) { return; } + + f = self.move_flags; + + if(self.count & 0x80) + { + //self.move_flags &= ~FL_ONGROUND; + if(self.move_movetype == MOVETYPE_NONE || self.move_movetype == MOVETYPE_FLY) + Movetype_Physics_NoMatchServer(); + // the trivial movetypes do not have to match the + // server's ticrate as they are ticrate independent + // NOTE: this assumption is only true if MOVETYPE_FLY + // projectiles detonate on impact. If they continue + // moving, we might still be ticrate dependent. + else + Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy); + if(!(self.move_flags & FL_ONGROUND)) + if(self.velocity != '0 0 0') + self.move_angles = self.angles = vectoangles(self.velocity); + } + else + { + InterpolateOrigin_Do(); + } + + if(self.count & 0x80) + { + drawn = (time >= self.spawntime - 0.02); + t = max(time, self.spawntime); + } + else + { + drawn = (self.iflags & IFLAG_VALID); + t = time; + } + + if(!(f & FL_ONGROUND)) + { + rot = '0 0 0'; + switch(self.cnt) + { + /* + case PROJECTILE_GRENADE: + rot = '-2000 0 0'; // forward + break; + */ + case PROJECTILE_GRENADE_BOUNCING: + rot = '0 -1000 0'; // sideways + break; + case PROJECTILE_HOOKBOMB: + rot = '1000 0 0'; // forward + break; + default: + break; + } + + if(Nade_IDFromProjectile(self.cnt) != 0) + rot = self.avelocity; + + self.angles = AnglesTransform_ToAngles(AnglesTransform_Multiply(AnglesTransform_FromAngles(self.angles), rot * (t - self.spawntime))); + } + + vector ang; + ang = self.angles; + ang_x = -ang_x; + makevectors(ang); + + a = 1 - (time - self.fade_time) * self.fade_rate; + self.alpha = bound(0, self.alphamod * a, 1); + if(self.alpha <= 0) + drawn = 0; + self.renderflags = 0; + + trailorigin = self.origin; + switch(self.cnt) + { + case PROJECTILE_GRENADE: + case PROJECTILE_GRENADE_BOUNCING: + trailorigin += v_right * 1 + v_forward * -10; + break; + default: + break; + } + + if(Nade_IDFromProjectile(self.cnt) != 0) + trailorigin += v_up * 4; + + if(drawn) + Projectile_DrawTrail(trailorigin); + else + Projectile_ResetTrail(trailorigin); + + self.drawmask = 0; + + if(!drawn) + return; + + switch(self.cnt) + { + // Possibly add dlights here. + default: + break; + } + + self.drawmask = MASK_NORMAL; +} + +void loopsound(entity e, float ch, string samp, float vol, float attn) +{ + if(self.silent) + return; + + sound(e, ch, samp, vol, attn); + e.snd_looping = ch; +} + +void Ent_RemoveProjectile() +{ + if(self.count & 0x80) + { + tracebox(self.origin, self.mins, self.maxs, self.origin + self.velocity * 0.05, MOVE_NORMAL, self); + Projectile_DrawTrail(trace_endpos); + } +} + +void Ent_Projectile() +{ + float f; + + // projectile properties: + // kind (interpolated, or clientside) + // + // modelindex + // origin + // scale + // if clientside: + // velocity + // gravity + // soundindex (hardcoded list) + // effects + // + // projectiles don't send angles, because they always follow the velocity + + f = ReadByte(); + self.count = (f & 0x80); + self.iflags = (self.iflags & IFLAG_INTERNALMASK) | IFLAG_AUTOANGLES | IFLAG_ANGLES | IFLAG_ORIGIN; + self.solid = SOLID_TRIGGER; + self.proj_time = time; + //self.effects = EF_NOMODELFLAGS; + + // this should make collisions with bmodels more exact, but it leads to + // projectiles no longer being able to lie on a bmodel + self.move_nomonsters = MOVE_WORLDONLY; + if(f & 0x40) + self.move_flags |= FL_ONGROUND; + else + self.move_flags &= ~FL_ONGROUND; + + if(!self.move_time) + { + // for some unknown reason, we don't need to care for + // sv_gameplayfix_delayprojectiles here. + self.move_time = time; + self.spawntime = time; + } + else + self.move_time = max(self.move_time, time); + + if(!(self.count & 0x80)) + InterpolateOrigin_Undo(); + + if(f & 1) + { + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + setorigin(self, self.origin); + if(self.count & 0x80) + { + self.velocity_x = ReadCoord(); + self.velocity_y = ReadCoord(); + self.velocity_z = ReadCoord(); + if(f & 0x10) + self.gravity = ReadCoord(); + else + self.gravity = 0; // none + self.move_origin = self.origin; + self.move_velocity = self.velocity; + } + + if(time == self.spawntime || (self.count & 0x80) || (f & 0x08)) + { + self.trail_oldorigin = self.origin; + if(!(self.count & 0x80)) + InterpolateOrigin_Reset(); + } + + if(f & 0x20) + { + self.fade_time = time + ReadByte() * ticrate; + self.fade_rate = 1 / (ReadByte() * ticrate); + } + else + { + self.fade_time = 0; + self.fade_rate = 0; + } + + self.team = ReadByte() - 1; + } + + if(f & 2) + { + self.cnt = ReadByte(); + + self.silent = (self.cnt & 0x80); + self.cnt = (self.cnt & 0x7F); + + self.scale = 1; + self.traileffect = 0; + switch(self.cnt) + { + case PROJECTILE_ELECTRO: setmodel(self, "models/ebomb.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break; + case PROJECTILE_ROCKET: setmodel(self, "models/rocket.md3");self.traileffect = particleeffectnum("TR_ROCKET"); self.scale = 2; break; + case PROJECTILE_CRYLINK: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break; + case PROJECTILE_CRYLINK_BOUNCING: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break; + case PROJECTILE_ELECTRO_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break; + case PROJECTILE_GRENADE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break; + case PROJECTILE_GRENADE_BOUNCING: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break; + case PROJECTILE_MINE: setmodel(self, "models/mine.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break; + case PROJECTILE_BLASTER: setmodel(self, "models/laser.mdl");self.traileffect = particleeffectnum(""); break; + case PROJECTILE_HLAC: setmodel(self, "models/hlac_bullet.md3");self.traileffect = particleeffectnum(""); break; + case PROJECTILE_PORTO_RED: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break; + case PROJECTILE_PORTO_BLUE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break; + case PROJECTILE_HOOKBOMB: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_KNIGHTSPIKE"); break; + case PROJECTILE_HAGAR: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break; + case PROJECTILE_HAGAR_BOUNCING: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break; + case PROJECTILE_NAPALM_FOUNTAIN: //self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("torch_small"); break; + case PROJECTILE_FIREBALL: self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("fireball"); break; // particle effect is good enough + case PROJECTILE_FIREMINE: self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("firemine"); break; // particle effect is good enough + case PROJECTILE_TAG: setmodel(self, "models/laser.mdl"); self.traileffect = particleeffectnum("TR_ROCKET"); break; + case PROJECTILE_FLAC: setmodel(self, "models/hagarmissile.mdl"); self.scale = 0.4; self.traileffect = particleeffectnum("TR_SEEKER"); break; + case PROJECTILE_SEEKER: setmodel(self, "models/tagrocket.md3"); self.traileffect = particleeffectnum("TR_SEEKER"); break; + + case PROJECTILE_MAGE_SPIKE: setmodel(self, "models/ebomb.mdl"); self.traileffect = particleeffectnum("TR_VORESPIKE"); break; + case PROJECTILE_SHAMBLER_LIGHTNING: setmodel(self, "models/ebomb.mdl"); self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break; + + case PROJECTILE_RAPTORBOMB: setmodel(self, "models/vehicles/clusterbomb.md3"); self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break; + case PROJECTILE_RAPTORBOMBLET: setmodel(self, "models/vehicles/bomblet.md3"); self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break; + case PROJECTILE_RAPTORCANNON: setmodel(self, "models/plasmatrail.mdl"); self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break; + + case PROJECTILE_SPIDERROCKET: setmodel(self, "models/vehicles/rocket02.md3"); self.traileffect = particleeffectnum("spiderbot_rocket_thrust"); break; + case PROJECTILE_WAKIROCKET: setmodel(self, "models/vehicles/rocket01.md3"); self.traileffect = particleeffectnum("wakizashi_rocket_thrust"); break; + case PROJECTILE_WAKICANNON: setmodel(self, "models/laser.mdl"); self.traileffect = particleeffectnum(""); break; + + case PROJECTILE_BUMBLE_GUN: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break; + case PROJECTILE_BUMBLE_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break; + + default: + if(Nade_IDFromProjectile(self.cnt) != 0) { setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum(Nade_TrailEffect(self.cnt, self.team)); break; } + error("Received invalid CSQC projectile, can't work with this!"); + break; + } + + self.mins = '0 0 0'; + self.maxs = '0 0 0'; + self.colormod = '0 0 0'; + self.move_touch = SUB_Stop; + self.move_movetype = MOVETYPE_TOSS; + self.alphamod = 1; + + switch(self.cnt) + { + case PROJECTILE_ELECTRO: + // only new engines support sound moving with object + loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTEN_NORM); + self.mins = '0 0 -4'; + self.maxs = '0 0 -4'; + self.move_movetype = MOVETYPE_BOUNCE; + self.move_touch = func_null; + self.move_bounce_factor = g_balance_electro_secondary_bouncefactor; + self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop; + break; + case PROJECTILE_ROCKET: + loopsound(self, CH_SHOTS_SINGLE, "weapons/rocket_fly.wav", VOL_BASE, ATTEN_NORM); + self.mins = '-3 -3 -3'; + self.maxs = '3 3 3'; + break; + case PROJECTILE_GRENADE: + self.mins = '-3 -3 -3'; + self.maxs = '3 3 3'; + break; + case PROJECTILE_GRENADE_BOUNCING: + self.mins = '-3 -3 -3'; + self.maxs = '3 3 3'; + self.move_movetype = MOVETYPE_BOUNCE; + self.move_touch = func_null; + self.move_bounce_factor = g_balance_mortar_bouncefactor; + self.move_bounce_stopspeed = g_balance_mortar_bouncestop; + break; + case PROJECTILE_SHAMBLER_LIGHTNING: + self.mins = '-8 -8 -8'; + self.maxs = '8 8 8'; + self.scale = 2.5; + self.avelocity = randomvec() * 720; + break; + case PROJECTILE_MINE: + self.mins = '-4 -4 -4'; + self.maxs = '4 4 4'; + break; + case PROJECTILE_PORTO_RED: + self.colormod = '2 1 1'; + self.alphamod = 0.5; + self.move_movetype = MOVETYPE_BOUNCE; + self.move_touch = func_null; + break; + case PROJECTILE_PORTO_BLUE: + self.colormod = '1 1 2'; + self.alphamod = 0.5; + self.move_movetype = MOVETYPE_BOUNCE; + self.move_touch = func_null; + break; + case PROJECTILE_HAGAR_BOUNCING: + self.move_movetype = MOVETYPE_BOUNCE; + self.move_touch = func_null; + break; + case PROJECTILE_CRYLINK_BOUNCING: + self.move_movetype = MOVETYPE_BOUNCE; + self.move_touch = func_null; + break; + case PROJECTILE_NAPALM_FOUNTAIN: + case PROJECTILE_FIREBALL: + loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly2.wav", VOL_BASE, ATTEN_NORM); + self.mins = '-16 -16 -16'; + self.maxs = '16 16 16'; + break; + case PROJECTILE_FIREMINE: + loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly.wav", VOL_BASE, ATTEN_NORM); + self.move_movetype = MOVETYPE_BOUNCE; + self.move_touch = func_null; + self.mins = '-4 -4 -4'; + self.maxs = '4 4 4'; + break; + case PROJECTILE_TAG: + self.mins = '-2 -2 -2'; + self.maxs = '2 2 2'; + break; + case PROJECTILE_FLAC: + self.mins = '-2 -2 -2'; + self.maxs = '2 2 2'; + break; + case PROJECTILE_SEEKER: + loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM); + self.mins = '-4 -4 -4'; + self.maxs = '4 4 4'; + break; + case PROJECTILE_RAPTORBOMB: + self.mins = '-3 -3 -3'; + self.maxs = '3 3 3'; + break; + case PROJECTILE_RAPTORBOMBLET: + break; + case PROJECTILE_RAPTORCANNON: + break; + case PROJECTILE_SPIDERROCKET: + loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM); + break; + case PROJECTILE_WAKIROCKET: + loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM); + break; + /* + case PROJECTILE_WAKICANNON: + break; + case PROJECTILE_BUMBLE_GUN: + // only new engines support sound moving with object + loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTEN_NORM); + self.mins = '0 0 -4'; + self.maxs = '0 0 -4'; + self.move_movetype = MOVETYPE_BOUNCE; + self.move_touch = func_null; + self.move_bounce_factor = g_balance_electro_secondary_bouncefactor; + self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop; + break; + */ + default: + break; + } + + if(Nade_IDFromProjectile(self.cnt) != 0) + { + self.mins = '-16 -16 -16'; + self.maxs = '16 16 16'; + self.colormod = Nade_Color(Nade_IDFromProjectile(self.cnt)); + self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + self.move_movetype = MOVETYPE_BOUNCE; + self.move_touch = func_null; + self.scale = 1.5; + self.avelocity = randomvec() * 720; + + if(Nade_IDFromProjectile(self.cnt) == NADE_TYPE_TRANSLOCATE) + self.solid = SOLID_TRIGGER; + } + + setsize(self, self.mins, self.maxs); + } + + if(self.gravity) + { + if(self.move_movetype == MOVETYPE_FLY) + self.move_movetype = MOVETYPE_TOSS; + if(self.move_movetype == MOVETYPE_BOUNCEMISSILE) + self.move_movetype = MOVETYPE_BOUNCE; + } + else + { + if(self.move_movetype == MOVETYPE_TOSS) + self.move_movetype = MOVETYPE_FLY; + if(self.move_movetype == MOVETYPE_BOUNCE) + self.move_movetype = MOVETYPE_BOUNCEMISSILE; + } + + if(!(self.count & 0x80)) + InterpolateOrigin_Note(); + + self.draw = Projectile_Draw; + self.entremove = Ent_RemoveProjectile; +} + +void Projectile_Precache() +{ + precache_model("models/ebomb.mdl"); + precache_model("models/elaser.mdl"); + precache_model("models/grenademodel.md3"); + precache_model("models/mine.md3"); + precache_model("models/hagarmissile.mdl"); + precache_model("models/hlac_bullet.md3"); + precache_model("models/laser.mdl"); + precache_model("models/plasmatrail.mdl"); + precache_model("models/rocket.md3"); + precache_model("models/tagrocket.md3"); + precache_model("models/tracer.mdl"); + precache_model("models/sphere/sphere.md3"); + + precache_model("models/weapons/v_ok_grenade.md3"); + + precache_sound("weapons/electro_fly.wav"); + precache_sound("weapons/rocket_fly.wav"); + precache_sound("weapons/fireball_fly.wav"); + precache_sound("weapons/fireball_fly2.wav"); + precache_sound("weapons/tag_rocket_fly.wav"); + +} diff --git a/qcsrc/client/weapons/projectile.qh b/qcsrc/client/weapons/projectile.qh new file mode 100644 index 0000000000..70c8ba0dfc --- /dev/null +++ b/qcsrc/client/weapons/projectile.qh @@ -0,0 +1,3 @@ +.float traileffect; +void Projectile_ResetTrail(vector to); +void Projectile_DrawTrail(vector to); diff --git a/qcsrc/common/command/generic.qc b/qcsrc/common/command/generic.qc index 08b0b69ddf..0710726de1 100644 --- a/qcsrc/common/command/generic.qc +++ b/qcsrc/common/command/generic.qc @@ -28,7 +28,7 @@ void Curl_URI_Get_Callback(float id, float status, string data) do_cvar = curl_uri_get_cvar[i]; if(status != 0) { - printf(_("error: status is %d\n"), status); + dprintf("error: status is %d\n", status); if(do_cvar) strunzone(do_cvar); return; @@ -239,12 +239,12 @@ void GenericCommand_dumpnotifs(float request) if(filename == "") { - filename = "notifications.cfg"; + filename = "notifications_dump.cfg"; alsoprint = FALSE; } else if(filename == "-") { - filename = "notifications.cfg"; + filename = "notifications_dump.cfg"; alsoprint = TRUE; } fh = fopen(filename, FILE_WRITE); @@ -269,7 +269,60 @@ void GenericCommand_dumpnotifs(float request) case CMD_REQUEST_USAGE: { print(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " dumpnotifs [filename]")); - print(" Where 'filename' is the file to write (default is notifications.cfg),\n"); + print(" Where 'filename' is the file to write (default is notifications_dump.cfg),\n"); + print(" if supplied with '-' output to console as well as default,\n"); + print(" if left blank, it will only write to default.\n"); + return; + } + } +} + +void GenericCommand_dumpweapons(float request) // WEAPONTODO: make this work with other progs than just server +{ + switch(request) + { + case CMD_REQUEST_COMMAND: + { + #ifdef SVQC + wep_config_file = -1; + wep_config_alsoprint = -1; + string filename = argv(1); + + if(filename == "") + { + filename = "weapons_dump.cfg"; + wep_config_alsoprint = FALSE; + } + else if(filename == "-") + { + filename = "weapons_dump.cfg"; + wep_config_alsoprint = TRUE; + } + wep_config_file = fopen(filename, FILE_WRITE); + + if(wep_config_file >= 0) + { + Dump_Weapon_Settings(); + print(sprintf("Dumping weapons... File located in ^2data/data/%s^7.\n", filename)); + fclose(wep_config_file); + wep_config_file = -1; + wep_config_alsoprint = -1; + } + else + { + print(sprintf("^1Error: ^7Could not open file '%s'!\n", filename)); + } + #else + print(_("Weapons dump command only works with sv_cmd.\n")); + #endif + return; + } + + default: + case CMD_REQUEST_USAGE: + { + print(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " dumpweapons [filename]")); + print(" Where 'filename' is the file to write (default is weapons_dump.cfg),\n"); print(" if supplied with '-' output to console as well as default,\n"); print(" if left blank, it will only write to default.\n"); return; @@ -585,6 +638,7 @@ void GenericCommand_(float request) GENERIC_COMMAND("addtolist", GenericCommand_addtolist(request, arguments), "Add a string to a cvar") \ GENERIC_COMMAND("dumpcommands", GenericCommand_dumpcommands(request), "Dump all commands on the program to *_cmd_dump.txt") \ GENERIC_COMMAND("dumpnotifs", GenericCommand_dumpnotifs(request), "Dump all notifications into notifications_dump.txt") \ + GENERIC_COMMAND("dumpweapons", GenericCommand_dumpweapons(request), "Dump all weapons into weapons_dump.txt") \ GENERIC_COMMAND("maplist", GenericCommand_maplist(request, arguments), "Automatic control of maplist") \ GENERIC_COMMAND("nextframe", GenericCommand_nextframe(request, arguments, command), "Execute the given command next frame of this VM") \ GENERIC_COMMAND("qc_curl", GenericCommand_qc_curl(request, arguments), "Queries a URL") \ diff --git a/qcsrc/common/constants.qh b/qcsrc/common/constants.qh index fb6d781c0c..21216452cf 100644 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@ -30,17 +30,18 @@ const float AS_FLOAT = 8; const float TE_CSQC_PICTURE = 100; const float TE_CSQC_RACE = 101; -const float TE_CSQC_NEXGUNBEAMPARTICLE = 103; -const float TE_CSQC_LIGHTNINGARC = 104; +const float TE_CSQC_VORTEXBEAMPARTICLE = 103; +const float TE_CSQC_ARC = 104; const float TE_CSQC_TEAMNAGGER = 105; const float TE_CSQC_PINGPLREPORT = 106; const float TE_CSQC_TARGET_MUSIC = 107; const float TE_CSQC_WEAPONCOMPLAIN = 108; -const float TE_CSQC_NEX_SCOPE = 109; +const float TE_CSQC_VORTEX_SCOPE = 109; const float TE_CSQC_MINELAYER_MAXMINES = 110; const float TE_CSQC_HAGAR_MAXROCKETS = 111; const float TE_CSQC_VEHICLESETUP = 112; const float TE_CSQC_SVNOTICE = 113; +const float TE_CSQC_SHOCKWAVEPARTICLE = 114; const float RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder const float RACE_NET_CHECKPOINT_CLEAR = 1; @@ -85,8 +86,7 @@ const float ENT_CLIENT_WARPZONE = 24; const float ENT_CLIENT_WARPZONE_CAMERA = 25; const float ENT_CLIENT_TRIGGER_MUSIC = 26; const float ENT_CLIENT_HOOK = 27; -const float ENT_CLIENT_LGBEAM = 28; -const float ENT_CLIENT_GAUNTLET = 29; +const float ENT_CLIENT_ARC_BEAM = 29; // WEAPONTODO: fix numbers const float ENT_CLIENT_ACCURACY = 30; const float ENT_CLIENT_SHOWNAMES = 31; const float ENT_CLIENT_WARPZONE_TELEPORTED = 32; @@ -96,7 +96,7 @@ const float ENT_CLIENT_BUMBLE_RAYGUN = 35; const float ENT_CLIENT_SPAWNPOINT = 36; const float ENT_CLIENT_SPAWNEVENT = 37; const float ENT_CLIENT_NOTIFICATION = 38; - +const float ENT_CLIENT_ELIMINATEDPLAYERS = 39; const float ENT_CLIENT_TURRET = 40; const float ENT_CLIENT_AUXILIARYXHAIR = 50; const float ENT_CLIENT_VEHICLE = 60; @@ -238,7 +238,7 @@ const float ATTEN_MAX = 3.984375; #define VOL_BASE 0.7 #define VOL_BASEVOICE 1.0 -// this sets sounds and other properties of the projectiles in csqc +// WEAPONTODO: move this into separate/new projectile handling code // this sets sounds and other properties of the projectiles in csqc const float PROJECTILE_ELECTRO = 1; const float PROJECTILE_ROCKET = 2; const float PROJECTILE_TAG = 3; @@ -247,7 +247,7 @@ const float PROJECTILE_ELECTRO_BEAM = 6; const float PROJECTILE_GRENADE = 7; const float PROJECTILE_GRENADE_BOUNCING = 8; const float PROJECTILE_MINE = 9; -const float PROJECTILE_LASER = 10; +const float PROJECTILE_BLASTER = 10; const float PROJECTILE_HLAC = 11; const float PROJECTILE_SEEKER = 12; const float PROJECTILE_FLAC = 13; @@ -292,25 +292,6 @@ const float WATERLEVEL_NONE = 0; const float WATERLEVEL_WETFEET = 1; const float WATERLEVEL_SWIMMING = 2; const float WATERLEVEL_SUBMERGED = 3; - -const float MAX_SHOT_DISTANCE = 32768; - -// weapon requests -const float WR_SETUP = 1; // (SVQC) setup weapon data -const float WR_THINK = 2; // (SVQC) logic to run every frame -const float WR_CHECKAMMO1 = 3; // (SVQC) checks ammo for weapon -const float WR_CHECKAMMO2 = 4; // (SVQC) checks ammo for weapon -const float WR_AIM = 5; // (SVQC) runs bot aiming code for this weapon -const float WR_PRECACHE = 6; // (CSQC and SVQC) precaches models/sounds used by this weapon -const float WR_SUICIDEMESSAGE = 7; // (SVQC) notification number for suicide message (may inspect w_deathtype for details) -const float WR_KILLMESSAGE = 8; // (SVQC) notification number for kill message (may inspect w_deathtype for details) -const float WR_RELOAD = 9; // (SVQC) does not need to do anything -const float WR_RESETPLAYER = 10; // (SVQC) does not need to do anything -const float WR_IMPACTEFFECT = 11; // (CSQC) impact effect -const float WR_SWITCHABLE = 12; // (CSQC) impact effect -const float WR_PLAYERDEATH = 13; // (SVQC) does not need to do anything -const float WR_GONETHINK = 14; // (SVQC) logic to run every frame, also if no longer having the weapon as long as the switch away has not been performed - #define SERVERFLAG_ALLOW_FULLBRIGHT 1 #define SERVERFLAG_TEAMPLAY 2 #define SERVERFLAG_PLAYERSTATS 4 diff --git a/qcsrc/common/csqcmodel_settings.qh b/qcsrc/common/csqcmodel_settings.qh index cfe012275c..f521d7de9d 100644 --- a/qcsrc/common/csqcmodel_settings.qh +++ b/qcsrc/common/csqcmodel_settings.qh @@ -50,7 +50,7 @@ CSQCMODEL_ENDIF \ CSQCMODEL_PROPERTY(1024, float, ReadAngle, WriteAngle, v_angle_x) \ CSQCMODEL_PROPERTY_SCALED(4096, float, ReadByte, WriteByte, scale, 16, 0, 255) -// TODO get rid of colormod/glowmod here, find good solution for nex charge glowmod hack; also get rid of some useless properties on non-players that only exist for CopyBody +// TODO get rid of colormod/glowmod here, find good solution for vortex charge glowmod hack; also get rid of some useless properties on non-players that only exist for CopyBody // add hook function calls here #define CSQCMODEL_HOOK_PREUPDATE \ diff --git a/qcsrc/common/explosion_equation.qc b/qcsrc/common/explosion_equation.qc deleted file mode 100644 index 3fddd70d23..0000000000 --- a/qcsrc/common/explosion_equation.qc +++ /dev/null @@ -1,62 +0,0 @@ -float explosion_calcpush_getmultiplier(vector explosion_v, vector target_v) -{ - float a; - a = explosion_v * (explosion_v - target_v); - - if(a <= 0) - // target is too fast to be hittable by this - return 0; - - a /= (explosion_v * explosion_v); - // we know we can divide by this, or above a would be == 0 - - return a; -} - -#if 0 -vector explosion_calcpush(vector explosion_v, float explosion_m, vector target_v, float target_m, float elasticity) -{ - // solution of the equations: - // v' = v + a vp // central hit - // m*v' + mp*vp' = m*v + mp*vp // conservation of momentum - // m*v'^2 + mp*vp'^2 = m*v^2 + mp*vp^2 // conservation of energy (ELASTIC hit) - // -> a = 0 // case 1: did not hit - // -> a = 2*mp*(vp^2 - vp.v) / ((m+mp) * vp^2) // case 2: did hit - // // non-elastic hits are somewhere between these two - - // this would be physically correct, but we don't do that - return explosion_v * explosion_calcpush_getmultiplier(explosion_v, target_v) * ( - (1 + elasticity) * ( - explosion_m - ) / ( - target_m + explosion_m - ) - ); // note: this factor is at least 0, at most 2 -} -#endif - -// simplified formula, tuned so that if the target has velocity 0, we get exactly the original force -vector damage_explosion_calcpush(vector explosion_f, vector target_v, float speedfactor) -{ - // if below 1, the formulas make no sense (and would cause superjumps) - if(speedfactor < 1) - return explosion_f; - -#if 0 - float m; - // find m so that - // speedfactor * (1 + e) * m / (1 + m) == 1 - m = 1 / ((1 + 0) * speedfactor - 1); - vector v; - v = explosion_calcpush(explosion_f * speedfactor, m, target_v, 1, 0); - // the factor we then get is: - // 1 - printf("MASS: %f\nv: %v -> %v\nENERGY BEFORE == %f + %f = %f\nENERGY AFTER >= %f\n", - m, - target_v, target_v + v, - target_v * target_v, m * explosion_f * speedfactor * explosion_f * speedfactor, target_v * target_v + m * explosion_f * speedfactor * explosion_f * speedfactor, - (target_v + v) * (target_v + v)); - return v; -#endif - return explosion_f * explosion_calcpush_getmultiplier(explosion_f * speedfactor, target_v); -} diff --git a/qcsrc/common/explosion_equation.qh b/qcsrc/common/explosion_equation.qh deleted file mode 100644 index c8630cdb19..0000000000 --- a/qcsrc/common/explosion_equation.qh +++ /dev/null @@ -1 +0,0 @@ -vector damage_explosion_calcpush(vector explosion_f, vector target_v, float speedfactor); diff --git a/qcsrc/common/items.qc b/qcsrc/common/items.qc deleted file mode 100644 index 19173079fa..0000000000 --- a/qcsrc/common/items.qc +++ /dev/null @@ -1,251 +0,0 @@ -// WEAPON PLUGIN SYSTEM -entity weapon_info[WEP_MAXCOUNT]; -entity dummy_weapon_info; - -#if WEP_MAXCOUNT > 72 -# error Kein Weltraum links auf dem Gerät -#endif - -WepSet WepSet_FromWeapon(float a) { - a -= WEP_FIRST; -#if WEP_MAXCOUNT > 24 - if(a >= 24) { - a -= 24; -#if WEP_MAXCOUNT > 48 - if(a >= 24) { - a -= 24; - return '0 0 1' * power2of(a); - } -#endif - return '0 1 0' * power2of(a); - } -#endif - return '1 0 0' * power2of(a); -} -#ifdef SVQC -void WepSet_AddStat() -{ - addstat(STAT_WEAPONS, AS_INT, weapons_x); -#if WEP_MAXCOUNT > 24 - addstat(STAT_WEAPONS2, AS_INT, weapons_y); -#if WEP_MAXCOUNT > 48 - addstat(STAT_WEAPONS3, AS_INT, weapons_z); -#endif -#endif -} -void WriteWepSet(float dst, WepSet w) -{ -#if WEP_MAXCOUNT > 48 - WriteInt72_t(dst, w); -#elif WEP_MAXCOUNT > 24 - WriteInt48_t(dst, w); -#else - WriteInt24_t(dst, w_x); -#endif -} -#endif -#ifdef CSQC -WepSet WepSet_GetFromStat() -{ - WepSet w = '0 0 0'; - w_x = getstati(STAT_WEAPONS); -#if WEP_MAXCOUNT > 24 - w_y = getstati(STAT_WEAPONS2); -#if WEP_MAXCOUNT > 48 - w_z = getstati(STAT_WEAPONS3); -#endif -#endif - return w; -} -WepSet ReadWepSet() -{ -#if WEP_MAXCOUNT > 48 - return ReadInt72_t(); -#elif WEP_MAXCOUNT > 24 - return ReadInt48_t(); -#else - return ReadInt24_t() * '1 0 0'; -#endif -} -#endif - -void register_weapon(float id, WepSet bit, float(float) func, float ammotype, float i, float weapontype, float pickupbasevalue, string modelname, string shortname, string wname) -{ - entity e; - weapon_info[id - 1] = e = spawn(); - e.classname = "weapon_info"; - e.weapon = id; - e.weapons = bit; - e.netname = shortname; - e.message = wname; - e.items = ammotype; - e.weapon_func = func; - e.mdl = modelname; - e.model = strzone(strcat("models/weapons/g_", modelname, ".md3")); - e.spawnflags = weapontype; - e.model2 = strzone(strcat("wpn-", e.mdl)); - e.impulse = i; - e.bot_pickupbasevalue = pickupbasevalue; - if(ammotype & IT_SHELLS) - e.ammo_field = ammo_shells; - else if(ammotype & IT_NAILS) - e.ammo_field = ammo_nails; - else if(ammotype & IT_ROCKETS) - e.ammo_field = ammo_rockets; - else if(ammotype & IT_CELLS) - e.ammo_field = ammo_cells; - else if(ammotype & IT_FUEL) - e.ammo_field = ammo_fuel; - else - e.ammo_field = ammo_batteries; -} -float w_null(float dummy) -{ - return 0; -} -void register_weapons_done() -{ - dummy_weapon_info = spawn(); - dummy_weapon_info.classname = "weapon_info"; - dummy_weapon_info.weapon = 0; // you can recognize dummies by this - dummy_weapon_info.weapons = '0 0 0'; - dummy_weapon_info.netname = ""; - dummy_weapon_info.message = "AOL CD Thrower"; - dummy_weapon_info.items = 0; - dummy_weapon_info.weapon_func = w_null; - dummy_weapon_info.mdl = ""; - dummy_weapon_info.model = ""; - dummy_weapon_info.spawnflags = 0; - dummy_weapon_info.model2 = ""; - dummy_weapon_info.impulse = -1; - dummy_weapon_info.bot_pickupbasevalue = 0; - - float i; - weaponorder_byid = ""; - for(i = WEP_MAXCOUNT; i >= 1; --i) - if(weapon_info[i-1]) - weaponorder_byid = strcat(weaponorder_byid, " ", ftos(i)); - weaponorder_byid = strzone(substring(weaponorder_byid, 1, strlen(weaponorder_byid) - 1)); -} -entity get_weaponinfo(float id) -{ - entity w; - if(id < WEP_FIRST || id > WEP_LAST) - return dummy_weapon_info; - w = weapon_info[id - 1]; - if(w) - return w; - return dummy_weapon_info; -} -string W_FixWeaponOrder(string order, float complete) -{ - return fixPriorityList(order, WEP_FIRST, WEP_LAST, 230 - WEP_FIRST, complete); -} -string W_NameWeaponOrder_MapFunc(string s) -{ - entity wi; - if(s == "0" || stof(s)) - { - wi = get_weaponinfo(stof(s)); - if(wi != dummy_weapon_info) - return wi.netname; - } - return s; -} -string W_NameWeaponOrder(string order) -{ - return mapPriorityList(order, W_NameWeaponOrder_MapFunc); -} -string W_NumberWeaponOrder_MapFunc(string s) -{ - float i; - if(s == "0" || stof(s)) - return s; - for(i = WEP_FIRST; i <= WEP_LAST; ++i) - if(s == get_weaponinfo(i).netname) - return ftos(i); - return s; -} -string W_NumberWeaponOrder(string order) -{ - return mapPriorityList(order, W_NumberWeaponOrder_MapFunc); -} - -float W_FixWeaponOrder_BuildImpulseList_buf[WEP_MAXCOUNT]; -string W_FixWeaponOrder_BuildImpulseList_order; -void W_FixWeaponOrder_BuildImpulseList_swap(float i, float j, entity pass) -{ - float h; - h = W_FixWeaponOrder_BuildImpulseList_buf[i]; - W_FixWeaponOrder_BuildImpulseList_buf[i] = W_FixWeaponOrder_BuildImpulseList_buf[j]; - W_FixWeaponOrder_BuildImpulseList_buf[j] = h; -} -float W_FixWeaponOrder_BuildImpulseList_cmp(float i, float j, entity pass) -{ - entity e1, e2; - float d; - e1 = get_weaponinfo(W_FixWeaponOrder_BuildImpulseList_buf[i]); - e2 = get_weaponinfo(W_FixWeaponOrder_BuildImpulseList_buf[j]); - d = mod(e1.impulse + 9, 10) - mod(e2.impulse + 9, 10); - if(d != 0) - return -d; // high impulse first! - return - strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "), sprintf(" %d ", W_FixWeaponOrder_BuildImpulseList_buf[i]), 0) - - - strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "), sprintf(" %d ", W_FixWeaponOrder_BuildImpulseList_buf[j]), 0) - ; // low char index first! -} -string W_FixWeaponOrder_BuildImpulseList(string o) -{ - float i; - W_FixWeaponOrder_BuildImpulseList_order = o; - for(i = WEP_FIRST; i <= WEP_LAST; ++i) - W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST] = i; - heapsort(WEP_LAST - WEP_FIRST + 1, W_FixWeaponOrder_BuildImpulseList_swap, W_FixWeaponOrder_BuildImpulseList_cmp, world); - o = ""; - for(i = WEP_FIRST; i <= WEP_LAST; ++i) - o = strcat(o, " ", ftos(W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST])); - W_FixWeaponOrder_BuildImpulseList_order = string_null; - return substring(o, 1, -1); -} - -string W_FixWeaponOrder_AllowIncomplete(string order) -{ - return W_FixWeaponOrder(order, 0); -} - -string W_FixWeaponOrder_ForceComplete(string order) -{ - if(order == "") - order = W_NumberWeaponOrder(cvar_defstring("cl_weaponpriority")); - return W_FixWeaponOrder(order, 1); -} - -void W_RandomWeapons(entity e, float n) -{ - float i, j; - WepSet remaining; - WepSet result; - remaining = e.weapons; - result = '0 0 0'; - for(i = 0; i < n; ++i) - { - RandomSelection_Init(); - for(j = WEP_FIRST; j <= WEP_LAST; ++j) - if(remaining & WepSet_FromWeapon(j)) - RandomSelection_Add(world, j, string_null, 1, 1); - result |= WepSet_FromWeapon(RandomSelection_chosen_float); - remaining &= ~WepSet_FromWeapon(RandomSelection_chosen_float); - } - e.weapons = result; -} - -string W_Name(float weaponid) -{ - return (get_weaponinfo(weaponid)).message; -} - -float W_AmmoItemCode(float wpn) -{ - return (get_weaponinfo(wpn)).items & IT_AMMO; -} diff --git a/qcsrc/common/items.qh b/qcsrc/common/items.qh deleted file mode 100644 index 264c9ca73b..0000000000 --- a/qcsrc/common/items.qh +++ /dev/null @@ -1,151 +0,0 @@ -const float BOT_PICKUP_RATING_LOW = 2500; -const float BOT_PICKUP_RATING_MID = 5000; -const float BOT_PICKUP_RATING_HIGH = 10000; - -const float WEP_TYPE_OTHER = 0x00; // not for damaging people -const float WEP_TYPE_SPLASH = 0x01; // splash damage -const float WEP_TYPE_HITSCAN = 0x02; // hitscan -const float WEP_TYPEMASK = 0x0F; -const float WEP_FLAG_CANCLIMB = 0x10; // can be used for movement -const float WEP_FLAG_NORMAL = 0x20; // in "most weapons" set -const float WEP_FLAG_HIDDEN = 0x40; // hides from menu -const float WEP_FLAG_RELOADABLE = 0x80; // can has reload -const float WEP_FLAG_SUPERWEAPON = 0x100; // powerup timer -const float WEP_FLAG_MUTATORBLOCKED = 0x200; // hides from impulse 99 etc. (mutators are allowed to clear this flag) - -const float IT_UNLIMITED_WEAPON_AMMO = 1; -// when this bit is set, using a weapon does not reduce ammo. Checkpoints can give this powerup. -const float IT_UNLIMITED_SUPERWEAPONS = 2; -// when this bit is set, superweapons don't expire. Checkpoints can give this powerup. -const float IT_CTF_SHIELDED = 4; // set for the flag shield -const float IT_USING_JETPACK = 8; // confirmation that button is pressed -const float IT_JETPACK = 16; // actual item -const float IT_FUEL_REGEN = 32; // fuel regeneration trigger -WANT_CONST float IT_SHELLS = 256; -WANT_CONST float IT_NAILS = 512; -WANT_CONST float IT_ROCKETS = 1024; -WANT_CONST float IT_CELLS = 2048; -const float IT_SUPERWEAPON = 4096; -const float IT_FUEL = 128; -const float IT_STRENGTH = 8192; -const float IT_INVINCIBLE = 16384; -const float IT_HEALTH = 32768; -// union: - // for items: - WANT_CONST float IT_KEY1 = 131072; - WANT_CONST float IT_KEY2 = 262144; - // for players: - const float IT_RED_FLAG_TAKEN = 32768; - const float IT_RED_FLAG_LOST = 65536; - const float IT_RED_FLAG_CARRYING = 98304; - const float IT_BLUE_FLAG_TAKEN = 131072; - const float IT_BLUE_FLAG_LOST = 262144; - const float IT_BLUE_FLAG_CARRYING = 393216; -// end -const float IT_5HP = 524288; -const float IT_25HP = 1048576; -const float IT_ARMOR_SHARD = 2097152; -const float IT_ARMOR = 4194304; - -const float IT_AMMO = 3968; // IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS | IT_FUEL; -const float IT_PICKUPMASK = 51; // IT_FUEL_REGEN | IT_JETPACK | IT_UNLIMITED_AMMO; // strength and invincible are handled separately -const float IT_UNLIMITED_AMMO = 3; // IT_UNLIMITED_SUPERWEAPONS | IT_UNLIMITED_WEAPON_AMMO; - -// variables: -string weaponorder_byid; - -// functions: -entity get_weaponinfo(float id); -string W_FixWeaponOrder(string order, float complete); -string W_NameWeaponOrder(string order); -string W_NumberWeaponOrder(string order); - -// ammo types -.float ammo_shells; -.float ammo_nails; -.float ammo_rockets; -.float ammo_cells; -.float ammo_fuel; -.float ammo_batteries; // dummy - -// Weapon sets -typedef vector WepSet; -WepSet WepSet_FromWeapon(float a); -#ifdef SVQC -void WepSet_AddStat(); -void WriteWepSet(float dest, WepSet w); -#endif -#ifdef CSQC -WepSet WepSet_GetFromStat(); -WepSet ReadWepSet(); -#endif - -// Weapon name macros -#define WEP_FIRST 1 -#define WEP_MAXCOUNT 24 // Increase as needed. Can be up to three times as much. -float WEP_COUNT; -float WEP_LAST; -WepSet WEPSET_ALL; -WepSet WEPSET_SUPERWEAPONS; - -// entity properties of weaponinfo: -.float weapon; // WEP_... -.WepSet weapons; // WEPSET_... -.string netname; // short name -.string message; // human readable name -.float items; // IT_... -.float(float) weapon_func; // w_... -.string mdl; // modelname without g_, v_, w_ -.string model; // full name of g_ model -.float spawnflags; // WEPSPAWNFLAG_... combined -.float impulse; // weapon impulse -.float bot_pickupbasevalue; // bot weapon priority -.string model2; // wpn- sprite name -..float ammo_field; // main ammo field - -// dynamic weapon adding -float w_null(float dummy); -void register_weapon(float id, WepSet bit, float(float) func, float ammotype, float i, float weapontype, float pickupbasevalue, string modelname, string shortname, string wname); -void register_weapons_done(); - -#define REGISTER_WEAPON_2(id,bit,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \ - float id; \ - WepSet bit; \ - float func(float); \ - void RegisterWeapons_##id() \ - { \ - WEP_LAST = (id = WEP_FIRST + WEP_COUNT); \ - bit = WepSet_FromWeapon(id); \ - WEPSET_ALL |= bit; \ - if((weapontype) & WEP_FLAG_SUPERWEAPON) \ - WEPSET_SUPERWEAPONS |= bit; \ - ++WEP_COUNT; \ - register_weapon(id,bit,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname); \ - } \ - ACCUMULATE_FUNCTION(RegisterWeapons, RegisterWeapons_##id) -#ifdef MENUQC -#define REGISTER_WEAPON(id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \ - REGISTER_WEAPON_2(WEP_##id,WEPSET_##id,w_null,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) -#else -#define REGISTER_WEAPON(id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \ - REGISTER_WEAPON_2(WEP_##id,WEPSET_##id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) -#endif - -#include "../server/w_all.qc" - -#undef REGISTER_WEAPON -ACCUMULATE_FUNCTION(RegisterWeapons, register_weapons_done); - - -string W_FixWeaponOrder(string order, float complete); -string W_NumberWeaponOrder(string order); -string W_NameWeaponOrder(string order); -string W_FixWeaponOrder_BuildImpulseList(string o); -string W_FixWeaponOrder_AllowIncomplete(string order); -string W_FixWeaponOrder_ForceComplete(string order); - -void W_RandomWeapons(entity e, float n); - -string W_Name(float weaponid); - -float W_AmmoItemCode(float wpn); diff --git a/qcsrc/common/mapinfo.qc b/qcsrc/common/mapinfo.qc index 84df17516d..ba1fbeb6f4 100644 --- a/qcsrc/common/mapinfo.qc +++ b/qcsrc/common/mapinfo.qc @@ -1133,7 +1133,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, float pAllowGenerate, flo else if(MapInfo_isRedundant(MapInfo_Map_bspname, MapInfo_Map_title)) MapInfo_Map_titlestring = MapInfo_Map_title; else - MapInfo_Map_titlestring = sprintf(_("%s: %s"), MapInfo_Map_bspname, MapInfo_Map_title); + MapInfo_Map_titlestring = sprintf("%s: %s", MapInfo_Map_bspname, MapInfo_Map_title); MapInfo_Cache_Store(); if(MapInfo_Map_supportedGametypes != 0) diff --git a/qcsrc/common/mapinfo.qh b/qcsrc/common/mapinfo.qh index a933a6db50..3eae41ea67 100644 --- a/qcsrc/common/mapinfo.qh +++ b/qcsrc/common/mapinfo.qh @@ -53,7 +53,7 @@ REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,"timelimit=20 p REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,"timelimit=20 caplimit=10 leadlimit=0",_("Find and bring the enemy flag to your base to capture it")); #define g_ctf IS_GAMETYPE(CTF) -REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 leadlimit=0",_("Kill all enemy teammates to win the round")); +REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill all enemy teammates to win the round")); #define g_ca IS_GAMETYPE(CA) REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,"timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture all the control points to win")); diff --git a/qcsrc/common/monsters/monster/mage.qc b/qcsrc/common/monsters/monster/mage.qc index 26bb0a9afd..3adc59a302 100644 --- a/qcsrc/common/monsters/monster/mage.qc +++ b/qcsrc/common/monsters/monster/mage.qc @@ -70,7 +70,7 @@ float friend_needshelp(entity e) switch(self.skin) { case 0: return (e.health < autocvar_g_balance_health_regenstable); - case 1: return ((e.ammo_cells && e.ammo_cells < g_pickup_cells_max) || (e.ammo_rockets && e.ammo_rockets < g_pickup_rockets_max) || (e.ammo_nails && e.ammo_nails < g_pickup_nails_max) || (e.ammo_shells && e.ammo_shells < g_pickup_shells_max)); + case 1: return ((e.ammo_cells && e.ammo_cells < g_pickup_cells_max) || (e.ammo_plasma && e.ammo_plasma < g_pickup_plasma_max) || (e.ammo_rockets && e.ammo_rockets < g_pickup_rockets_max) || (e.ammo_nails && e.ammo_nails < g_pickup_nails_max) || (e.ammo_shells && e.ammo_shells < g_pickup_shells_max)); case 2: return (e.armorvalue < autocvar_g_balance_armor_regenstable); case 3: return (e.health > 0); } @@ -87,7 +87,7 @@ void mage_spike_explode() self.realowner.mage_spike = world; pointparticles(particleeffectnum("explosion_small"), self.origin, '0 0 0', 1); - RadiusDamage (self, self.realowner, (autocvar_g_monster_mage_attack_spike_damage), (autocvar_g_monster_mage_attack_spike_damage) * 0.5, (autocvar_g_monster_mage_attack_spike_radius), world, 0, DEATH_MONSTER_MAGE, other); + RadiusDamage (self, self.realowner, (autocvar_g_monster_mage_attack_spike_damage), (autocvar_g_monster_mage_attack_spike_damage) * 0.5, (autocvar_g_monster_mage_attack_spike_radius), world, world, 0, DEATH_MONSTER_MAGE, other); remove (self); } @@ -209,6 +209,7 @@ void mage_heal() break; case 1: if(head.ammo_cells) head.ammo_cells = bound(head.ammo_cells, head.ammo_cells + 1, g_pickup_cells_max); + if(head.ammo_plasma) head.ammo_plasma = bound(head.ammo_plasma, head.ammo_plasma + 1, g_pickup_plasma_max); if(head.ammo_rockets) head.ammo_rockets = bound(head.ammo_rockets, head.ammo_rockets + 1, g_pickup_rockets_max); if(head.ammo_shells) head.ammo_shells = bound(head.ammo_shells, head.ammo_shells + 2, g_pickup_shells_max); if(head.ammo_nails) head.ammo_nails = bound(head.ammo_nails, head.ammo_nails + 5, g_pickup_nails_max); @@ -248,7 +249,7 @@ void mage_heal() void mage_push() { sound(self, CH_SHOTS, "weapons/tagexp1.wav", 1, ATTEN_NORM); - RadiusDamage (self, self, (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_radius), world, (autocvar_g_monster_mage_attack_push_force), DEATH_MONSTER_MAGE, self.enemy); + RadiusDamage (self, self, (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_radius), world, world, (autocvar_g_monster_mage_attack_push_force), DEATH_MONSTER_MAGE, self.enemy); pointparticles(particleeffectnum("TE_EXPLOSION"), self.origin, '0 0 0', 1); self.frame = mage_anim_attack; diff --git a/qcsrc/common/monsters/monster/shambler.qc b/qcsrc/common/monsters/monster/shambler.qc index 0e82fe8d29..7c46a1da15 100644 --- a/qcsrc/common/monsters/monster/shambler.qc +++ b/qcsrc/common/monsters/monster/shambler.qc @@ -74,7 +74,7 @@ void shambler_lightning_explode() if(self.movetype == MOVETYPE_NONE) self.velocity = self.oldvelocity; - RadiusDamage (self, self.realowner, (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_radius), world, (autocvar_g_monster_shambler_attack_lightning_force), self.projectiledeathtype, other); + RadiusDamage (self, self.realowner, (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_radius), world, world, (autocvar_g_monster_shambler_attack_lightning_force), self.projectiledeathtype, other); for(head = findradius(self.origin, (autocvar_g_monster_shambler_attack_lightning_radius_zap)); head; head = head.chain) if(head != self.realowner) if(head.takedamage) { @@ -148,7 +148,7 @@ void shambler_lightning() gren.event_damage = shambler_lightning_damage; gren.damagedbycontents = TRUE; gren.missile_flags = MIF_SPLASH | MIF_ARC; - W_SetupProjectileVelocityEx(gren, v_forward, v_up, (autocvar_g_monster_shambler_attack_lightning_speed), (autocvar_g_monster_shambler_attack_lightning_speed_up), 0, 0, FALSE); + W_SetupProjVelocity_Explicit(gren, v_forward, v_up, (autocvar_g_monster_shambler_attack_lightning_speed), (autocvar_g_monster_shambler_attack_lightning_speed_up), 0, 0, FALSE); gren.angles = vectoangles (gren.velocity); gren.flags = FL_PROJECTILE; @@ -222,7 +222,7 @@ float m_shambler(float req) self.monster_loot = spawnfunc_item_health_mega; self.monster_attackfunc = shambler_attack; self.frame = shambler_anim_stand; - self.weapon = WEP_NEX; + self.weapon = WEP_VORTEX; return TRUE; } diff --git a/qcsrc/common/monsters/monster/spider.qc b/qcsrc/common/monsters/monster/spider.qc index 7e35b8b162..c6c454547f 100644 --- a/qcsrc/common/monsters/monster/spider.qc +++ b/qcsrc/common/monsters/monster/spider.qc @@ -35,7 +35,7 @@ void spider_web_explode() if(self) { pointparticles(particleeffectnum("electro_impact"), self.origin, '0 0 0', 1); - RadiusDamage(self, self.realowner, 0, 0, 25, world, 25, self.projectiledeathtype, world); + RadiusDamage(self, self.realowner, 0, 0, 25, world, world, 25, self.projectiledeathtype, world); for(e = findradius(self.origin, 25); e; e = e.chain) if(e != self) if(e.takedamage && e.deadflag == DEAD_NO) if(e.health > 0) if(e.monsterid != MON_SPIDER) e.spider_slowness = time + (autocvar_g_monster_spider_attack_web_damagetime); @@ -72,7 +72,7 @@ void spider_shootweb() //proj.glow_size = 50; //proj.glow_color = 45; proj.movetype = MOVETYPE_BOUNCE; - W_SetupProjectileVelocityEx(proj, v_forward, v_up, (autocvar_g_monster_spider_attack_web_speed), (autocvar_g_monster_spider_attack_web_speed_up), 0, 0, FALSE); + W_SetupProjVelocity_Explicit(proj, v_forward, v_up, (autocvar_g_monster_spider_attack_web_speed), (autocvar_g_monster_spider_attack_web_speed_up), 0, 0, FALSE); proj.touch = spider_web_touch; setsize(proj, '-4 -4 -4', '4 4 4'); proj.takedamage = DAMAGE_NO; diff --git a/qcsrc/common/monsters/monster/wyvern.qc b/qcsrc/common/monsters/monster/wyvern.qc index ed4962d061..a44f6d9c7a 100644 --- a/qcsrc/common/monsters/monster/wyvern.qc +++ b/qcsrc/common/monsters/monster/wyvern.qc @@ -35,7 +35,7 @@ void wyvern_fireball_explode() { pointparticles(particleeffectnum("fireball_explode"), self.origin, '0 0 0', 1); - RadiusDamage(self, self.realowner, (autocvar_g_monster_wyvern_attack_fireball_damage), (autocvar_g_monster_wyvern_attack_fireball_edgedamage), (autocvar_g_monster_wyvern_attack_fireball_force), world, (autocvar_g_monster_wyvern_attack_fireball_radius), self.projectiledeathtype, world); + RadiusDamage(self, self.realowner, (autocvar_g_monster_wyvern_attack_fireball_damage), (autocvar_g_monster_wyvern_attack_fireball_edgedamage), (autocvar_g_monster_wyvern_attack_fireball_force), world, world, (autocvar_g_monster_wyvern_attack_fireball_radius), self.projectiledeathtype, world); for(e = world; (e = findfloat(e, takedamage, DAMAGE_AIM)); ) if(vlen(e.origin - self.origin) <= (autocvar_g_monster_wyvern_attack_fireball_radius)) Fire_AddDamage(e, self, 5 * Monster_SkillModifier(), (autocvar_g_monster_wyvern_attack_fireball_damagetime), self.projectiledeathtype); diff --git a/qcsrc/common/monsters/sv_monsters.qc b/qcsrc/common/monsters/sv_monsters.qc index 6d06de50eb..fd966f5d2e 100644 --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@ -335,7 +335,7 @@ void Monster_CheckMinibossFlag () self.health += autocvar_g_monsters_miniboss_healthboost; self.effects |= EF_RED; if(!self.weapon) - self.weapon = WEP_NEX; + self.weapon = WEP_VORTEX; } } diff --git a/qcsrc/common/notifications.qh b/qcsrc/common/notifications.qh index 567b226399..676a5d32ce 100644 --- a/qcsrc/common/notifications.qh +++ b/qcsrc/common/notifications.qh @@ -388,7 +388,7 @@ void Send_Notification_WOCOVA( MSG_INFO_NOTIF(1, INFO_DEATH_SELF_BETRAYAL, 2, 1, "s1 s2loc spree_lost", "s1", "notify_teamkill_red", _("^BG%s^K1 became enemies with the Lord of Teamplay%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_CAMP, 2, 1, "s1 s2loc spree_lost", "s1", "notify_camping", _("^BG%s^K1 thought they found a nice camping ground%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_CHEAT, 2, 1, "s1 s2loc spree_lost", "s1", "notify_selfkill", _("^BG%s^K1 unfairly eliminated themself%s%s"), "") \ - MSG_INFO_NOTIF(1, INFO_DEATH_SELF_CUSTOM, 3, 1, "s1 s2 s3loc spree_lost", "s1", "notify_void", _("^BG%s^K1 %s^K1%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_DEATH_SELF_CUSTOM, 3, 1, "s1 s2 s3loc spree_lost", "s1", "notify_void", "^BG%s^K1 %s^K1%s%s", "") \ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_DROWN, 2, 1, "s1 s2loc spree_lost", "s1", "notify_water", _("^BG%s^K1 couldn't catch their breath%s%s"), _("^BG%s^K1 was in the water for too long%s%s")) \ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_FALL, 2, 1, "s1 s2loc spree_lost", "s1", "notify_fall", _("^BG%s^K1 hit the ground with a crunch%s%s"), _("^BG%s^K1 hit the ground with a bit too much force%s%s")) \ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_FIRE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 became a bit too crispy%s%s"), _("^BG%s^K1 felt a little hot%s%s")) \ @@ -438,6 +438,8 @@ void Send_Notification_WOCOVA( MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_WAKI_ROCKET, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 couldn't find shelter from a Racer rocket%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VOID, 2, 1, "s1 s2loc spree_lost", "s1", "notify_void", _("^BG%s^K1 was in the wrong place%s%s"), "") \ MULTITEAM_INFO(1, INFO_DEATH_TEAMKILL_, 4, 3, 1, "s1 s2 s3loc spree_end", "s2 s1", "notify_teamkill_%s", _("^BG%s^K1 was betrayed by ^BG%s^K1%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_CA_JOIN_LATE, 0, 0, "", "", "", _("^F1Round already started, you will join the game in the next round"), "") \ + MSG_INFO_NOTIF(1, INFO_CA_LEAVE, 0, 0, "", "", "", _("^F2You will spectate in the next round"), "") \ MSG_INFO_NOTIF(1, INFO_DOMINATION_CAPTURE_TIME, 2, 2, "s1 s2 f1 f2", "", "", _("^BG%s^BG%s^BG (%s points every %s seconds)"), "") \ MSG_INFO_NOTIF(1, INFO_FREEZETAG_FREEZE, 2, 0, "s1 s2", "", "", _("^BG%s^K1 was frozen by ^BG%s"), "") \ MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED, 2, 0, "s1 s2", "", "", _("^BG%s^K3 was revived by ^BG%s"), "") \ @@ -499,13 +501,19 @@ void Send_Notification_WOCOVA( MSG_INFO_NOTIF(1, INFO_WATERMARK, 1, 0, "s1", "", "", _("^F3SVQC Build information: ^F4%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_ACCORDEON_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weapontuba", _("^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Accordeon%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_ACCORDEON_SUICIDE, 2, 1, "s1 s2loc spree_lost", "s1", "weapontuba", _("^BG%s^K1 hurt their own ears with the @!#%%'n Accordeon%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_WEAPON_ARC_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponhlac", _("^BG%s%s^K1 was electrocuted by ^BG%s^K1's Arc%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_WEAPON_BLASTER_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponlaser", _("^BG%s%s^K1 was shot to death by ^BG%s^K1's Blaster%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_WEAPON_BLASTER_SUICIDE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponlaser", _("^BG%s^K1 shot themself to hell with their Blaster%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_CRYLINK_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponcrylink", _("^BG%s%s^K1 felt the strong pull of ^BG%s^K1's Crylink%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_CRYLINK_SUICIDE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponcrylink", _("^BG%s^K1 felt the strong pull of their Crylink%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_WEAPON_DEVASTATOR_MURDER_DIRECT, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrocketlauncher", _("^BG%s%s^K1 ate ^BG%s^K1's rocket%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_WEAPON_DEVASTATOR_MURDER_SPLASH, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrocketlauncher", _("^BG%s%s^K1 got too close ^BG%s^K1's rocket%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_WEAPON_DEVASTATOR_SUICIDE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponrocketlauncher", _("^BG%s^K1 blew themself up with their Devastator%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_MURDER_BOLT, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponelectro", _("^BG%s%s^K1 was blasted by ^BG%s^K1's Electro bolt%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_MURDER_COMBO, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponelectro", _("^BG%s%s^K1 felt the electrifying air of ^BG%s^K1's Electro combo%s%s"), "") \ - MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_MURDER_ORBS, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponelectro", _("^BG%s%s^K1 got too close to ^BG%s^K1's Electro plasma%s%s"), "") \ - MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_SUICIDE_BOLT, 2, 1, "s1 s2loc spree_lost", "s1", "weaponelectro", _("^BG%s^K1 played with Electro plasma%s%s"), "") \ - MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_SUICIDE_ORBS, 2, 1, "s1 s2loc spree_lost", "s1", "weaponelectro", _("^BG%s^K1 could not remember where they put their Electro plasma%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_MURDER_ORBS, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponelectro", _("^BG%s%s^K1 got too close to ^BG%s^K1's Electro orb%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_SUICIDE_BOLT, 2, 1, "s1 s2loc spree_lost", "s1", "weaponelectro", _("^BG%s^K1 played with Electro bolts%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_WEAPON_ELECTRO_SUICIDE_ORBS, 2, 1, "s1 s2loc spree_lost", "s1", "weaponelectro", _("^BG%s^K1 could not remember where they put their Electro orb%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_FIREBALL_MURDER_BLAST, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponfireball", _("^BG%s%s^K1 got too close to ^BG%s^K1's fireball%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_FIREBALL_MURDER_FIREMINE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponfireball", _("^BG%s%s^K1 got burnt by ^BG%s^K1's firemine%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_FIREBALL_SUICIDE_BLAST, 2, 1, "s1 s2loc spree_lost", "s1", "weaponfireball", _("^BG%s^K1 should have used a smaller gun%s%s"), "") \ @@ -518,33 +526,30 @@ void Send_Notification_WOCOVA( MSG_INFO_NOTIF(1, INFO_WEAPON_HOOK_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponhook", _("^BG%s%s^K1 was caught in ^BG%s^K1's Hook gravity bomb%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_KLEINBOTTLE_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weapontuba", _("^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Klein Bottle%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_KLEINBOTTLE_SUICIDE, 2, 1, "s1 s2loc spree_lost", "s1", "weapontuba", _("^BG%s^K1 hurt their own ears with the @!#%%'n Klein Bottle%s%s"), "") \ - MSG_INFO_NOTIF(1, INFO_WEAPON_LASER_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponlaser", _("^BG%s%s^K1 was shot to death by ^BG%s^K1's Laser%s%s"), "") \ - MSG_INFO_NOTIF(1, INFO_WEAPON_LASER_SUICIDE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponlaser", _("^BG%s^K1 shot themself to hell with their Laser%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_WEAPON_MACHINEGUN_MURDER_SNIPE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponuzi", _("^BG%s%s^K1 was sniped by ^BG%s^K1's Machine Gun%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_WEAPON_MACHINEGUN_MURDER_SPRAY, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponuzi", _("^BG%s%s^K1 was riddled full of holes by ^BG%s^K1's Machine Gun%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_MINELAYER_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponminelayer", _("^BG%s%s^K1 got too close to ^BG%s^K1's mine%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_MINELAYER_SUICIDE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponminelayer", _("^BG%s^K1 forgot about their mine%s%s"), "") \ - MSG_INFO_NOTIF(1, INFO_WEAPON_MINSTANEX_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponminstanex", _("^BG%s%s^K1 has been vaporized by ^BG%s^K1's Minstanex%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_MURDER_BOUNCE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weapongrenadelauncher", _("^BG%s%s^K1 got too close to ^BG%s^K1's Mortar grenade%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_MURDER_EXPLODE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weapongrenadelauncher", _("^BG%s%s^K1 ate ^BG%s^K1's Mortar grenade%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_SUICIDE_BOUNCE, 2, 1, "s1 s2loc spree_lost", "s1", "weapongrenadelauncher", _("^BG%s^K1 didn't see their own Mortar grenade%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_MORTAR_SUICIDE_EXPLODE, 2, 1, "s1 s2loc spree_lost", "s1", "weapongrenadelauncher", _("^BG%s^K1 blew themself up with their own Mortar%s%s"), "") \ - MSG_INFO_NOTIF(1, INFO_WEAPON_NEX_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponnex", _("^BG%s%s^K1 has been vaporized by ^BG%s^K1's Nex%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrifle", _("^BG%s%s^K1 was sniped with a Rifle by ^BG%s^K1%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER_HAIL, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrifle", _("^BG%s%s^K1 died in ^BG%s^K1's Rifle bullet hail%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER_HAIL_PIERCING, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrifle", _("^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle bullet hail%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_RIFLE_MURDER_PIERCING, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrifle", _("^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle%s%s"), "") \ - MSG_INFO_NOTIF(1, INFO_WEAPON_ROCKETLAUNCHER_MURDER_DIRECT, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrocketlauncher", _("^BG%s%s^K1 ate ^BG%s^K1's rocket%s%s"), "") \ - MSG_INFO_NOTIF(1, INFO_WEAPON_ROCKETLAUNCHER_MURDER_SPLASH, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrocketlauncher", _("^BG%s%s^K1 got too close ^BG%s^K1's rocket%s%s"), "") \ - MSG_INFO_NOTIF(1, INFO_WEAPON_ROCKETLAUNCHER_SUICIDE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponrocketlauncher", _("^BG%s^K1 blew themself up with their Rocketlauncher%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_SEEKER_MURDER_SPRAY, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponseeker", _("^BG%s%s^K1 was pummeled by ^BG%s^K1's Seeker rockets%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_SEEKER_MURDER_TAG, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponseeker", _("^BG%s%s^K1 was tagged by ^BG%s^K1's Seeker%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_SEEKER_SUICIDE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponseeker", _("^BG%s^K1 played with tiny Seeker rockets%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_WEAPON_SHOCKWAVE_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponshotgun", _("^BG%s%s^K1 was gunned down by ^BG%s^K1's Shockwave%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_WEAPON_SHOCKWAVE_MURDER_SLAP, 3, 2, "spree_inf s2 s1 s3loc spree_end", "s2 s1", "notify_melee_shotgun", _("^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shockwave%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_SHOTGUN_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponshotgun", _("^BG%s%s^K1 was gunned down by ^BG%s^K1's Shotgun%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_SHOTGUN_MURDER_SLAP, 3, 2, "spree_inf s2 s1 s3loc spree_end", "s2 s1", "notify_melee_shotgun", _("^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shotgun%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_THINKING_WITH_PORTALS, 2, 1, "s1 s2loc spree_lost", "s1", "notify_selfkill", _("^BG%s^K1 is now thinking with portals%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_TUBA_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weapontuba", _("^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Tuba%s%s"), "") \ MSG_INFO_NOTIF(1, INFO_WEAPON_TUBA_SUICIDE, 2, 1, "s1 s2loc spree_lost", "s1", "weapontuba", _("^BG%s^K1 hurt their own ears with the @!#%%'n Tuba%s%s"), "") \ - MSG_INFO_NOTIF(1, INFO_WEAPON_UZI_MURDER_SNIPE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponuzi", _("^BG%s%s^K1 was sniped by ^BG%s^K1's Machine Gun%s%s"), "") \ - MSG_INFO_NOTIF(1, INFO_WEAPON_UZI_MURDER_SPRAY, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponuzi", _("^BG%s%s^K1 was riddled full of holes by ^BG%s^K1's Machine Gun%s%s"), "") + MSG_INFO_NOTIF(1, INFO_WEAPON_VAPORIZER_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponminstanex", _("^BG%s%s^K1 has been sublimated by ^BG%s^K1's Vaporizer%s%s"), "") \ + MSG_INFO_NOTIF(1, INFO_WEAPON_VORTEX_MURDER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponnex", _("^BG%s%s^K1 has been vaporized by ^BG%s^K1's Vortex%s%s"), "") #define MULTITEAM_CENTER2(default,prefix,strnum,flnum,args,cpid,durcnt,normal,gentle) \ MSG_CENTER_NOTIF(default, prefix##RED, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_1, strtoupper(NAME_TEAM_1)), TCR(gentle, COL_TEAM_1, strtoupper(NAME_TEAM_1))) \ @@ -562,6 +567,7 @@ void Send_Notification_WOCOVA( MULTITEAM_CENTER##teams(default,prefix,strnum,flnum,args,cpid,durcnt,normal,gentle) #define MSG_CENTER_NOTIFICATIONS \ + MSG_CENTER_NOTIF(1, CENTER_ALONE, 0, 0, "", NO_CPID, "0 0", _("^F4You are now alone!"), "") \ MSG_CENTER_NOTIF(1, CENTER_ASSAULT_ATTACKING, 0, 0, "", CPID_ASSAULT_ROLE, "0 0", _("^BGYou are attacking!"), "") \ MSG_CENTER_NOTIF(1, CENTER_ASSAULT_DEFENDING, 0, 0, "", CPID_ASSAULT_ROLE, "0 0", _("^BGYou are defending!"), "") \ MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_BEGIN, 0, 0, "", CPID_ROUND, "2 0", _("^F4Begin!"), "") \ @@ -671,14 +677,14 @@ void Send_Notification_WOCOVA( MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_ROUNDSTART, 0, 1, "", CPID_KEYHUNT_OTHER, "1 f1", _("^F4Round will start in ^COUNT"), "") \ MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_SCAN, 0, 1, "", CPID_KEYHUNT_OTHER, "f1 0", _("^BGScanning frequency range..."), "") \ MULTITEAM_CENTER(1, CENTER_KEYHUNT_START_, 4, 0, 0, "", CPID_KEYHUNT, "0 0", _("^BGYou are starting with the ^TC^TT Key"), "") \ - MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_WAIT, 0, 4, "missing_teams", CPID_KEYHUNT_OTHER, "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \ - MSG_CENTER_NOTIF(1, CENTER_MISSING_TEAMS, 0, 4, "missing_teams", CPID_MISSING_TEAMS, "-1 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \ + MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_WAIT, 0, 1, "missing_teams", CPID_KEYHUNT_OTHER, "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \ + MSG_CENTER_NOTIF(1, CENTER_MISSING_TEAMS, 0, 1, "missing_teams", CPID_MISSING_TEAMS, "-1 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \ MSG_CENTER_NOTIF(1, CENTER_MISSING_PLAYERS, 0, 1, "f1", CPID_MISSING_PLAYERS, "-1 0", _("^BGWaiting for %s player(s) to join..."), "") \ MSG_CENTER_NOTIF(1, CENTER_INSTAGIB_FINDAMMO, 0, 0, "", CPID_INSTAGIB_FINDAMMO,"1 9", _("^F4^COUNT^BG left to find some ammo!"), "") \ MSG_CENTER_NOTIF(1, CENTER_INSTAGIB_FINDAMMO_FIRST, 0, 0, "", CPID_INSTAGIB_FINDAMMO,"1 10", _("^BGGet some ammo or you'll be dead in ^F4^COUNT^BG!"), _("^BGGet some ammo! ^F4^COUNT^BG left!")) \ MSG_CENTER_NOTIF(1, CENTER_INSTAGIB_LIVES_REMAINING, 0, 1, "f1", NO_CPID, "0 0", _("^F2Extra lives remaining: ^K1%s"), "") \ MSG_CENTER_NOTIF(1, CENTER_INSTAGIB_SECONDARY, 0, 0, "", NO_CPID, "0 0", _("^BGSecondary fire inflicts no damage!"), "") \ - MSG_CENTER_NOTIF(1, CENTER_MOTD, 1, 0, "s1", CPID_MOTD, "-1 0", _("^BG%s"), "") \ + MSG_CENTER_NOTIF(1, CENTER_MOTD, 1, 0, "s1", CPID_MOTD, "-1 0", "^BG%s", "") \ MSG_CENTER_NOTIF(1, CENTER_NIX_COUNTDOWN, 0, 2, "item_wepname", CPID_NIX, "1 f2", _("^F2^COUNT^BG until weapon change...\nNext weapon: ^F1%s"), "") \ MSG_CENTER_NOTIF(1, CENTER_NIX_NEWWEAPON, 0, 1, "item_wepname", CPID_NIX, "0 0", _("^F2Active weapon: ^F1%s"), "") \ MSG_CENTER_NOTIF(1, CENTER_NADE, 0, 0, "", NO_CPID, "0 0", _("^BGPress ^F2DROPWEAPON^BG again to toss the grenade!"), "") \ @@ -817,8 +823,14 @@ void Send_Notification_WOCOVA( MSG_MULTI_NOTIF(1, MULTI_INSTAGIB_FINDAMMO, ANNCE_NUM_10, NO_MSG, CENTER_INSTAGIB_FINDAMMO_FIRST) \ MSG_MULTI_NOTIF(1, WEAPON_ACCORDEON_MURDER, NO_MSG, INFO_WEAPON_ACCORDEON_MURDER, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_ACCORDEON_SUICIDE, NO_MSG, INFO_WEAPON_ACCORDEON_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_ARC_MURDER, NO_MSG, INFO_WEAPON_ARC_MURDER, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_BLASTER_MURDER, NO_MSG, INFO_WEAPON_BLASTER_MURDER, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_BLASTER_SUICIDE, NO_MSG, INFO_WEAPON_BLASTER_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ MSG_MULTI_NOTIF(1, WEAPON_CRYLINK_MURDER, NO_MSG, INFO_WEAPON_CRYLINK_MURDER, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_CRYLINK_SUICIDE, NO_MSG, INFO_WEAPON_CRYLINK_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_DEVASTATOR_MURDER_DIRECT, NO_MSG, INFO_WEAPON_DEVASTATOR_MURDER_DIRECT, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_DEVASTATOR_MURDER_SPLASH, NO_MSG, INFO_WEAPON_DEVASTATOR_MURDER_SPLASH, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_DEVASTATOR_SUICIDE, NO_MSG, INFO_WEAPON_DEVASTATOR_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_BOLT, NO_MSG, INFO_WEAPON_ELECTRO_MURDER_BOLT, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_COMBO, NO_MSG, INFO_WEAPON_ELECTRO_MURDER_COMBO, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_ELECTRO_MURDER_ORBS, NO_MSG, INFO_WEAPON_ELECTRO_MURDER_ORBS, NO_MSG) \ @@ -836,33 +848,30 @@ void Send_Notification_WOCOVA( MSG_MULTI_NOTIF(1, WEAPON_HOOK_MURDER, NO_MSG, INFO_WEAPON_HOOK_MURDER, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_KLEINBOTTLE_MURDER, NO_MSG, INFO_WEAPON_KLEINBOTTLE_MURDER, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_KLEINBOTTLE_SUICIDE, NO_MSG, INFO_WEAPON_KLEINBOTTLE_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_LASER_MURDER, NO_MSG, INFO_WEAPON_LASER_MURDER, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_LASER_SUICIDE, NO_MSG, INFO_WEAPON_LASER_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_MACHINEGUN_MURDER_SNIPE, NO_MSG, INFO_WEAPON_MACHINEGUN_MURDER_SNIPE, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_MACHINEGUN_MURDER_SPRAY, NO_MSG, INFO_WEAPON_MACHINEGUN_MURDER_SPRAY, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_MINELAYER_MURDER, NO_MSG, INFO_WEAPON_MINELAYER_MURDER, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_MINELAYER_SUICIDE, NO_MSG, INFO_WEAPON_MINELAYER_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_MINSTANEX_MURDER, NO_MSG, INFO_WEAPON_MINSTANEX_MURDER, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_MORTAR_MURDER_BOUNCE, NO_MSG, INFO_WEAPON_MORTAR_MURDER_BOUNCE, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_MORTAR_MURDER_EXPLODE, NO_MSG, INFO_WEAPON_MORTAR_MURDER_EXPLODE, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_MORTAR_SUICIDE_BOUNCE, NO_MSG, INFO_WEAPON_MORTAR_SUICIDE_BOUNCE, CENTER_DEATH_SELF_GENERIC) \ MSG_MULTI_NOTIF(1, WEAPON_MORTAR_SUICIDE_EXPLODE, NO_MSG, INFO_WEAPON_MORTAR_SUICIDE_EXPLODE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_NEX_MURDER, NO_MSG, INFO_WEAPON_NEX_MURDER, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER, NO_MSG, INFO_WEAPON_RIFLE_MURDER, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_HAIL, NO_MSG, INFO_WEAPON_RIFLE_MURDER_HAIL, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_HAIL_PIERCING, NO_MSG, INFO_WEAPON_RIFLE_MURDER_HAIL_PIERCING, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_RIFLE_MURDER_PIERCING, NO_MSG, INFO_WEAPON_RIFLE_MURDER_PIERCING, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_ROCKETLAUNCHER_MURDER_DIRECT, NO_MSG, INFO_WEAPON_ROCKETLAUNCHER_MURDER_DIRECT, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_ROCKETLAUNCHER_MURDER_SPLASH, NO_MSG, INFO_WEAPON_ROCKETLAUNCHER_MURDER_SPLASH, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_ROCKETLAUNCHER_SUICIDE, NO_MSG, INFO_WEAPON_ROCKETLAUNCHER_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ MSG_MULTI_NOTIF(1, WEAPON_SEEKER_MURDER_SPRAY, NO_MSG, INFO_WEAPON_SEEKER_MURDER_SPRAY, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_SEEKER_MURDER_TAG, NO_MSG, INFO_WEAPON_SEEKER_MURDER_TAG, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_SEEKER_SUICIDE, NO_MSG, INFO_WEAPON_SEEKER_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ + MSG_MULTI_NOTIF(1, WEAPON_SHOCKWAVE_MURDER, NO_MSG, INFO_WEAPON_SHOCKWAVE_MURDER, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_SHOCKWAVE_MURDER_SLAP, NO_MSG, INFO_WEAPON_SHOCKWAVE_MURDER_SLAP, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_SHOTGUN_MURDER, NO_MSG, INFO_WEAPON_SHOTGUN_MURDER, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_SHOTGUN_MURDER_SLAP, NO_MSG, INFO_WEAPON_SHOTGUN_MURDER_SLAP, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_THINKING_WITH_PORTALS, NO_MSG, INFO_WEAPON_THINKING_WITH_PORTALS, CENTER_DEATH_SELF_GENERIC) \ MSG_MULTI_NOTIF(1, WEAPON_TUBA_MURDER, NO_MSG, INFO_WEAPON_TUBA_MURDER, NO_MSG) \ MSG_MULTI_NOTIF(1, WEAPON_TUBA_SUICIDE, NO_MSG, INFO_WEAPON_TUBA_SUICIDE, CENTER_DEATH_SELF_GENERIC) \ - MSG_MULTI_NOTIF(1, WEAPON_UZI_MURDER_SNIPE, NO_MSG, INFO_WEAPON_UZI_MURDER_SNIPE, NO_MSG) \ - MSG_MULTI_NOTIF(1, WEAPON_UZI_MURDER_SPRAY, NO_MSG, INFO_WEAPON_UZI_MURDER_SPRAY, NO_MSG) + MSG_MULTI_NOTIF(1, WEAPON_VAPORIZER_MURDER, NO_MSG, INFO_WEAPON_VAPORIZER_MURDER, NO_MSG) \ + MSG_MULTI_NOTIF(1, WEAPON_VORTEX_MURDER, NO_MSG, INFO_WEAPON_VORTEX_MURDER, NO_MSG) #define MULTITEAM_CHOICE2(default,challow,prefix,chtype,optiona,optionb) \ MSG_CHOICE_NOTIF(default, challow, prefix##RED, chtype, optiona##RED, optionb##RED) \ @@ -1017,7 +1026,7 @@ string arg_slot[NOTIF_MAX_ARGS]; ARG_CASE(ARG_CS_SV_HA, "f3race_time", mmssss(f3)) \ ARG_CASE(ARG_CS_SV, "race_col", CCR(((f1 == 1) ? "^F1" : "^F2"))) \ ARG_CASE(ARG_CS_SV, "race_diff", ((f2 > f3) ? sprintf(CCR("^1[+%s]"), mmssss(f2 - f3)) : sprintf(CCR("^2[-%s]"), mmssss(f3 - f2)))) \ - ARG_CASE(ARG_CS, "missing_teams", notif_arg_missing_teams(f1, f2, f3, f4)) \ + ARG_CASE(ARG_CS, "missing_teams", notif_arg_missing_teams(f1)) \ ARG_CASE(ARG_CS, "pass_key", ((((tmp_s = getcommandkey("pass", "+use")) != "pass") && !(strstrofs(tmp_s, "not bound", 0) >= 0)) ? sprintf(CCR(_(" ^F1(Press %s)")), tmp_s) : "")) \ ARG_CASE(ARG_CS, "frag_ping", notif_arg_frag_ping(TRUE, f2)) \ ARG_CASE(ARG_CS, "frag_stats", notif_arg_frag_stats(f2, f3, f4)) \ @@ -1026,7 +1035,7 @@ string arg_slot[NOTIF_MAX_ARGS]; ARG_CASE(ARG_CS_SV, "spree_inf", (autocvar_notification_show_sprees ? notif_arg_spree_inf(1, input, s2, f2) : "")) \ ARG_CASE(ARG_CS_SV, "spree_end", (autocvar_notification_show_sprees ? notif_arg_spree_inf(-1, "", "", f1) : "")) \ ARG_CASE(ARG_CS_SV, "spree_lost", (autocvar_notification_show_sprees ? notif_arg_spree_inf(-2, "", "", f1) : "")) \ - ARG_CASE(ARG_CS_SV, "item_wepname", W_Name(f1)) \ + ARG_CASE(ARG_CS_SV, "item_wepname", WEP_NAME(f1)) \ ARG_CASE(ARG_CS_SV, "item_buffname", sprintf("%s%s", rgb_to_hexcolor(Buff_Color(f1)), Buff_PrettyName(f1))) \ ARG_CASE(ARG_CS_SV, "item_wepammo", (s1 != "" ? sprintf(_(" with %s"), s1) : "")) \ ARG_CASE(ARG_DC, "item_centime", ftos(autocvar_notification_item_centerprinttime)) \ @@ -1062,26 +1071,26 @@ string notif_arg_frag_stats(float fhealth, float farmor, float fping) return sprintf(CCR(_("\n(^F4Dead^BG)%s")), notif_arg_frag_ping(FALSE, fping)); } -string notif_arg_missing_teams(float f1, float f2, float f3, float f4) +string notif_arg_missing_teams(float f1) { return sprintf("%s%s%s%s", - (f1 ? - sprintf("%s%s", Team_ColoredFullName(f1 - 1), ((f2 + f3 + f4) ? ", " : "")) + ((f1 & 1) ? + sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_1), ((f1 & (2 + 4 + 8)) ? ", " : "")) : "" ), - (f2 ? - sprintf("%s%s", Team_ColoredFullName(f2 - 1), ((f3 + f4) ? ", " : "")) + ((f1 & 2) ? + sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_2), ((f1 & (4 + 8)) ? ", " : "")) : "" ), - (f3 ? - sprintf("%s%s", Team_ColoredFullName(f3 - 1), (f4 ? ", " : "")) + ((f1 & 4) ? + sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_3), ((f1 & 8) ? ", " : "")) : "" ), - (f4 ? - Team_ColoredFullName(f4 - 1) + ((f1 & 8) ? + Team_ColoredFullName(NUM_TEAM_4) : "" ) diff --git a/qcsrc/common/stats.qh b/qcsrc/common/stats.qh index f358069e21..d92977f2c5 100644 --- a/qcsrc/common/stats.qh +++ b/qcsrc/common/stats.qh @@ -43,19 +43,19 @@ const float STAT_GAMESTARTTIME = 37; const float STAT_STRENGTH_FINISHED = 38; const float STAT_INVINCIBLE_FINISHED = 39; // 40 empty? -// 41 empty? +const float STAT_ARC_HEAT = 41; const float STAT_PRESSED_KEYS = 42; -const float STAT_ALLOW_OLDNEXBEAM = 43; // this stat could later contain some other bits of info, like, more server-side particle config +const float STAT_ALLOW_OLDVORTEXBEAM = 43; // this stat could later contain some other bits of info, like, more server-side particle config const float STAT_FUEL = 44; const float STAT_NB_METERSTART = 45; const float STAT_SHOTORG = 46; // compressShotOrigin const float STAT_LEADLIMIT = 47; const float STAT_WEAPON_CLIPLOAD = 48; const float STAT_WEAPON_CLIPSIZE = 49; -const float STAT_NEX_CHARGE = 50; +const float STAT_VORTEX_CHARGE = 50; const float STAT_LAST_PICKUP = 51; const float STAT_HUD = 52; -const float STAT_NEX_CHARGEPOOL = 53; +const float STAT_VORTEX_CHARGEPOOL = 53; const float STAT_DAMAGE_DEALT_TOTAL = 54; const float STAT_TYPEHIT_TIME = 55; const float STAT_LAYED_MINES = 56; @@ -86,7 +86,7 @@ const float STAT_NADE_BONUS_TYPE = 80; const float STAT_NADE_BONUS_SCORE = 81; const float STAT_HEALING_ORB = 82; const float STAT_HEALING_ORB_ALPHA = 83; -// 84 empty? +const float STAT_PLASMA = 84; // 85 empty? // 86 empty? // 87 empty? diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index 2c6636a3b9..15cde708ab 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -2560,6 +2560,30 @@ void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t queue_start.FindConnectedComponent_processing = 0; } +#ifdef SVQC +vector combine_to_vector(float x, float y, float z) +{ + vector result; result_x = x; result_y = y; result_z = z; + return result; +} + +vector get_corner_position(entity box, float corner) +{ + switch(corner) + { + case 1: return combine_to_vector(box.absmin_x, box.absmin_y, box.absmin_z); + case 2: return combine_to_vector(box.absmax_x, box.absmin_y, box.absmin_z); + case 3: return combine_to_vector(box.absmin_x, box.absmax_y, box.absmin_z); + case 4: return combine_to_vector(box.absmin_x, box.absmin_y, box.absmax_z); + case 5: return combine_to_vector(box.absmax_x, box.absmax_y, box.absmin_z); + case 6: return combine_to_vector(box.absmin_x, box.absmax_y, box.absmax_z); + case 7: return combine_to_vector(box.absmax_x, box.absmin_y, box.absmax_z); + case 8: return combine_to_vector(box.absmax_x, box.absmax_y, box.absmax_z); + default: return '0 0 0'; + } +} +#endif + // todo: this sucks, lets find a better way to do backtraces? #ifndef MENUQC void backtrace(string msg) diff --git a/qcsrc/common/util.qh b/qcsrc/common/util.qh index 9ddc925795..61b9ad0e2b 100644 --- a/qcsrc/common/util.qh +++ b/qcsrc/common/util.qh @@ -1,4 +1,3 @@ -#define WANT_CONST // commonly used, but better make them macros #define TRUE 1 #define FALSE 0 @@ -365,6 +364,11 @@ typedef entity(entity cur, entity near, entity pass) findNextEntityNearFunction_ typedef float(entity a, entity b, entity pass) isConnectedFunction_t; void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass); +#ifdef SVQC +vector combine_to_vector(float x, float y, float z); +vector get_corner_position(entity box, float corner); +#endif + // expand multiple arguments into one argument by stripping parenthesis #define XPD(...) __VA_ARGS__ @@ -446,3 +450,5 @@ float Mod_Q1BSP_NativeContentsFromSuperContents(float supercontents); // Quadratic splines (bezier) vector bezier_quadratic_getpoint(vector a, vector p, vector b, float t); vector bezier_quadratic_getderivative(vector a, vector p, vector b, float t); + +#define APPEND_TO_STRING(list,sep,add) ((list) = (((list) != "") ? strcat(list, sep, add) : (add))) diff --git a/qcsrc/common/weapons/all.qh b/qcsrc/common/weapons/all.qh new file mode 100644 index 0000000000..a40488f838 --- /dev/null +++ b/qcsrc/common/weapons/all.qh @@ -0,0 +1,27 @@ +// ONLY EVER ADD NEW WEAPONS AT THE END. IF YOU REMOVE ONE, PUT THE LAST ONE ON +// ITS PLACE. THIS IS TO AVOID UNNECESSARY RENUMBERING OF WEAPON IMPULSES. +// IF YOU DISREGARD THIS NOTICE, I'LL KILL YOU WITH THE @!#%'N TUBA + +// core weapons +#include "w_blaster.qc" +#include "w_shotgun.qc" +#include "w_machinegun.qc" +#include "w_mortar.qc" +#include "w_minelayer.qc" +#include "w_electro.qc" +#include "w_crylink.qc" +#include "w_vortex.qc" +#include "w_hagar.qc" +#include "w_devastator.qc" + +// other weapons +#include "w_porto.qc" +#include "w_vaporizer.qc" +#include "w_hook.qc" +#include "w_hlac.qc" +#include "w_tuba.qc" +#include "w_rifle.qc" +#include "w_fireball.qc" +#include "w_seeker.qc" +#include "w_shockwave.qc" +#include "w_arc.qc" diff --git a/qcsrc/common/weapons/calculations.qc b/qcsrc/common/weapons/calculations.qc new file mode 100644 index 0000000000..7bc64e0b53 --- /dev/null +++ b/qcsrc/common/weapons/calculations.qc @@ -0,0 +1,261 @@ +// ============================= +// Explosion Force Calculation +// ============================= + +float explosion_calcpush_getmultiplier(vector explosion_v, vector target_v) +{ + float a; + a = explosion_v * (explosion_v - target_v); + + if(a <= 0) + // target is too fast to be hittable by this + return 0; + + a /= (explosion_v * explosion_v); + // we know we can divide by this, or above a would be == 0 + + return a; +} + +#if 0 +vector explosion_calcpush(vector explosion_v, float explosion_m, vector target_v, float target_m, float elasticity) +{ + // solution of the equations: + // v' = v + a vp // central hit + // m*v' + mp*vp' = m*v + mp*vp // conservation of momentum + // m*v'^2 + mp*vp'^2 = m*v^2 + mp*vp^2 // conservation of energy (ELASTIC hit) + // -> a = 0 // case 1: did not hit + // -> a = 2*mp*(vp^2 - vp.v) / ((m+mp) * vp^2) // case 2: did hit + // // non-elastic hits are somewhere between these two + + // this would be physically correct, but we don't do that + return explosion_v * explosion_calcpush_getmultiplier(explosion_v, target_v) * ( + (1 + elasticity) * ( + explosion_m + ) / ( + target_m + explosion_m + ) + ); // note: this factor is at least 0, at most 2 +} +#endif + +// simplified formula, tuned so that if the target has velocity 0, we get exactly the original force +vector damage_explosion_calcpush(vector explosion_f, vector target_v, float speedfactor) +{ + // if below 1, the formulas make no sense (and would cause superjumps) + if(speedfactor < 1) + return explosion_f; + +#if 0 + float m; + // find m so that + // speedfactor * (1 + e) * m / (1 + m) == 1 + m = 1 / ((1 + 0) * speedfactor - 1); + vector v; + v = explosion_calcpush(explosion_f * speedfactor, m, target_v, 1, 0); + // the factor we then get is: + // 1 + printf("MASS: %f\nv: %v -> %v\nENERGY BEFORE == %f + %f = %f\nENERGY AFTER >= %f\n", + m, + target_v, target_v + v, + target_v * target_v, m * explosion_f * speedfactor * explosion_f * speedfactor, target_v * target_v + m * explosion_f * speedfactor * explosion_f * speedfactor, + (target_v + v) * (target_v + v)); + return v; +#endif + return explosion_f * explosion_calcpush_getmultiplier(explosion_f * speedfactor, target_v); +} + + +// ========================= +// Shot Spread Calculation +// ========================= + +vector cliptoplane(vector v, vector p) +{ + return v - (v * p) * p; +} + +vector solve_cubic_pq(float p, float q) +{ + float D, u, v, a; + D = q*q/4.0 + p*p*p/27.0; + if(D < 0) + { + // irreducibilis + a = 1.0/3.0 * acos(-q/2.0 * sqrt(-27.0/(p*p*p))); + u = sqrt(-4.0/3.0 * p); + // a in range 0..pi/3 + // cos(a) + // cos(a + 2pi/3) + // cos(a + 4pi/3) + return + u * + ( + '1 0 0' * cos(a + 2.0/3.0*M_PI) + + + '0 1 0' * cos(a + 4.0/3.0*M_PI) + + + '0 0 1' * cos(a) + ); + } + else if(D == 0) + { + // simple + if(p == 0) + return '0 0 0'; + u = 3*q/p; + v = -u/2; + if(u >= v) + return '1 1 0' * v + '0 0 1' * u; + else + return '0 1 1' * v + '1 0 0' * u; + } + else + { + // cardano + u = cbrt(-q/2.0 + sqrt(D)); + v = cbrt(-q/2.0 - sqrt(D)); + return '1 1 1' * (u + v); + } +} +vector solve_cubic_abcd(float a, float b, float c, float d) +{ + // y = 3*a*x + b + // x = (y - b) / 3a + float p, q; + vector v; + p = (9*a*c - 3*b*b); + q = (27*a*a*d - 9*a*b*c + 2*b*b*b); + v = solve_cubic_pq(p, q); + v = (v - b * '1 1 1') * (1.0 / (3.0 * a)); + if(a < 0) + v += '1 0 -1' * (v_z - v_x); // swap x, z + return v; +} + +vector findperpendicular(vector v) +{ + vector p; + p_x = v_z; + p_y = -v_x; + p_z = v_y; + return normalize(cliptoplane(p, v)); +} + +vector W_CalculateSpread(vector forward, float spread, float spreadfactor, float spreadstyle) +{ + float sigma; + vector v1 = '0 0 0', v2; + float dx, dy, r; + float sstyle; + spread *= spreadfactor; //g_weaponspreadfactor; + if(spread <= 0) + return forward; + sstyle = spreadstyle; //autocvar_g_projectiles_spread_style; + + if(sstyle == 0) + { + // this is the baseline for the spread value! + // standard deviation: sqrt(2/5) + // density function: sqrt(1-r^2) + return forward + randomvec() * spread; + } + else if(sstyle == 1) + { + // same thing, basically + return normalize(forward + cliptoplane(randomvec() * spread, forward)); + } + else if(sstyle == 2) + { + // circle spread... has at sigma=1 a standard deviation of sqrt(1/2) + sigma = spread * 0.89442719099991587855; // match baseline stddev + v1 = findperpendicular(forward); + v2 = cross(forward, v1); + // random point on unit circle + dx = random() * 2 * M_PI; + dy = sin(dx); + dx = cos(dx); + // radius in our dist function + r = random(); + r = sqrt(r); + return normalize(forward + (v1 * dx + v2 * dy) * r * sigma); + } + else if(sstyle == 3) // gauss 3d + { + sigma = spread * 0.44721359549996; // match baseline stddev + // note: 2D gaussian has sqrt(2) times the stddev of 1D, so this factor is right + v1 = forward; + v1_x += gsl_ran_gaussian(sigma); + v1_y += gsl_ran_gaussian(sigma); + v1_z += gsl_ran_gaussian(sigma); + return v1; + } + else if(sstyle == 4) // gauss 2d + { + sigma = spread * 0.44721359549996; // match baseline stddev + // note: 2D gaussian has sqrt(2) times the stddev of 1D, so this factor is right + v1_x = gsl_ran_gaussian(sigma); + v1_y = gsl_ran_gaussian(sigma); + v1_z = gsl_ran_gaussian(sigma); + return normalize(forward + cliptoplane(v1, forward)); + } + else if(sstyle == 5) // 1-r + { + sigma = spread * 1.154700538379252; // match baseline stddev + v1 = findperpendicular(forward); + v2 = cross(forward, v1); + // random point on unit circle + dx = random() * 2 * M_PI; + dy = sin(dx); + dx = cos(dx); + // radius in our dist function + r = random(); + r = solve_cubic_abcd(-2, 3, 0, -r) * '0 1 0'; + return normalize(forward + (v1 * dx + v2 * dy) * r * sigma); + } + else if(sstyle == 6) // 1-r^2 + { + sigma = spread * 1.095445115010332; // match baseline stddev + v1 = findperpendicular(forward); + v2 = cross(forward, v1); + // random point on unit circle + dx = random() * 2 * M_PI; + dy = sin(dx); + dx = cos(dx); + // radius in our dist function + r = random(); + r = sqrt(1 - r); + r = sqrt(1 - r); + return normalize(forward + (v1 * dx + v2 * dy) * r * sigma); + } + else if(sstyle == 7) // (1-r) (2-r) + { + sigma = spread * 1.224744871391589; // match baseline stddev + v1 = findperpendicular(forward); + v2 = cross(forward, v1); + // random point on unit circle + dx = random() * 2 * M_PI; + dy = sin(dx); + dx = cos(dx); + // radius in our dist function + r = random(); + r = 1 - sqrt(r); + r = 1 - sqrt(r); + return normalize(forward + (v1 * dx + v2 * dy) * r * sigma); + } + else + error("g_projectiles_spread_style must be 0 (sphere), 1 (flattened sphere), 2 (circle), 3 (gauss 3D), 4 (gauss plane), 5 (linear falloff), 6 (quadratic falloff), 7 (stronger falloff)!"); + return '0 0 0'; + /* + * how to derive falloff functions: + * rho(r) := (2-r) * (1-r); + * a : 0; + * b : 1; + * rhor(r) := r * rho(r); + * cr(t) := integrate(rhor(r), r, a, t); + * scr(t) := integrate(rhor(r) * r^2, r, a, t); + * variance : scr(b) / cr(b); + * solve(cr(r) = rand * cr(b), r), programmmode:false; + * sqrt(0.4 / variance), numer; + */ +} diff --git a/qcsrc/common/weapons/calculations.qh b/qcsrc/common/weapons/calculations.qh new file mode 100644 index 0000000000..9a6dd1c547 --- /dev/null +++ b/qcsrc/common/weapons/calculations.qh @@ -0,0 +1,2 @@ +vector damage_explosion_calcpush(vector explosion_f, vector target_v, float speedfactor); +vector W_CalculateSpread(vector forward, float spread, float spreadfactor, float spreadstyle); diff --git a/qcsrc/common/weapons/config.qc b/qcsrc/common/weapons/config.qc new file mode 100644 index 0000000000..2537022f6a --- /dev/null +++ b/qcsrc/common/weapons/config.qc @@ -0,0 +1,55 @@ +// ========================== +// Balance Config Generator +// ========================== + +void W_Config_Queue_Swap(float root, float child, entity pass) +{ + string oldroot = wep_config_queue[root]; + wep_config_queue[root] = wep_config_queue[child]; + wep_config_queue[child] = oldroot; +} + +float W_Config_Queue_Compare(float root, float child, entity pass) +{ + return strcmp(wep_config_queue[root], wep_config_queue[child]); +} + +void Dump_Weapon_Settings(void) +{ + float i, x, totalsettings = 0; + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + { + // step 1: clear the queue + WEP_CONFIG_COUNT = 0; + for(x = 0; x <= MAX_WEP_CONFIG; ++x) + { wep_config_queue[x] = string_null; } + + // step 2: build new queue + WEP_ACTION(i, WR_CONFIG); + + // step 3: sort queue + heapsort(WEP_CONFIG_COUNT, W_Config_Queue_Swap, W_Config_Queue_Compare, world); + + // step 4: write queue + WEP_CONFIG_WRITETOFILE(sprintf( + "// {{{ #%d: %s%s\n", + i, + WEP_NAME(i), + (((get_weaponinfo(i)).spawnflags & WEP_FLAG_MUTATORBLOCKED) ? " (MUTATOR WEAPON)" : "") + )) + for(x = 0; x <= WEP_CONFIG_COUNT; ++x) { WEP_CONFIG_WRITETOFILE(wep_config_queue[x]) } + WEP_CONFIG_WRITETOFILE("// }}}\n") + + // step 5: debug info + print(sprintf("#%d: %s: %d settings...\n", i, WEP_NAME(i), WEP_CONFIG_COUNT)); + totalsettings += WEP_CONFIG_COUNT; + } + + // clear queue now that we're finished + WEP_CONFIG_COUNT = 0; + for(x = 0; x <= MAX_WEP_CONFIG; ++x) + { wep_config_queue[x] = string_null; } + + // extra information + print(sprintf("Totals: %d weapons, %d settings\n", (i - 1), totalsettings)); +} diff --git a/qcsrc/common/weapons/config.qh b/qcsrc/common/weapons/config.qh new file mode 100644 index 0000000000..6a04893148 --- /dev/null +++ b/qcsrc/common/weapons/config.qh @@ -0,0 +1,44 @@ +// ========================== +// Balance Config Generator +// ========================== + +void Dump_Weapon_Settings(void); +float wep_config_file; +float wep_config_alsoprint; + +#define MAX_WEP_CONFIG 256 +float WEP_CONFIG_COUNT; +string wep_config_queue[MAX_WEP_CONFIG]; + +#define WEP_CONFIG_QUEUE(a) { \ + wep_config_queue[WEP_CONFIG_COUNT] = a; \ + ++WEP_CONFIG_COUNT; } + +#define WEP_CONFIG_WRITETOFILE(a) { \ + fputs(wep_config_file, a); \ + if(wep_config_alsoprint) { print(a); } } + +#define WEP_CONFIG_WRITE_CVARS_NONE(wepname,name) \ + { WEP_CONFIG_QUEUE( \ + sprintf("set g_balance_%s_%s %g\n", #wepname, #name, \ + cvar(sprintf("g_balance_%s_%s", #wepname, #name)))) } + +#define WEP_CONFIG_WRITE_CVARS_PRI(wepname,name) WEP_CONFIG_WRITE_CVARS_NONE(wepname, primary_##name) +#define WEP_CONFIG_WRITE_CVARS_SEC(wepname,name) WEP_CONFIG_WRITE_CVARS_NONE(wepname, secondary_##name) +#define WEP_CONFIG_WRITE_CVARS_BOTH(wepname,name) \ + WEP_CONFIG_WRITE_CVARS_PRI(wepname, name) \ + WEP_CONFIG_WRITE_CVARS_SEC(wepname, name) + +#define WEP_CONFIG_WRITE_CVARS(wepid,wepname,mode,name) WEP_CONFIG_WRITE_CVARS_##mode(wepname, name) + +#define WEP_CONFIG_WRITE_PROPS_string(wepname,name) \ + { WEP_CONFIG_QUEUE( \ + sprintf("set g_balance_%s_%s \"%s\"\n", #wepname, #name, \ + cvar_string(sprintf("g_balance_%s_%s", #wepname, #name)))) } + +#define WEP_CONFIG_WRITE_PROPS_float(wepname,name) \ + { WEP_CONFIG_QUEUE( \ + sprintf("set g_balance_%s_%s %g\n", #wepname, #name, \ + cvar(sprintf("g_balance_%s_%s", #wepname, #name)))) } + +#define WEP_CONFIG_WRITE_PROPS(wepid,wepname,type,prop,name) WEP_CONFIG_WRITE_PROPS_##type(wepname,name) diff --git a/qcsrc/common/weapons/w_arc.qc b/qcsrc/common/weapons/w_arc.qc new file mode 100644 index 0000000000..83cd797553 --- /dev/null +++ b/qcsrc/common/weapons/w_arc.qc @@ -0,0 +1,1546 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ ARC, +/* function */ W_Arc, +/* ammotype */ ammo_cells, +/* impulse */ 3, +/* flags */ WEP_FLAG_NORMAL, +/* rating */ BOT_PICKUP_RATING_HIGH, +/* color */ '1 1 1', +/* modelname */ "arc", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairhlac 0.7", +/* wepimg */ "weaponarc", +/* refname */ "arc", +/* wepname */ _("Arc") +); + +#define ARC_SETTINGS(w_cvar,w_prop) ARC_SETTINGS_LIST(w_cvar, w_prop, ARC, arc) +#define ARC_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, NONE, beam_ammo) \ + w_cvar(id, sn, NONE, beam_animtime) \ + w_cvar(id, sn, NONE, beam_botaimspeed) \ + w_cvar(id, sn, NONE, beam_botaimlifetime) \ + w_cvar(id, sn, NONE, beam_damage) \ + w_cvar(id, sn, NONE, beam_degreespersegment) \ + w_cvar(id, sn, NONE, beam_distancepersegment) \ + w_cvar(id, sn, NONE, beam_falloff_halflifedist) \ + w_cvar(id, sn, NONE, beam_falloff_maxdist) \ + w_cvar(id, sn, NONE, beam_falloff_mindist) \ + w_cvar(id, sn, NONE, beam_force) \ + w_cvar(id, sn, NONE, beam_healing_amax) \ + w_cvar(id, sn, NONE, beam_healing_aps) \ + w_cvar(id, sn, NONE, beam_healing_hmax) \ + w_cvar(id, sn, NONE, beam_healing_hps) \ + w_cvar(id, sn, NONE, beam_maxangle) \ + w_cvar(id, sn, NONE, beam_nonplayerdamage) \ + w_cvar(id, sn, NONE, beam_range) \ + w_cvar(id, sn, NONE, beam_refire) \ + w_cvar(id, sn, NONE, beam_returnspeed) \ + w_cvar(id, sn, NONE, beam_tightness) \ + w_cvar(id, sn, NONE, burst_ammo) \ + w_cvar(id, sn, NONE, burst_damage) \ + w_cvar(id, sn, NONE, burst_healing_aps) \ + w_cvar(id, sn, NONE, burst_healing_hps) \ + w_cvar(id, sn, NONE, overheat_max)/* maximum heat before jamming */ \ + w_cvar(id, sn, NONE, overheat_min)/* minimum heat to wait for cooldown */ \ + w_cvar(id, sn, NONE, beam_heat) /* heat increase per second (primary) */ \ + w_cvar(id, sn, NONE, burst_heat) /* heat increase per second (secondary) */ \ + w_cvar(id, sn, NONE, cooldown) /* heat decrease per second when resting */ \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifndef MENUQC +#define ARC_MAX_SEGMENTS 20 +vector arc_shotorigin[4]; +.vector beam_start; +.vector beam_dir; +.vector beam_wantdir; +.float beam_type; + +#define ARC_BT_MISS 0x00 +#define ARC_BT_WALL 0x01 +#define ARC_BT_HEAL 0x02 +#define ARC_BT_HIT 0x03 +#define ARC_BT_BURST_MISS 0x10 +#define ARC_BT_BURST_WALL 0x11 +#define ARC_BT_BURST_HEAL 0x12 +#define ARC_BT_BURST_HIT 0x13 +#define ARC_BT_BURSTMASK 0x10 + +#define ARC_SF_SETTINGS 1 +#define ARC_SF_START 2 +#define ARC_SF_WANTDIR 4 +#define ARC_SF_BEAMDIR 8 +#define ARC_SF_BEAMTYPE 16 +#define ARC_SF_LOCALMASK 14 +#endif +#ifdef SVQC +ARC_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +.entity arc_beam; +.float arc_BUTTON_ATCK_prev; // for better animation control +.float beam_prev; +.float beam_initialized; +.float beam_bursting; +.float beam_teleporttime; +.float beam_heat; // (beam) amount of heat produced +.float arc_overheat; // (dropped arc/player) time during which it's too hot +.float arc_cooldown; // (dropped arc/player) cooling speed +.float arc_heat_percent; // (player) arc heat in [0,1] (stat) +.float arc_smoke_sound; +#endif +#ifdef CSQC +void Ent_ReadArcBeam(float isnew); + +.vector beam_color; +.float beam_alpha; +.float beam_thickness; +.float beam_traileffect; +.float beam_hiteffect; +.float beam_hitlight[4]; // 0: radius, 123: rgb +.float beam_muzzleeffect; +.float beam_muzzlelight[4]; // 0: radius, 123: rgb +.string beam_image; + +.entity beam_muzzleentity; + +.float beam_degreespersegment; +.float beam_distancepersegment; +.float beam_usevieworigin; +.float beam_initialized; +.float beam_maxangle; +.float beam_range; +.float beam_returnspeed; +.float beam_tightness; +.vector beam_shotorigin; + +entity Draw_ArcBeam_callback_entity; +float Draw_ArcBeam_callback_last_thickness; +vector Draw_ArcBeam_callback_last_top; // NOTE: in same coordinate system as player. +vector Draw_ArcBeam_callback_last_bottom; // NOTE: in same coordinate system as player. +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_arc(void) { weapon_defaultspawnfunc(WEP_ARC); } + +float W_Arc_Beam_Send(entity to, float sf) +{ + WriteByte(MSG_ENTITY, ENT_CLIENT_ARC_BEAM); + + // Truncate information when this beam is displayed to the owner client + // - The owner client has no use for beam start position or directions, + // it always figures this information out for itself with csqc code. + // - Spectating the owner also truncates this information. + float drawlocal = ((to == self.owner) || ((to.enemy == self.owner) && IS_SPEC(to))); + if(drawlocal) { sf &= ~ARC_SF_LOCALMASK; } + + WriteByte(MSG_ENTITY, sf); + + if(sf & ARC_SF_SETTINGS) // settings information + { + WriteShort(MSG_ENTITY, WEP_CVAR(arc, beam_degreespersegment)); + WriteShort(MSG_ENTITY, WEP_CVAR(arc, beam_distancepersegment)); + WriteShort(MSG_ENTITY, WEP_CVAR(arc, beam_maxangle)); + WriteCoord(MSG_ENTITY, WEP_CVAR(arc, beam_range)); + WriteShort(MSG_ENTITY, WEP_CVAR(arc, beam_returnspeed)); + WriteByte(MSG_ENTITY, WEP_CVAR(arc, beam_tightness) * 10); + + WriteByte(MSG_ENTITY, drawlocal); + } + if(sf & ARC_SF_START) // starting location + { + WriteCoord(MSG_ENTITY, self.beam_start_x); + WriteCoord(MSG_ENTITY, self.beam_start_y); + WriteCoord(MSG_ENTITY, self.beam_start_z); + } + if(sf & ARC_SF_WANTDIR) // want/aim direction + { + WriteCoord(MSG_ENTITY, self.beam_wantdir_x); + WriteCoord(MSG_ENTITY, self.beam_wantdir_y); + WriteCoord(MSG_ENTITY, self.beam_wantdir_z); + } + if(sf & ARC_SF_BEAMDIR) // beam direction + { + WriteCoord(MSG_ENTITY, self.beam_dir_x); + WriteCoord(MSG_ENTITY, self.beam_dir_y); + WriteCoord(MSG_ENTITY, self.beam_dir_z); + } + if(sf & ARC_SF_BEAMTYPE) // beam type + { + WriteByte(MSG_ENTITY, self.beam_type); + } + + return TRUE; +} + +void Reset_ArcBeam(entity player, vector forward) +{ + if (!player.arc_beam) { + return; + } + player.arc_beam.beam_dir = forward; + player.arc_beam.beam_teleporttime = time; +} + +float Arc_GetHeat_Percent(entity player) +{ + if ( WEP_CVAR(arc, overheat_max) <= 0 || WEP_CVAR(arc, overheat_max) <= 0 ) + { + player.arc_overheat = 0; + return 0; + } + + if ( player.arc_beam ) + return player.arc_beam.beam_heat/WEP_CVAR(arc, overheat_max); + + if ( player.arc_overheat > time ) + { + return (player.arc_overheat-time) / WEP_CVAR(arc, overheat_max) + * player.arc_cooldown; + } + + return 0; +} +void Arc_Player_SetHeat(entity player) +{ + player.arc_heat_percent = Arc_GetHeat_Percent(player); + //dprint("Heat: ",ftos(player.arc_heat_percent*100),"%\n"); +} + +void W_Arc_Beam_Think(void) +{ + if(self != self.owner.arc_beam) + { + remove(self); + return; + } + + + float burst = 0; + if( self.owner.BUTTON_ATCK2 || self.beam_bursting) + { + if(!self.beam_bursting) + self.beam_bursting = TRUE; + burst = ARC_BT_BURSTMASK; + } + + if( + !IS_PLAYER(self.owner) + || + (self.owner.WEP_AMMO(ARC) <= 0 && !(self.owner.items & IT_UNLIMITED_WEAPON_AMMO)) + || + self.owner.deadflag != DEAD_NO + || + (!self.owner.BUTTON_ATCK && !burst ) + || + self.owner.frozen + || + (WEP_CVAR(arc, overheat_max) > 0 && self.beam_heat >= WEP_CVAR(arc, overheat_max)) + ) + { + if ( WEP_CVAR(arc, cooldown) > 0 ) + { + float cooldown_speed = 0; + if ( self.beam_heat > WEP_CVAR(arc, overheat_min) && WEP_CVAR(arc, cooldown) > 0 ) + { + cooldown_speed = WEP_CVAR(arc, cooldown); + } + else if ( !burst ) + { + cooldown_speed = self.beam_heat / WEP_CVAR(arc, beam_refire); + } + + if ( cooldown_speed ) + { + self.owner.arc_overheat = time + self.beam_heat / cooldown_speed; + self.owner.arc_cooldown = cooldown_speed; + } + + if ( WEP_CVAR(arc, overheat_max) > 0 && self.beam_heat >= WEP_CVAR(arc, overheat_max) ) + { + pointparticles( particleeffectnum("arc_overheat"), + self.beam_start, self.beam_wantdir, 1 ); + sound(self, CH_WEAPON_A, "weapons/strength_fire.wav", VOL_BASE, ATTN_NORM); + } + } + + if(self == self.owner.arc_beam) { self.owner.arc_beam = world; } + entity oldself = self; + self = self.owner; + if(!WEP_ACTION(WEP_ARC, WR_CHECKAMMO1) && !WEP_ACTION(WEP_ARC, WR_CHECKAMMO2)) + { + // note: this doesn't force the switch + W_SwitchToOtherWeapon(self); + } + self = oldself; + remove(self); + return; + } + + // decrease ammo + float coefficient = frametime; + if(!(self.owner.items & IT_UNLIMITED_WEAPON_AMMO)) + { + float rootammo; + if(burst) + { rootammo = WEP_CVAR(arc, burst_ammo); } + else + { rootammo = WEP_CVAR(arc, beam_ammo); } + + if(rootammo) + { + coefficient = min(coefficient, self.owner.WEP_AMMO(ARC) / rootammo); + self.owner.WEP_AMMO(ARC) = max(0, self.owner.WEP_AMMO(ARC) - (rootammo * frametime)); + } + } + float heat_speed = burst ? WEP_CVAR(arc, burst_heat) : WEP_CVAR(arc, beam_heat); + self.beam_heat = min( WEP_CVAR(arc, overheat_max), self.beam_heat + heat_speed*frametime ); + + makevectors(self.owner.v_angle); + + W_SetupShot_Range( + self.owner, + TRUE, + 0, + "", + 0, + WEP_CVAR(arc, beam_damage) * coefficient, + WEP_CVAR(arc, beam_range) + ); + + // After teleport, "lock" the beam until the teleport is confirmed. + if (time < self.beam_teleporttime + ANTILAG_LATENCY(self.owner)) { + w_shotdir = self.beam_dir; + } + + // network information: shot origin and want/aim direction + if(self.beam_start != w_shotorg) + { + self.SendFlags |= ARC_SF_START; + self.beam_start = w_shotorg; + } + if(self.beam_wantdir != w_shotdir) + { + self.SendFlags |= ARC_SF_WANTDIR; + self.beam_wantdir = w_shotdir; + } + + if(!self.beam_initialized) + { + self.beam_dir = w_shotdir; + self.beam_initialized = TRUE; + } + + // WEAPONTODO: Detect player velocity so that the beam curves when moving too + // idea: blend together self.beam_dir with the inverted direction the player is moving in + // might have to make some special accomodation so that it only uses view_right and view_up + + // note that if we do this, it'll always be corrected to a maximum angle by beam_maxangle handling + + float segments; + if(self.beam_dir != w_shotdir) + { + // calculate how much we're going to move the end of the beam to the want position + // WEAPONTODO (server and client): + // blendfactor never actually becomes 0 in this situation, which is a problem + // regarding precision... this means that self.beam_dir and w_shotdir approach + // eachother, however they never actually become the same value with this method. + // Perhaps we should do some form of rounding/snapping? + float angle = vlen(w_shotdir - self.beam_dir) * RAD2DEG; + if(angle && (angle > WEP_CVAR(arc, beam_maxangle))) + { + // if the angle is greater than maxangle, force the blendfactor to make this the maximum factor + float blendfactor = bound( + 0, + (1 - (WEP_CVAR(arc, beam_returnspeed) * frametime)), + min(WEP_CVAR(arc, beam_maxangle) / angle, 1) + ); + self.beam_dir = normalize((w_shotdir * (1 - blendfactor)) + (self.beam_dir * blendfactor)); + } + else + { + // the radius is not too far yet, no worries :D + float blendfactor = bound( + 0, + (1 - (WEP_CVAR(arc, beam_returnspeed) * frametime)), + 1 + ); + self.beam_dir = normalize((w_shotdir * (1 - blendfactor)) + (self.beam_dir * blendfactor)); + } + + // network information: beam direction + self.SendFlags |= ARC_SF_BEAMDIR; + + // calculate how many segments are needed + float max_allowed_segments; + + if(WEP_CVAR(arc, beam_distancepersegment)) + { + max_allowed_segments = min( + ARC_MAX_SEGMENTS, + 1 + (vlen(w_shotdir / WEP_CVAR(arc, beam_distancepersegment))) + ); + } + else { max_allowed_segments = ARC_MAX_SEGMENTS; } + + if(WEP_CVAR(arc, beam_degreespersegment)) + { + segments = bound( + 1, + ( + min( + angle, + WEP_CVAR(arc, beam_maxangle) + ) + / + WEP_CVAR(arc, beam_degreespersegment) + ), + max_allowed_segments + ); + } + else { segments = 1; } + } + else { segments = 1; } + + vector beam_endpos = (w_shotorg + (self.beam_dir * WEP_CVAR(arc, beam_range))); + vector beam_controlpoint = w_shotorg + w_shotdir * (WEP_CVAR(arc, beam_range) * (1 - WEP_CVAR(arc, beam_tightness))); + + float i; + float new_beam_type = 0; + vector last_origin = w_shotorg; + for(i = 1; i <= segments; ++i) + { + // WEAPONTODO (client): + // In order to do nice fading and pointing on the starting segment, we must always + // have that drawn as a separate triangle... However, that is difficult to do when + // keeping in mind the above problems and also optimizing the amount of segments + // drawn on screen at any given time. (Automatic beam quality scaling, essentially) + + vector new_origin = bezier_quadratic_getpoint( + w_shotorg, + beam_controlpoint, + beam_endpos, + i / segments); + vector new_dir = normalize(new_origin - last_origin); + + WarpZone_traceline_antilag( + self.owner, + last_origin, + new_origin, + MOVE_NORMAL, + self.owner, + ANTILAG_LATENCY(self.owner) + ); + + // Do all the transforms for warpzones right now, as we already + // "are" in the post-trace system (if we hit a player, that's + // always BEHIND the last passed wz). + last_origin = trace_endpos; + w_shotorg = WarpZone_TransformOrigin(WarpZone_trace_transform, w_shotorg); + beam_controlpoint = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_controlpoint); + beam_endpos = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_endpos); + new_dir = WarpZone_TransformVelocity(WarpZone_trace_transform, new_dir); + + float is_player = ( + trace_ent.classname == "player" + || + trace_ent.classname == "body" + || + (trace_ent.flags & FL_MONSTER) + ); + + if(trace_ent && trace_ent.takedamage && (is_player || WEP_CVAR(arc, beam_nonplayerdamage))) + { + // calculate our own hit origin as trace_endpos tends to jump around annoyingly (to player origin?) + // NO. trace_endpos should be just fine. If not, + // that's an engine bug that needs proper debugging. + vector hitorigin = trace_endpos; + + float falloff = ExponentialFalloff( + WEP_CVAR(arc, beam_falloff_mindist), + WEP_CVAR(arc, beam_falloff_maxdist), + WEP_CVAR(arc, beam_falloff_halflifedist), + vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, hitorigin) - w_shotorg) + ); + + if(is_player && SAME_TEAM(self.owner, trace_ent)) + { + float roothealth, rootarmor; + if(burst) + { + roothealth = WEP_CVAR(arc, burst_healing_hps); + rootarmor = WEP_CVAR(arc, burst_healing_aps); + } + else + { + roothealth = WEP_CVAR(arc, beam_healing_hps); + rootarmor = WEP_CVAR(arc, beam_healing_aps); + } + + if(trace_ent.health <= WEP_CVAR(arc, beam_healing_hmax) && roothealth) + { + trace_ent.health = min( + trace_ent.health + (roothealth * coefficient), + WEP_CVAR(arc, beam_healing_hmax) + ); + } + if(trace_ent.armorvalue <= WEP_CVAR(arc, beam_healing_amax) && rootarmor) + { + trace_ent.armorvalue = min( + trace_ent.armorvalue + (rootarmor * coefficient), + WEP_CVAR(arc, beam_healing_amax) + ); + } + + // stop rot, set visual effect + if(roothealth || rootarmor) + { + trace_ent.pauserothealth_finished = max( + trace_ent.pauserothealth_finished, + time + autocvar_g_balance_pause_health_rot + ); + trace_ent.pauserotarmor_finished = max( + trace_ent.pauserotarmor_finished, + time + autocvar_g_balance_pause_armor_rot + ); + new_beam_type = ARC_BT_HEAL; + } + } + else + { + float rootdamage; + if(is_player) + { + if(burst) + { rootdamage = WEP_CVAR(arc, burst_damage); } + else + { rootdamage = WEP_CVAR(arc, beam_damage); } + } + else + { rootdamage = WEP_CVAR(arc, beam_nonplayerdamage); } + + if(accuracy_isgooddamage(self.owner, trace_ent)) + { + accuracy_add( + self.owner, + WEP_ARC, + 0, + rootdamage * coefficient * falloff + ); + } + + Damage( + trace_ent, + self.owner, + self.owner, + rootdamage * coefficient * falloff, + WEP_ARC, + hitorigin, + WEP_CVAR(arc, beam_force) * new_dir * coefficient * falloff + ); + + new_beam_type = ARC_BT_HIT; + } + break; + } + else if(trace_fraction != 1) + { + // we collided with geometry + new_beam_type = ARC_BT_WALL; + break; + } + } + + // te_explosion(trace_endpos); + + // if we're bursting, use burst visual effects + new_beam_type |= burst; + + // network information: beam type + if(new_beam_type != self.beam_type) + { + self.SendFlags |= ARC_SF_BEAMTYPE; + self.beam_type = new_beam_type; + } + + self.owner.beam_prev = time; + self.nextthink = time; +} + +void W_Arc_Beam(float burst) +{ + + // only play fire sound if 1 sec has passed since player let go the fire button + if(time - self.beam_prev > 1) + { + sound(self, CH_WEAPON_A, "weapons/lgbeam_fire.wav", VOL_BASE, ATTN_NORM); + } + + entity beam = self.arc_beam = spawn(); + beam.classname = "W_Arc_Beam"; + beam.solid = SOLID_NOT; + beam.think = W_Arc_Beam_Think; + beam.owner = self; + beam.movetype = MOVETYPE_NONE; + beam.bot_dodge = TRUE; + beam.bot_dodgerating = WEP_CVAR(arc, beam_damage); + beam.beam_bursting = burst; + Net_LinkEntity(beam, FALSE, 0, W_Arc_Beam_Send); + + entity oldself = self; + self = beam; + self.think(); + self = oldself; +} + +void Arc_Smoke() +{ + makevectors(self.v_angle); + W_SetupShot_Range(self,TRUE,0,"",0,0,0); + + vector smoke_origin = w_shotorg + self.velocity*frametime; + if ( self.arc_overheat > time ) + { + if ( random() < self.arc_heat_percent ) + pointparticles( particleeffectnum("arc_smoke"), smoke_origin, '0 0 0', 1 ); + if ( self.BUTTON_ATCK || self.BUTTON_ATCK2 ) + { + pointparticles( particleeffectnum("arc_overheat_fire"), smoke_origin, w_shotdir, 1 ); + if ( !self.arc_smoke_sound ) + { + self.arc_smoke_sound = 1; + sound(self, CH_SHOTS_SINGLE, "weapons/gauntletbeam_fly.wav", VOL_BASE, ATTN_NORM); + } + } + } + else if ( self.arc_beam && WEP_CVAR(arc, overheat_max) > 0 && + self.arc_beam.beam_heat > WEP_CVAR(arc, overheat_min) ) + { + if ( random() < (self.arc_beam.beam_heat-WEP_CVAR(arc, overheat_min)) / + ( WEP_CVAR(arc, overheat_max)-WEP_CVAR(arc, overheat_min) ) ) + pointparticles( particleeffectnum("arc_smoke"), smoke_origin, '0 0 0', 1 ); + } + + if ( self.arc_smoke_sound && ( self.arc_overheat <= time || + !( self.BUTTON_ATCK || self.BUTTON_ATCK2 ) ) || self.switchweapon != WEP_ARC ) + { + self.arc_smoke_sound = 0; + sound(self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM); + } +} + +float W_Arc(float req) +{ + switch(req) + { + case WR_AIM: + { + if(WEP_CVAR(arc, beam_botaimspeed)) + { + self.BUTTON_ATCK = bot_aim( + WEP_CVAR(arc, beam_botaimspeed), + 0, + WEP_CVAR(arc, beam_botaimlifetime), + FALSE + ); + } + else + { + self.BUTTON_ATCK = bot_aim( + 1000000, + 0, + 0.001, + FALSE + ); + } + return TRUE; + } + case WR_THINK: + { + Arc_Player_SetHeat(self); + Arc_Smoke(); + + if ( self.arc_overheat <= time ) + if(self.BUTTON_ATCK || self.BUTTON_ATCK2 || self.arc_beam.beam_bursting ) + { + + if(self.arc_BUTTON_ATCK_prev) + { + #if 0 + if(self.animstate_startframe == self.anim_shoot_x && self.animstate_numframes == self.anim_shoot_y) + weapon_thinkf(WFRAME_DONTCHANGE, autocvar_g_balance_arc_primary_animtime, w_ready); + else + #endif + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready); + } + + if((!self.arc_beam) || wasfreed(self.arc_beam)) + { + if(weapon_prepareattack(!!self.BUTTON_ATCK2, 0)) + { + W_Arc_Beam(!!self.BUTTON_ATCK2); + + if(!self.arc_BUTTON_ATCK_prev) + { + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready); + self.arc_BUTTON_ATCK_prev = 1; + } + } + } + + return TRUE; + } + + if(self.arc_BUTTON_ATCK_prev != 0) + { + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready); + ATTACK_FINISHED(self) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor(); + } + self.arc_BUTTON_ATCK_prev = 0; + + #if 0 + if(self.BUTTON_ATCK2) + if(weapon_prepareattack(1, autocvar_g_balance_arc_secondary_refire)) + { + W_Arc_Attack2(); + self.arc_count = autocvar_g_balance_arc_secondary_count; + weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_arc_secondary_animtime, w_arc_checkattack); + self.arc_secondarytime = time + autocvar_g_balance_arc_secondary_refire2 * W_WeaponRateFactor(); + } + #endif + + return TRUE; + } + case WR_INIT: + { + precache_model("models/weapons/g_arc.md3"); + precache_model("models/weapons/v_arc.md3"); + precache_model("models/weapons/h_arc.iqm"); + precache_sound("weapons/lgbeam_fire.wav"); + precache_sound("weapons/gauntletbeam_fly.wav"); + precache_sound("weapons/strength_fire.wav"); + if(!arc_shotorigin[0]) + { + arc_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 1); + arc_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 2); + arc_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 3); + arc_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 4); + } + ARC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_CHECKAMMO1: + { + return ((!WEP_CVAR(arc, beam_ammo)) || (self.WEP_AMMO(ARC) > 0)); + } + case WR_CHECKAMMO2: + { + return WEP_CVAR(arc, overheat_max) > 0 && + ((!WEP_CVAR(arc, burst_ammo)) || (self.WEP_AMMO(ARC) > 0)); + } + case WR_CONFIG: + { + ARC_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_KILLMESSAGE: + { + return WEAPON_ARC_MURDER; + } + case WR_DROP: + { + weapon_dropevent_item.arc_overheat = self.arc_overheat; + weapon_dropevent_item.arc_cooldown = self.arc_cooldown; + self.arc_overheat = 0; + self.arc_cooldown = 0; + return TRUE; + } + case WR_PICKUP: + { + if ( !client_hasweapon(self, WEP_ARC, FALSE, FALSE) && + weapon_dropevent_item.arc_overheat > time ) + { + self.arc_overheat = weapon_dropevent_item.arc_overheat; + self.arc_cooldown = weapon_dropevent_item.arc_cooldown; + } + return TRUE; + } + } + return FALSE; +} +#endif +#ifdef CSQC +void Draw_ArcBeam_callback(vector start, vector hit, vector end) +{ + entity beam = Draw_ArcBeam_callback_entity; + vector transformed_view_org; + transformed_view_org = WarpZone_TransformOrigin(WarpZone_trace_transform, view_origin); + + // Thickdir shall be perpendicular to the beam and to the view-to-beam direction (WEAPONTODO: WHY) + // WEAPONTODO: Wouldn't it be better to be perpendicular to the beam and to the view FORWARD direction? + vector thickdir = normalize(cross(normalize(start - hit), transformed_view_org - start)); + + vector hitorigin; + + // draw segment + #if 0 + if(trace_fraction != 1) + { + // calculate our own hit origin as trace_endpos tends to jump around annoyingly (to player origin?) + hitorigin = start + (Draw_ArcBeam_callback_new_dir * Draw_ArcBeam_callback_segmentdist * trace_fraction); + hitorigin = WarpZone_TransformOrigin(WarpZone_trace_transform, hitorigin); + } + else + { + hitorigin = hit; + } + #else + hitorigin = hit; + #endif + + // decide upon thickness + float thickness = beam.beam_thickness; + + // draw primary beam render + vector top = hitorigin + (thickdir * thickness); + vector bottom = hitorigin - (thickdir * thickness); + + vector last_top = WarpZone_TransformOrigin(WarpZone_trace_transform, Draw_ArcBeam_callback_last_top); + vector last_bottom = WarpZone_TransformOrigin(WarpZone_trace_transform, Draw_ArcBeam_callback_last_bottom); + + R_BeginPolygon(beam.beam_image, DRAWFLAG_NORMAL); // DRAWFLAG_ADDITIVE + R_PolygonVertex( + top, + '0 0.5 0' + ('0 0.5 0' * (thickness / beam.beam_thickness)), + beam.beam_color, + beam.beam_alpha + ); + R_PolygonVertex( + last_top, + '0 0.5 0' + ('0 0.5 0' * (Draw_ArcBeam_callback_last_thickness / beam.beam_thickness)), + beam.beam_color, + beam.beam_alpha + ); + R_PolygonVertex( + last_bottom, + '0 0.5 0' * (1 - (Draw_ArcBeam_callback_last_thickness / beam.beam_thickness)), + beam.beam_color, + beam.beam_alpha + ); + R_PolygonVertex( + bottom, + '0 0.5 0' * (1 - (thickness / beam.beam_thickness)), + beam.beam_color, + beam.beam_alpha + ); + R_EndPolygon(); + + // draw trailing particles + // NOTES: + // - Don't use spammy particle counts here, use a FEW small particles around the beam + // - We're not using WarpZone_TrailParticles here because we will handle warpzones ourselves. + if(beam.beam_traileffect) + { + trailparticles(beam, beam.beam_traileffect, start, hitorigin); + } + + // set up for the next + Draw_ArcBeam_callback_last_thickness = thickness; + Draw_ArcBeam_callback_last_top = WarpZone_UnTransformOrigin(WarpZone_trace_transform, top); + Draw_ArcBeam_callback_last_bottom = WarpZone_UnTransformOrigin(WarpZone_trace_transform, bottom); +} + +void Reset_ArcBeam(void) +{ + entity e; + for (e = world; (e = findfloat(e, beam_usevieworigin, 1)); ) { + e.beam_initialized = FALSE; + } + for (e = world; (e = findfloat(e, beam_usevieworigin, 2)); ) { + e.beam_initialized = FALSE; + } +} + +void Draw_ArcBeam(void) +{ + float dt = time - self.move_time; + self.move_time = time; + if(dt <= 0) { return; } + + if(!self.beam_usevieworigin) + { + InterpolateOrigin_Do(); + } + + // origin = beam starting origin + // v_angle = wanted/aim direction + // angles = current direction of beam + + vector start_pos; + vector wantdir; //= view_forward; + vector beamdir; //= self.beam_dir; + + float segments; + if(self.beam_usevieworigin) + { + // WEAPONTODO: + // Currently we have to replicate nearly the same method of figuring + // out the shotdir that the server does... Ideally in the future we + // should be able to acquire this from a generalized function built + // into a weapon system for client code. + + // find where we are aiming + makevectors(warpzone_save_view_angles); + vector forward = v_forward; + vector right = v_right; + vector up = v_up; + + // decide upon start position + if(self.beam_usevieworigin == 2) + { start_pos = warpzone_save_view_origin; } + else + { start_pos = self.origin; } + + // trace forward with an estimation + WarpZone_TraceLine( + start_pos, + start_pos + forward * self.beam_range, + MOVE_NOMONSTERS, + self + ); + + // untransform in case our trace went through a warpzone + vector end_pos = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); + + // un-adjust trueaim if shotend is too close + if(vlen(end_pos - start_pos) < g_trueaim_minrange) + end_pos = start_pos + (forward * g_trueaim_minrange); + + // move shot origin to the actual gun muzzle origin + vector origin_offset = + right * -self.beam_shotorigin_y + + up * self.beam_shotorigin_z; + + start_pos = start_pos + origin_offset; + + // Move it also forward, but only as far as possible without hitting anything. Don't poke into walls! + traceline(start_pos, start_pos + forward * self.beam_shotorigin_x, MOVE_NORMAL, self); + start_pos = trace_endpos; + + // calculate the aim direction now + wantdir = normalize(end_pos - start_pos); + + if(!self.beam_initialized) + { + self.beam_dir = wantdir; + self.beam_initialized = TRUE; + } + + if(self.beam_dir != wantdir) + { + // calculate how much we're going to move the end of the beam to the want position + // WEAPONTODO (server and client): + // blendfactor never actually becomes 0 in this situation, which is a problem + // regarding precision... this means that self.beam_dir and w_shotdir approach + // eachother, however they never actually become the same value with this method. + // Perhaps we should do some form of rounding/snapping? + float angle = vlen(wantdir - self.beam_dir) * RAD2DEG; + if(angle && (angle > self.beam_maxangle)) + { + // if the angle is greater than maxangle, force the blendfactor to make this the maximum factor + float blendfactor = bound( + 0, + (1 - (self.beam_returnspeed * frametime)), + min(self.beam_maxangle / angle, 1) + ); + self.beam_dir = normalize((wantdir * (1 - blendfactor)) + (self.beam_dir * blendfactor)); + } + else + { + // the radius is not too far yet, no worries :D + float blendfactor = bound( + 0, + (1 - (self.beam_returnspeed * frametime)), + 1 + ); + self.beam_dir = normalize((wantdir * (1 - blendfactor)) + (self.beam_dir * blendfactor)); + } + + // calculate how many segments are needed + float max_allowed_segments; + + if(self.beam_distancepersegment) + { + max_allowed_segments = min( + ARC_MAX_SEGMENTS, + 1 + (vlen(wantdir / self.beam_distancepersegment)) + ); + } + else { max_allowed_segments = ARC_MAX_SEGMENTS; } + + if(self.beam_degreespersegment) + { + segments = bound( + 1, + ( + min( + angle, + self.beam_maxangle + ) + / + self.beam_degreespersegment + ), + max_allowed_segments + ); + } + else { segments = 1; } + } + else { segments = 1; } + + // set the beam direction which the rest of the code will refer to + beamdir = self.beam_dir; + + // finally, set self.angles to the proper direction so that muzzle attachment points in proper direction + self.angles = fixedvectoangles2(forward, up); // TODO(Samual): is this == warpzone_save_view_angles? + } + else + { + // set the values from the provided info from the networked entity + start_pos = self.origin; + wantdir = self.v_angle; + beamdir = self.angles; + + if(beamdir != wantdir) + { + float angle = vlen(wantdir - beamdir) * RAD2DEG; + + // calculate how many segments are needed + float max_allowed_segments; + + if(self.beam_distancepersegment) + { + max_allowed_segments = min( + ARC_MAX_SEGMENTS, + 1 + (vlen(wantdir / self.beam_distancepersegment)) + ); + } + else { max_allowed_segments = ARC_MAX_SEGMENTS; } + + if(self.beam_degreespersegment) + { + segments = bound( + 1, + ( + min( + angle, + self.beam_maxangle + ) + / + self.beam_degreespersegment + ), + max_allowed_segments + ); + } + else { segments = 1; } + } + else { segments = 1; } + } + + setorigin(self, start_pos); + self.beam_muzzleentity.angles_z = random() * 360; // WEAPONTODO: use avelocity instead? + + vector beam_endpos = (start_pos + (beamdir * self.beam_range)); + vector beam_controlpoint = start_pos + wantdir * (self.beam_range * (1 - self.beam_tightness)); + + Draw_ArcBeam_callback_entity = self; + Draw_ArcBeam_callback_last_thickness = 0; + Draw_ArcBeam_callback_last_top = start_pos; + Draw_ArcBeam_callback_last_bottom = start_pos; + + vector last_origin = start_pos; + vector original_start_pos = start_pos; + + float i; + for(i = 1; i <= segments; ++i) + { + // WEAPONTODO (client): + // In order to do nice fading and pointing on the starting segment, we must always + // have that drawn as a separate triangle... However, that is difficult to do when + // keeping in mind the above problems and also optimizing the amount of segments + // drawn on screen at any given time. (Automatic beam quality scaling, essentially) + + vector new_origin = bezier_quadratic_getpoint( + start_pos, + beam_controlpoint, + beam_endpos, + i / segments); + + WarpZone_TraceBox_ThroughZone( + last_origin, + '0 0 0', + '0 0 0', + new_origin, + MOVE_NORMAL, + world, + world, + Draw_ArcBeam_callback + ); + + // Do all the transforms for warpzones right now, as we already "are" in the post-trace + // system (if we hit a player, that's always BEHIND the last passed wz). + last_origin = trace_endpos; + start_pos = WarpZone_TransformOrigin(WarpZone_trace_transform, start_pos); + beam_controlpoint = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_controlpoint); + beam_endpos = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_endpos); + beamdir = WarpZone_TransformVelocity(WarpZone_trace_transform, beamdir); + Draw_ArcBeam_callback_last_top = WarpZone_TransformOrigin(WarpZone_trace_transform, Draw_ArcBeam_callback_last_top); + Draw_ArcBeam_callback_last_bottom = WarpZone_TransformOrigin(WarpZone_trace_transform, Draw_ArcBeam_callback_last_bottom); + + if(trace_fraction < 1) { break; } + } + + // visual effects for startpoint and endpoint + if(self.beam_hiteffect) + { + // FIXME we really should do this on the server so it actually + // matches gameplay. What this client side stuff is doing is no + // more than guesswork. + if((trace_ent || trace_fraction < 1) && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)) + pointparticles( + self.beam_hiteffect, + last_origin, + beamdir * -1, + frametime * 2 + ); + } + if(self.beam_hitlight[0]) + { + adddynamiclight( + last_origin, + self.beam_hitlight[0], + vec3( + self.beam_hitlight[1], + self.beam_hitlight[2], + self.beam_hitlight[3] + ) + ); + } + if(self.beam_muzzleeffect) + { + pointparticles( + self.beam_muzzleeffect, + original_start_pos + wantdir * 20, + wantdir * 1000, + frametime * 0.1 + ); + } + if(self.beam_muzzlelight[0]) + { + adddynamiclight( + original_start_pos + wantdir * 20, + self.beam_muzzlelight[0], + vec3( + self.beam_muzzlelight[1], + self.beam_muzzlelight[2], + self.beam_muzzlelight[3] + ) + ); + } + + // cleanup + Draw_ArcBeam_callback_entity = world; + Draw_ArcBeam_callback_last_thickness = 0; + Draw_ArcBeam_callback_last_top = '0 0 0'; + Draw_ArcBeam_callback_last_bottom = '0 0 0'; +} + +void Remove_ArcBeam(void) +{ + remove(self.beam_muzzleentity); + sound(self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM); +} + +void Ent_ReadArcBeam(float isnew) +{ + float sf = ReadByte(); + entity flash; + + if(isnew) + { + // calculate shot origin offset from gun alignment + float gunalign = autocvar_cl_gunalign; + if(gunalign != 1 && gunalign != 2 && gunalign != 4) + gunalign = 3; // default value + --gunalign; + + self.beam_shotorigin = arc_shotorigin[gunalign]; + + // set other main attributes of the beam + self.draw = Draw_ArcBeam; + self.entremove = Remove_ArcBeam; + self.move_time = time; + loopsound(self, CH_SHOTS_SINGLE, "weapons/lgbeam_fly.wav", VOL_BASE, ATTEN_NORM); + + flash = spawn(); + flash.owner = self; + flash.effects = EF_ADDITIVE | EF_FULLBRIGHT; + flash.drawmask = MASK_NORMAL; + flash.solid = SOLID_NOT; + flash.avelocity_z = 5000; + setattachment(flash, self, ""); + setorigin(flash, '0 0 0'); + + self.beam_muzzleentity = flash; + } + else + { + flash = self.beam_muzzleentity; + } + + if(sf & ARC_SF_SETTINGS) // settings information + { + self.beam_degreespersegment = ReadShort(); + self.beam_distancepersegment = ReadShort(); + self.beam_maxangle = ReadShort(); + self.beam_range = ReadCoord(); + self.beam_returnspeed = ReadShort(); + self.beam_tightness = (ReadByte() / 10); + + if(ReadByte()) + { + if(autocvar_chase_active) + { self.beam_usevieworigin = 1; } + else // use view origin + { self.beam_usevieworigin = 2; } + } + else + { + self.beam_usevieworigin = 0; + } + } + + if(!self.beam_usevieworigin) + { + // self.iflags = IFLAG_ORIGIN | IFLAG_ANGLES | IFLAG_V_ANGLE; // why doesn't this work? + self.iflags = IFLAG_ORIGIN; + + InterpolateOrigin_Undo(); + } + + if(sf & ARC_SF_START) // starting location + { + self.origin_x = ReadCoord(); + self.origin_y = ReadCoord(); + self.origin_z = ReadCoord(); + } + else if(self.beam_usevieworigin) // infer the location from player location + { + if(self.beam_usevieworigin == 2) + { + // use view origin + self.origin = view_origin; + } + else + { + // use player origin so that third person display still works + self.origin = getplayerorigin(player_localnum) + ('0 0 1' * getstati(STAT_VIEWHEIGHT)); + } + } + + setorigin(self, self.origin); + + if(sf & ARC_SF_WANTDIR) // want/aim direction + { + self.v_angle_x = ReadCoord(); + self.v_angle_y = ReadCoord(); + self.v_angle_z = ReadCoord(); + } + + if(sf & ARC_SF_BEAMDIR) // beam direction + { + self.angles_x = ReadCoord(); + self.angles_y = ReadCoord(); + self.angles_z = ReadCoord(); + } + + if(sf & ARC_SF_BEAMTYPE) // beam type + { + self.beam_type = ReadByte(); + switch(self.beam_type) + { + case ARC_BT_MISS: + { + self.beam_color = '-1 -1 1'; + self.beam_alpha = 0.5; + self.beam_thickness = 8; + self.beam_traileffect = particleeffectnum("arc_beam"); + self.beam_hiteffect = particleeffectnum("arc_lightning"); + self.beam_hitlight[0] = 0; + self.beam_hitlight[1] = 1; + self.beam_hitlight[2] = 1; + self.beam_hitlight[3] = 1; + self.beam_muzzleeffect = -1; //particleeffectnum("nex_muzzleflash"); + self.beam_muzzlelight[0] = 0; + self.beam_muzzlelight[1] = 1; + self.beam_muzzlelight[2] = 1; + self.beam_muzzlelight[3] = 1; + if(self.beam_muzzleeffect >= 0) + { + self.beam_image = "particles/lgbeam"; + setmodel(flash, "models/flash.md3"); + flash.alpha = self.beam_alpha; + flash.colormod = self.beam_color; + flash.scale = 0.5; + } + break; + } + case ARC_BT_WALL: // grenadelauncher_muzzleflash healray_muzzleflash + { + self.beam_color = '0.5 0.5 1'; + self.beam_alpha = 0.5; + self.beam_thickness = 8; + self.beam_traileffect = particleeffectnum("arc_beam"); + self.beam_hiteffect = particleeffectnum("arc_lightning"); + self.beam_hitlight[0] = 0; + self.beam_hitlight[1] = 1; + self.beam_hitlight[2] = 1; + self.beam_hitlight[3] = 1; + self.beam_muzzleeffect = -1; // particleeffectnum("grenadelauncher_muzzleflash"); + self.beam_muzzlelight[0] = 0; + self.beam_muzzlelight[1] = 1; + self.beam_muzzlelight[2] = 1; + self.beam_muzzlelight[3] = 1; + self.beam_image = "particles/lgbeam"; + if(self.beam_muzzleeffect >= 0) + { + setmodel(flash, "models/flash.md3"); + flash.alpha = self.beam_alpha; + flash.colormod = self.beam_color; + flash.scale = 0.5; + } + break; + } + case ARC_BT_HEAL: + { + self.beam_color = '0 1 0'; + self.beam_alpha = 0.5; + self.beam_thickness = 8; + self.beam_traileffect = particleeffectnum("arc_beam_heal"); + self.beam_hiteffect = particleeffectnum("arc_beam_healimpact"); + self.beam_hitlight[0] = 0; + self.beam_hitlight[1] = 1; + self.beam_hitlight[2] = 1; + self.beam_hitlight[3] = 1; + self.beam_muzzleeffect = -1; //particleeffectnum("nex_muzzleflash"); + self.beam_muzzlelight[0] = 0; + self.beam_muzzlelight[1] = 1; + self.beam_muzzlelight[2] = 1; + self.beam_muzzlelight[3] = 1; + self.beam_image = "particles/lgbeam"; + if(self.beam_muzzleeffect >= 0) + { + self.beam_image = "particles/lgbeam"; + setmodel(flash, "models/flash.md3"); + flash.alpha = self.beam_alpha; + flash.colormod = self.beam_color; + flash.scale = 0.5; + } + break; + } + case ARC_BT_HIT: + { + self.beam_color = '1 0 1'; + self.beam_alpha = 0.5; + self.beam_thickness = 8; + self.beam_traileffect = particleeffectnum("arc_beam"); + self.beam_hiteffect = particleeffectnum("arc_lightning"); + self.beam_hitlight[0] = 20; + self.beam_hitlight[1] = 1; + self.beam_hitlight[2] = 0; + self.beam_hitlight[3] = 0; + self.beam_muzzleeffect = -1; //particleeffectnum("nex_muzzleflash"); + self.beam_muzzlelight[0] = 50; + self.beam_muzzlelight[1] = 1; + self.beam_muzzlelight[2] = 0; + self.beam_muzzlelight[3] = 0; + self.beam_image = "particles/lgbeam"; + if(self.beam_muzzleeffect >= 0) + { + self.beam_image = "particles/lgbeam"; + setmodel(flash, "models/flash.md3"); + flash.alpha = self.beam_alpha; + flash.colormod = self.beam_color; + flash.scale = 0.5; + } + break; + } + case ARC_BT_BURST_MISS: + { + self.beam_color = '-1 -1 1'; + self.beam_alpha = 0.5; + self.beam_thickness = 14; + self.beam_traileffect = particleeffectnum("arc_beam"); + self.beam_hiteffect = particleeffectnum("arc_lightning"); + self.beam_hitlight[0] = 0; + self.beam_hitlight[1] = 1; + self.beam_hitlight[2] = 1; + self.beam_hitlight[3] = 1; + self.beam_muzzleeffect = -1; //particleeffectnum("nex_muzzleflash"); + self.beam_muzzlelight[0] = 0; + self.beam_muzzlelight[1] = 1; + self.beam_muzzlelight[2] = 1; + self.beam_muzzlelight[3] = 1; + self.beam_image = "particles/lgbeam"; + setmodel(flash, "models/flash.md3"); + flash.alpha = self.beam_alpha; + flash.colormod = self.beam_color; + flash.scale = 0.5; + break; + } + case ARC_BT_BURST_WALL: + { + self.beam_color = '0.5 0.5 1'; + self.beam_alpha = 0.5; + self.beam_thickness = 14; + self.beam_traileffect = particleeffectnum("arc_beam"); + self.beam_hiteffect = particleeffectnum("arc_lightning"); + self.beam_hitlight[0] = 0; + self.beam_hitlight[1] = 1; + self.beam_hitlight[2] = 1; + self.beam_hitlight[3] = 1; + self.beam_muzzleeffect = -1; //particleeffectnum("nex_muzzleflash"); + self.beam_muzzlelight[0] = 0; + self.beam_muzzlelight[1] = 1; + self.beam_muzzlelight[2] = 1; + self.beam_muzzlelight[3] = 1; + self.beam_image = "particles/lgbeam"; + if(self.beam_muzzleeffect >= 0) + { + self.beam_image = "particles/lgbeam"; + setmodel(flash, "models/flash.md3"); + flash.alpha = self.beam_alpha; + flash.colormod = self.beam_color; + flash.scale = 0.5; + } + break; + } + case ARC_BT_BURST_HEAL: + { + self.beam_color = '0 1 0'; + self.beam_alpha = 0.5; + self.beam_thickness = 14; + self.beam_traileffect = particleeffectnum("arc_beam_heal"); + self.beam_hiteffect = particleeffectnum("healray_impact"); + self.beam_hitlight[0] = 0; + self.beam_hitlight[1] = 1; + self.beam_hitlight[2] = 1; + self.beam_hitlight[3] = 1; + self.beam_muzzleeffect = -1; //particleeffectnum("nex_muzzleflash"); + self.beam_muzzlelight[0] = 0; + self.beam_muzzlelight[1] = 1; + self.beam_muzzlelight[2] = 1; + self.beam_muzzlelight[3] = 1; + self.beam_image = "particles/lgbeam"; + if(self.beam_muzzleeffect >= 0) + { + self.beam_image = "particles/lgbeam"; + setmodel(flash, "models/flash.md3"); + flash.alpha = self.beam_alpha; + flash.colormod = self.beam_color; + flash.scale = 0.5; + } + break; + } + case ARC_BT_BURST_HIT: + { + self.beam_color = '1 0 1'; + self.beam_alpha = 0.5; + self.beam_thickness = 14; + self.beam_traileffect = particleeffectnum("arc_beam"); + self.beam_hiteffect = particleeffectnum("arc_lightning"); + self.beam_hitlight[0] = 0; + self.beam_hitlight[1] = 1; + self.beam_hitlight[2] = 1; + self.beam_hitlight[3] = 1; + self.beam_muzzleeffect = -1; //particleeffectnum("nex_muzzleflash"); + self.beam_muzzlelight[0] = 0; + self.beam_muzzlelight[1] = 1; + self.beam_muzzlelight[2] = 1; + self.beam_muzzlelight[3] = 1; + self.beam_image = "particles/lgbeam"; + if(self.beam_muzzleeffect >= 0) + { + self.beam_image = "particles/lgbeam"; + setmodel(flash, "models/flash.md3"); + flash.alpha = self.beam_alpha; + flash.colormod = self.beam_color; + flash.scale = 0.5; + } + break; + } + + // shouldn't be possible, but lets make it colorful if it does :D + default: + { + self.beam_color = randomvec(); + self.beam_alpha = 1; + self.beam_thickness = 8; + self.beam_traileffect = FALSE; + self.beam_hiteffect = FALSE; + self.beam_hitlight[0] = 0; + self.beam_hitlight[1] = 1; + self.beam_hitlight[2] = 1; + self.beam_hitlight[3] = 1; + self.beam_muzzleeffect = -1; //particleeffectnum("nex_muzzleflash"); + self.beam_muzzlelight[0] = 0; + self.beam_muzzlelight[1] = 1; + self.beam_muzzlelight[2] = 1; + self.beam_muzzlelight[3] = 1; + self.beam_image = "particles/lgbeam"; + if(self.beam_muzzleeffect >= 0) + { + self.beam_image = "particles/lgbeam"; + setmodel(flash, "models/flash.md3"); + flash.alpha = self.beam_alpha; + flash.colormod = self.beam_color; + flash.scale = 0.5; + } + break; + } + } + } + + if(!self.beam_usevieworigin) + { + InterpolateOrigin_Note(); + } +} + +float W_Arc(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + // todo + return TRUE; + } + case WR_INIT: + { + precache_sound("weapons/lgbeam_fly.wav"); + return TRUE; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_blaster.qc b/qcsrc/common/weapons/w_blaster.qc new file mode 100644 index 0000000000..aa8d0a89b6 --- /dev/null +++ b/qcsrc/common/weapons/w_blaster.qc @@ -0,0 +1,296 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ BLASTER, +/* function */ W_Blaster, +/* ammotype */ ammo_none, +/* impulse */ 1, +/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH, +/* rating */ 0, +/* color */ '1 0.5 0.5', +/* modelname */ "laser", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairlaser 0.4", +/* wepimg */ "weaponlaser", +/* refname */ "blaster", +/* wepname */ _("Blaster") +); + +#define BLASTER_SETTINGS(w_cvar,w_prop) BLASTER_SETTINGS_LIST(w_cvar, w_prop, BLASTER, blaster) +#define BLASTER_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, BOTH, animtime) \ + w_cvar(id, sn, BOTH, damage) \ + w_cvar(id, sn, BOTH, delay) \ + w_cvar(id, sn, BOTH, edgedamage) \ + w_cvar(id, sn, BOTH, force) \ + w_cvar(id, sn, BOTH, force_zscale) \ + w_cvar(id, sn, BOTH, lifetime) \ + w_cvar(id, sn, BOTH, radius) \ + w_cvar(id, sn, BOTH, refire) \ + w_cvar(id, sn, BOTH, shotangle) \ + w_cvar(id, sn, BOTH, speed) \ + w_cvar(id, sn, BOTH, spread) \ + w_cvar(id, sn, NONE, secondary) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +BLASTER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +.float blaster_damage; +.float blaster_edgedamage; +.float blaster_radius; +.float blaster_force; +.float blaster_lifetime; +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_blaster(void) { weapon_defaultspawnfunc(WEP_BLASTER); } +void spawnfunc_weapon_laser(void) { spawnfunc_weapon_blaster(); } + +void W_Blaster_Touch(void) +{ + PROJECTILE_TOUCH; + + self.event_damage = func_null; + + RadiusDamage( + self, + self.realowner, + self.blaster_damage, + self.blaster_edgedamage, + self.blaster_radius, + world, + world, + self.blaster_force, + self.projectiledeathtype, + other + ); + + remove(self); +} + +void W_Blaster_Think(void) +{ + self.movetype = MOVETYPE_FLY; + self.think = SUB_Remove; + self.nextthink = time + self.blaster_lifetime; + CSQCProjectile(self, TRUE, PROJECTILE_BLASTER, TRUE); +} + +void W_Blaster_Attack( + float atk_deathtype, + float atk_shotangle, + float atk_damage, + float atk_edgedamage, + float atk_radius, + float atk_force, + float atk_speed, + float atk_spread, + float atk_delay, + float atk_lifetime) +{ + vector s_forward = v_forward * cos(atk_shotangle * DEG2RAD) + v_up * sin(atk_shotangle * DEG2RAD); + + W_SetupShot_Dir(self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, atk_damage); + pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + entity missile = spawn(); + missile.owner = missile.realowner = self; + missile.classname = "blasterbolt"; + missile.bot_dodge = TRUE; + missile.bot_dodgerating = atk_damage; + PROJECTILE_MAKETRIGGER(missile); + + missile.blaster_damage = atk_damage; + missile.blaster_edgedamage = atk_edgedamage; + missile.blaster_radius = atk_radius; + missile.blaster_force = atk_force; + missile.blaster_lifetime = atk_lifetime; + + setorigin(missile, w_shotorg); + setsize(missile, '0 0 0', '0 0 0'); + + W_SetupProjVelocity_Explicit( + missile, + w_shotdir, + v_up, + atk_speed, + 0, + 0, + atk_spread, + FALSE + ); + + missile.angles = vectoangles(missile.velocity); + + //missile.glow_color = 250; // 244, 250 + //missile.glow_size = 120; + + missile.touch = W_Blaster_Touch; + missile.flags = FL_PROJECTILE; + missile.missile_flags = MIF_SPLASH; + missile.projectiledeathtype = atk_deathtype; + missile.think = W_Blaster_Think; + missile.nextthink = time + atk_delay; + + other = missile; MUTATOR_CALLHOOK(EditProjectile); + + if(time >= missile.nextthink) + { + entity oldself; + oldself = self; + self = missile; + self.think(); + self = oldself; + } +} +float W_Blaster(float request) +{ + switch(request) + { + case WR_AIM: + { + if(WEP_CVAR(blaster, secondary)) + { + if((random() * (WEP_CVAR_PRI(blaster, damage) + WEP_CVAR_SEC(blaster, damage))) > WEP_CVAR_PRI(blaster, damage)) + { self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_SEC(blaster, speed), 0, WEP_CVAR_SEC(blaster, lifetime), FALSE); } + else + { self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(blaster, speed), 0, WEP_CVAR_PRI(blaster, lifetime), FALSE); } + } + else + { self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(blaster, speed), 0, WEP_CVAR_PRI(blaster, lifetime), FALSE); } + + return TRUE; + } + + case WR_THINK: + { + if(self.BUTTON_ATCK) + { + if(weapon_prepareattack(0, WEP_CVAR_PRI(blaster, refire))) + { + W_Blaster_Attack( + WEP_BLASTER, + WEP_CVAR_PRI(blaster, shotangle), + WEP_CVAR_PRI(blaster, damage), + WEP_CVAR_PRI(blaster, edgedamage), + WEP_CVAR_PRI(blaster, radius), + WEP_CVAR_PRI(blaster, force), + WEP_CVAR_PRI(blaster, speed), + WEP_CVAR_PRI(blaster, spread), + WEP_CVAR_PRI(blaster, delay), + WEP_CVAR_PRI(blaster, lifetime) + ); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(blaster, animtime), w_ready); + } + } + else if(self.BUTTON_ATCK2) + { + switch(WEP_CVAR(blaster, secondary)) + { + case 0: // switch to last used weapon + { + if(self.switchweapon == WEP_BLASTER) // don't do this if already switching + W_LastWeapon(); + break; + } + + case 1: // normal projectile secondary + { + if(weapon_prepareattack(1, WEP_CVAR_SEC(blaster, refire))) + { + W_Blaster_Attack( + WEP_BLASTER | HITTYPE_SECONDARY, + WEP_CVAR_SEC(blaster, shotangle), + WEP_CVAR_SEC(blaster, damage), + WEP_CVAR_SEC(blaster, edgedamage), + WEP_CVAR_SEC(blaster, radius), + WEP_CVAR_SEC(blaster, force), + WEP_CVAR_SEC(blaster, speed), + WEP_CVAR_SEC(blaster, spread), + WEP_CVAR_SEC(blaster, delay), + WEP_CVAR_SEC(blaster, lifetime) + ); + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(blaster, animtime), w_ready); + } + + break; + } + } + } + return TRUE; + } + + case WR_INIT: + { + precache_model("models/weapons/g_laser.md3"); + precache_model("models/weapons/v_laser.md3"); + precache_model("models/weapons/h_laser.iqm"); + precache_sound("weapons/lasergun_fire.wav"); + BLASTER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + + case WR_SETUP: + { + self.ammo_field = ammo_none; + return TRUE; + } + + case WR_CHECKAMMO1: + case WR_CHECKAMMO2: + { + return TRUE; // laser has infinite ammo + } + + case WR_CONFIG: + { + BLASTER_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + + case WR_SUICIDEMESSAGE: + { + return WEAPON_BLASTER_SUICIDE; + } + + case WR_KILLMESSAGE: + { + return WEAPON_BLASTER_MURDER; + } + } + return FALSE; +} +#endif +#ifdef CSQC +float W_Blaster(float request) +{ + switch(request) + { + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 6; + pointparticles(particleeffectnum("laser_impact"), org2, w_backoff * 1000, 1); + if(!w_issilent) { sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM); } + return TRUE; + } + + case WR_INIT: + { + precache_sound("weapons/laserimpact.wav"); + return TRUE; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_crylink.qc b/qcsrc/common/weapons/w_crylink.qc new file mode 100644 index 0000000000..d17826a924 --- /dev/null +++ b/qcsrc/common/weapons/w_crylink.qc @@ -0,0 +1,730 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ CRYLINK, +/* function */ W_Crylink, +/* ammotype */ ammo_cells, +/* impulse */ 6, +/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH, +/* rating */ BOT_PICKUP_RATING_MID, +/* color */ '1 0.5 1', +/* modelname */ "crylink", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshaircrylink 0.4", +/* wepimg */ "weaponcrylink", +/* refname */ "crylink", +/* wepname */ _("Crylink") +); + +#define CRYLINK_SETTINGS(w_cvar,w_prop) CRYLINK_SETTINGS_LIST(w_cvar, w_prop, CRYLINK, crylink) +#define CRYLINK_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, BOTH, ammo) \ + w_cvar(id, sn, BOTH, animtime) \ + w_cvar(id, sn, BOTH, damage) \ + w_cvar(id, sn, BOTH, edgedamage) \ + w_cvar(id, sn, BOTH, radius) \ + w_cvar(id, sn, BOTH, force) \ + w_cvar(id, sn, BOTH, spread) \ + w_cvar(id, sn, BOTH, refire) \ + w_cvar(id, sn, BOTH, speed) \ + w_cvar(id, sn, BOTH, shots) \ + w_cvar(id, sn, BOTH, bounces) \ + w_cvar(id, sn, BOTH, bouncedamagefactor) \ + w_cvar(id, sn, BOTH, middle_lifetime) \ + w_cvar(id, sn, BOTH, middle_fadetime) \ + w_cvar(id, sn, BOTH, other_lifetime) \ + w_cvar(id, sn, BOTH, other_fadetime) \ + w_cvar(id, sn, BOTH, linkexplode) \ + w_cvar(id, sn, BOTH, joindelay) \ + w_cvar(id, sn, BOTH, joinspread) \ + w_cvar(id, sn, BOTH, joinexplode) \ + w_cvar(id, sn, BOTH, joinexplode_damage) \ + w_cvar(id, sn, BOTH, joinexplode_edgedamage) \ + w_cvar(id, sn, BOTH, joinexplode_radius) \ + w_cvar(id, sn, BOTH, joinexplode_force) \ + w_cvar(id, sn, SEC, spreadtype) \ + w_cvar(id, sn, NONE, secondary) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +CRYLINK_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +.float gravity; +.float crylink_waitrelease; +.entity crylink_lastgroup; + +.entity queuenext; +.entity queueprev; +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_crylink(void) { weapon_defaultspawnfunc(WEP_CRYLINK); } + +void W_Crylink_CheckLinks(entity e) +{ + float i; + entity p; + + if(e == world) + error("W_Crylink_CheckLinks: entity is world"); + if(e.classname != "spike" || wasfreed(e)) + error(sprintf("W_Crylink_CheckLinks: entity is not a spike but a %s (freed: %d)", e.classname, wasfreed(e))); + + p = e; + for(i = 0; i < 1000; ++i) + { + if(p.queuenext.queueprev != p || p.queueprev.queuenext != p) + error("W_Crylink_CheckLinks: queue is inconsistent"); + p = p.queuenext; + if(p == e) + break; + } + if(i >= 1000) + error("W_Crylink_CheckLinks: infinite chain"); +} + +void W_Crylink_Dequeue_Raw(entity own, entity prev, entity me, entity next) +{ + W_Crylink_CheckLinks(next); + if(me == own.crylink_lastgroup) + own.crylink_lastgroup = ((me == next) ? world : next); + prev.queuenext = next; + next.queueprev = prev; + me.classname = "spike_oktoremove"; + if(me != next) + W_Crylink_CheckLinks(next); +} + +void W_Crylink_Dequeue(entity e) +{ + W_Crylink_Dequeue_Raw(e.realowner, e.queueprev, e, e.queuenext); +} + +void W_Crylink_Reset(void) +{ + W_Crylink_Dequeue(self); + remove(self); +} + +// force projectile to explode +void W_Crylink_LinkExplode(entity e, entity e2) +{ + float a; + + if(e == e2) + return; + + a = bound(0, 1 - (time - e.fade_time) * e.fade_rate, 1); + + if(e == e.realowner.crylink_lastgroup) + e.realowner.crylink_lastgroup = world; + + float isprimary = !(e.projectiledeathtype & HITTYPE_SECONDARY); + + RadiusDamage(e, e.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * a, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * a, WEP_CVAR_BOTH(crylink, isprimary, radius), world, world, WEP_CVAR_BOTH(crylink, isprimary, force) * a, e.projectiledeathtype, other); + + W_Crylink_LinkExplode(e.queuenext, e2); + + e.classname = "spike_oktoremove"; + remove(e); +} + +// adjust towards center +// returns the origin where they will meet... and the time till the meeting is +// stored in w_crylink_linkjoin_time. +// could possibly network this origin and time, and display a special particle +// effect when projectiles meet there :P +// jspeed: joining speed (calculate this as join spread * initial speed) +float w_crylink_linkjoin_time; +vector W_Crylink_LinkJoin(entity e, float jspeed) +{ + vector avg_origin, avg_velocity; + vector targ_origin; + float avg_dist, n; + entity p; + + // FIXME remove this debug code + W_Crylink_CheckLinks(e); + + w_crylink_linkjoin_time = 0; + + avg_origin = e.origin; + avg_velocity = e.velocity; + n = 1; + for(p = e; (p = p.queuenext) != e; ) + { + avg_origin += WarpZone_RefSys_TransformOrigin(p, e, p.origin); + avg_velocity += WarpZone_RefSys_TransformVelocity(p, e, p.velocity); + ++n; + } + avg_origin *= (1.0 / n); + avg_velocity *= (1.0 / n); + + if(n < 2) + return avg_origin; // nothing to do + + // yes, mathematically we can do this in ONE step, but beware of 32bit floats... + avg_dist = pow(vlen(e.origin - avg_origin), 2); + for(p = e; (p = p.queuenext) != e; ) + avg_dist += pow(vlen(WarpZone_RefSys_TransformOrigin(p, e, p.origin) - avg_origin), 2); + avg_dist *= (1.0 / n); + avg_dist = sqrt(avg_dist); + + if(avg_dist == 0) + return avg_origin; // no change needed + + if(jspeed == 0) + { + e.velocity = avg_velocity; + UpdateCSQCProjectile(e); + for(p = e; (p = p.queuenext) != e; ) + { + p.velocity = WarpZone_RefSys_TransformVelocity(e, p, avg_velocity); + UpdateCSQCProjectile(p); + } + targ_origin = avg_origin + 1000000000 * normalize(avg_velocity); // HUUUUUUGE + } + else + { + w_crylink_linkjoin_time = avg_dist / jspeed; + targ_origin = avg_origin + w_crylink_linkjoin_time * avg_velocity; + + e.velocity = (targ_origin - e.origin) * (1.0 / w_crylink_linkjoin_time); + UpdateCSQCProjectile(e); + for(p = e; (p = p.queuenext) != e; ) + { + p.velocity = WarpZone_RefSys_TransformVelocity(e, p, (targ_origin - WarpZone_RefSys_TransformOrigin(p, e, p.origin)) * (1.0 / w_crylink_linkjoin_time)); + UpdateCSQCProjectile(p); + } + + // analysis: + // jspeed -> +infinity: + // w_crylink_linkjoin_time -> +0 + // targ_origin -> avg_origin + // p->velocity -> HUEG towards center + // jspeed -> 0: + // w_crylink_linkjoin_time -> +/- infinity + // targ_origin -> avg_velocity * +/- infinity + // p->velocity -> avg_velocity + // jspeed -> -infinity: + // w_crylink_linkjoin_time -> -0 + // targ_origin -> avg_origin + // p->velocity -> HUEG away from center + } + + W_Crylink_CheckLinks(e); + + return targ_origin; +} + +void W_Crylink_LinkJoinEffect_Think(void) +{ + // is there at least 2 projectiles very close? + entity e, p; + float n; + e = self.owner.crylink_lastgroup; + n = 0; + if(e) + { + if(vlen(e.origin - self.origin) < vlen(e.velocity) * frametime) + ++n; + for(p = e; (p = p.queuenext) != e; ) + { + if(vlen(p.origin - self.origin) < vlen(p.velocity) * frametime) + ++n; + } + if(n >= 2) + { + float isprimary = !(e.projectiledeathtype & HITTYPE_SECONDARY); + + if(WEP_CVAR_BOTH(crylink, isprimary, joinexplode)) + { + n /= WEP_CVAR_BOTH(crylink, isprimary, shots); + RadiusDamage( + e, + e.realowner, + WEP_CVAR_BOTH(crylink, isprimary, joinexplode_damage) * n, + WEP_CVAR_BOTH(crylink, isprimary, joinexplode_edgedamage) * n, + WEP_CVAR_BOTH(crylink, isprimary, joinexplode_radius) * n, + e.realowner, + world, + WEP_CVAR_BOTH(crylink, isprimary, joinexplode_force) * n, + e.projectiledeathtype, + other + ); + pointparticles(particleeffectnum("crylink_joinexplode"), self.origin, '0 0 0', n); + } + } + } + remove(self); +} + +float W_Crylink_Touch_WouldHitFriendly(entity projectile, float rad) +{ + entity head = WarpZone_FindRadius((projectile.origin + (projectile.mins + projectile.maxs) * 0.5), rad + MAX_DAMAGEEXTRARADIUS, FALSE); + float hit_friendly = 0; + float hit_enemy = 0; + + while(head) + { + if((head.takedamage != DAMAGE_NO) && (head.deadflag == DEAD_NO)) + { + if(SAME_TEAM(head, projectile.realowner)) + ++hit_friendly; + else + ++hit_enemy; + } + + head = head.chain; + } + + return (hit_enemy ? FALSE : hit_friendly); +} + +// NO bounce protection, as bounces are limited! +void W_Crylink_Touch(void) +{ + float finalhit; + float f; + float isprimary = !(self.projectiledeathtype & HITTYPE_SECONDARY); + PROJECTILE_TOUCH; + + float a; + a = bound(0, 1 - (time - self.fade_time) * self.fade_rate, 1); + + finalhit = ((self.cnt <= 0) || (other.takedamage != DAMAGE_NO)); + if(finalhit) + f = 1; + else + f = WEP_CVAR_BOTH(crylink, isprimary, bouncedamagefactor); + if(a) + f *= a; + + float totaldamage = RadiusDamage(self, self.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * f, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * f, WEP_CVAR_BOTH(crylink, isprimary, radius), world, world, WEP_CVAR_BOTH(crylink, isprimary, force) * f, self.projectiledeathtype, other); + + if(totaldamage && ((WEP_CVAR_BOTH(crylink, isprimary, linkexplode) == 2) || ((WEP_CVAR_BOTH(crylink, isprimary, linkexplode) == 1) && !W_Crylink_Touch_WouldHitFriendly(self, WEP_CVAR_BOTH(crylink, isprimary, radius))))) + { + if(self == self.realowner.crylink_lastgroup) + self.realowner.crylink_lastgroup = world; + W_Crylink_LinkExplode(self.queuenext, self); + self.classname = "spike_oktoremove"; + remove(self); + return; + } + else if(finalhit) + { + // just unlink + W_Crylink_Dequeue(self); + remove(self); + return; + } + self.cnt = self.cnt - 1; + self.angles = vectoangles(self.velocity); + self.owner = world; + self.projectiledeathtype |= HITTYPE_BOUNCE; + // commented out as it causes a little hitch... + //if(proj.cnt == 0) + // CSQCProjectile(proj, TRUE, PROJECTILE_CRYLINK, TRUE); +} + +void W_Crylink_Fadethink(void) +{ + W_Crylink_Dequeue(self); + remove(self); +} + +void W_Crylink_Attack(void) +{ + float counter, shots; + entity proj, prevproj, firstproj; + vector s; + vector forward, right, up; + float maxdmg; + + W_DecreaseAmmo(WEP_CVAR_PRI(crylink, ammo)); + + maxdmg = WEP_CVAR_PRI(crylink, damage) * WEP_CVAR_PRI(crylink, shots); + maxdmg *= 1 + WEP_CVAR_PRI(crylink, bouncedamagefactor) * WEP_CVAR_PRI(crylink, bounces); + if(WEP_CVAR_PRI(crylink, joinexplode)) + maxdmg += WEP_CVAR_PRI(crylink, joinexplode_damage); + + W_SetupShot(self, FALSE, 2, "weapons/crylink_fire.wav", CH_WEAPON_A, maxdmg); + forward = v_forward; + right = v_right; + up = v_up; + + shots = WEP_CVAR_PRI(crylink, shots); + pointparticles(particleeffectnum("crylink_muzzleflash"), w_shotorg, w_shotdir * 1000, shots); + proj = prevproj = firstproj = world; + for(counter = 0; counter < shots; ++counter) + { + proj = spawn(); + proj.reset = W_Crylink_Reset; + proj.realowner = proj.owner = self; + proj.classname = "spike"; + proj.bot_dodge = TRUE; + proj.bot_dodgerating = WEP_CVAR_PRI(crylink, damage); + if(shots == 1) { + proj.queuenext = proj; + proj.queueprev = proj; + } + else if(counter == 0) { // first projectile, store in firstproj for now + firstproj = proj; + } + else if(counter == shots - 1) { // last projectile, link up with first projectile + prevproj.queuenext = proj; + firstproj.queueprev = proj; + proj.queuenext = firstproj; + proj.queueprev = prevproj; + } + else { // else link up with previous projectile + prevproj.queuenext = proj; + proj.queueprev = prevproj; + } + + prevproj = proj; + + proj.movetype = MOVETYPE_BOUNCEMISSILE; + PROJECTILE_MAKETRIGGER(proj); + proj.projectiledeathtype = WEP_CRYLINK; + //proj.gravity = 0.001; + + setorigin(proj, w_shotorg); + setsize(proj, '0 0 0', '0 0 0'); + + + s = '0 0 0'; + if(counter == 0) + s = '0 0 0'; + else + { + makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1))); + s_y = v_forward_x; + s_z = v_forward_y; + } + s = s * WEP_CVAR_PRI(crylink, spread) * g_weaponspreadfactor; + W_SetupProjVelocity_Explicit(proj, w_shotdir + right * s_y + up * s_z, v_up, WEP_CVAR_PRI(crylink, speed), 0, 0, 0, FALSE); + proj.touch = W_Crylink_Touch; + + proj.think = W_Crylink_Fadethink; + if(counter == 0) + { + proj.fade_time = time + WEP_CVAR_PRI(crylink, middle_lifetime); + proj.fade_rate = 1 / WEP_CVAR_PRI(crylink, middle_fadetime); + proj.nextthink = time + WEP_CVAR_PRI(crylink, middle_lifetime) + WEP_CVAR_PRI(crylink, middle_fadetime); + } + else + { + proj.fade_time = time + WEP_CVAR_PRI(crylink, other_lifetime); + proj.fade_rate = 1 / WEP_CVAR_PRI(crylink, other_fadetime); + proj.nextthink = time + WEP_CVAR_PRI(crylink, other_lifetime) + WEP_CVAR_PRI(crylink, other_fadetime); + } + proj.teleport_time = time + WEP_CVAR_PRI(crylink, joindelay); + proj.cnt = WEP_CVAR_PRI(crylink, bounces); + //proj.scale = 1 + 1 * proj.cnt; + + proj.angles = vectoangles(proj.velocity); + + //proj.glow_size = 20; + + proj.flags = FL_PROJECTILE; + proj.missile_flags = MIF_SPLASH; + + CSQCProjectile(proj, TRUE, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), TRUE); + + other = proj; MUTATOR_CALLHOOK(EditProjectile); + } + if(WEP_CVAR_PRI(crylink, joinspread) != 0) + { + self.crylink_lastgroup = proj; + W_Crylink_CheckLinks(proj); + self.crylink_waitrelease = 1; + } +} + +void W_Crylink_Attack2(void) +{ + float counter, shots; + entity proj, prevproj, firstproj; + vector s; + vector forward, right, up; + float maxdmg; + + W_DecreaseAmmo(WEP_CVAR_SEC(crylink, ammo)); + + maxdmg = WEP_CVAR_SEC(crylink, damage) * WEP_CVAR_SEC(crylink, shots); + maxdmg *= 1 + WEP_CVAR_SEC(crylink, bouncedamagefactor) * WEP_CVAR_SEC(crylink, bounces); + if(WEP_CVAR_SEC(crylink, joinexplode)) + maxdmg += WEP_CVAR_SEC(crylink, joinexplode_damage); + + W_SetupShot(self, FALSE, 2, "weapons/crylink_fire2.wav", CH_WEAPON_A, maxdmg); + forward = v_forward; + right = v_right; + up = v_up; + + shots = WEP_CVAR_SEC(crylink, shots); + pointparticles(particleeffectnum("crylink_muzzleflash"), w_shotorg, w_shotdir * 1000, shots); + proj = prevproj = firstproj = world; + for(counter = 0; counter < shots; ++counter) + { + proj = spawn(); + proj.reset = W_Crylink_Reset; + proj.realowner = proj.owner = self; + proj.classname = "spike"; + proj.bot_dodge = TRUE; + proj.bot_dodgerating = WEP_CVAR_SEC(crylink, damage); + if(shots == 1) { + proj.queuenext = proj; + proj.queueprev = proj; + } + else if(counter == 0) { // first projectile, store in firstproj for now + firstproj = proj; + } + else if(counter == shots - 1) { // last projectile, link up with first projectile + prevproj.queuenext = proj; + firstproj.queueprev = proj; + proj.queuenext = firstproj; + proj.queueprev = prevproj; + } + else { // else link up with previous projectile + prevproj.queuenext = proj; + proj.queueprev = prevproj; + } + + prevproj = proj; + + proj.movetype = MOVETYPE_BOUNCEMISSILE; + PROJECTILE_MAKETRIGGER(proj); + proj.projectiledeathtype = WEP_CRYLINK | HITTYPE_SECONDARY; + //proj.gravity = 0.001; + + setorigin(proj, w_shotorg); + setsize(proj, '0 0 0', '0 0 0'); + + if(WEP_CVAR_SEC(crylink, spreadtype) == 1) + { + s = '0 0 0'; + if(counter == 0) + s = '0 0 0'; + else + { + makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1))); + s_y = v_forward_x; + s_z = v_forward_y; + } + s = s * WEP_CVAR_SEC(crylink, spread) * g_weaponspreadfactor; + s = w_shotdir + right * s_y + up * s_z; + } + else + { + s = (w_shotdir + (((counter + 0.5) / shots) * 2 - 1) * v_right * WEP_CVAR_SEC(crylink, spread) * g_weaponspreadfactor); + } + + W_SetupProjVelocity_Explicit(proj, s, v_up, WEP_CVAR_SEC(crylink, speed), 0, 0, 0, FALSE); + proj.touch = W_Crylink_Touch; + proj.think = W_Crylink_Fadethink; + if(counter == (shots - 1) / 2) + { + proj.fade_time = time + WEP_CVAR_SEC(crylink, middle_lifetime); + proj.fade_rate = 1 / WEP_CVAR_SEC(crylink, middle_fadetime); + proj.nextthink = time + WEP_CVAR_SEC(crylink, middle_lifetime) + WEP_CVAR_SEC(crylink, middle_fadetime); + } + else + { + proj.fade_time = time + WEP_CVAR_SEC(crylink, other_lifetime); + proj.fade_rate = 1 / WEP_CVAR_SEC(crylink, other_fadetime); + proj.nextthink = time + WEP_CVAR_SEC(crylink, other_lifetime) + WEP_CVAR_SEC(crylink, other_fadetime); + } + proj.teleport_time = time + WEP_CVAR_SEC(crylink, joindelay); + proj.cnt = WEP_CVAR_SEC(crylink, bounces); + //proj.scale = 1 + 1 * proj.cnt; + + proj.angles = vectoangles(proj.velocity); + + //proj.glow_size = 20; + + proj.flags = FL_PROJECTILE; + proj.missile_flags = MIF_SPLASH; + + CSQCProjectile(proj, TRUE, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), TRUE); + + other = proj; MUTATOR_CALLHOOK(EditProjectile); + } + if(WEP_CVAR_SEC(crylink, joinspread) != 0) + { + self.crylink_lastgroup = proj; + W_Crylink_CheckLinks(proj); + self.crylink_waitrelease = 2; + } +} + +float W_Crylink(float req) +{ + float ammo_amount; + switch(req) + { + case WR_AIM: + { + if(random() < 0.10) + self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(crylink, speed), 0, WEP_CVAR_PRI(crylink, middle_lifetime), FALSE); + else + self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_SEC(crylink, speed), 0, WEP_CVAR_SEC(crylink, middle_lifetime), FALSE); + + return TRUE; + } + case WR_THINK: + { + if(autocvar_g_balance_crylink_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo))) // forced reload + WEP_ACTION(self.weapon, WR_RELOAD); + + if(self.BUTTON_ATCK) + { + if(self.crylink_waitrelease != 1) + if(weapon_prepareattack(0, WEP_CVAR_PRI(crylink, refire))) + { + W_Crylink_Attack(); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(crylink, animtime), w_ready); + } + } + + if(self.BUTTON_ATCK2 && autocvar_g_balance_crylink_secondary) + { + if(self.crylink_waitrelease != 2) + if(weapon_prepareattack(1, WEP_CVAR_SEC(crylink, refire))) + { + W_Crylink_Attack2(); + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(crylink, animtime), w_ready); + } + } + + if((self.crylink_waitrelease == 1 && !self.BUTTON_ATCK) || (self.crylink_waitrelease == 2 && !self.BUTTON_ATCK2)) + { + if(!self.crylink_lastgroup || time > self.crylink_lastgroup.teleport_time) + { + // fired and released now! + if(self.crylink_lastgroup) + { + vector pos; + entity linkjoineffect; + float isprimary = (self.crylink_waitrelease == 1); + + pos = W_Crylink_LinkJoin(self.crylink_lastgroup, WEP_CVAR_BOTH(crylink, isprimary, joinspread) * WEP_CVAR_BOTH(crylink, isprimary, speed)); + + linkjoineffect = spawn(); + linkjoineffect.think = W_Crylink_LinkJoinEffect_Think; + linkjoineffect.classname = "linkjoineffect"; + linkjoineffect.nextthink = time + w_crylink_linkjoin_time; + linkjoineffect.owner = self; + setorigin(linkjoineffect, pos); + } + self.crylink_waitrelease = 0; + if(!W_Crylink(WR_CHECKAMMO1) && !W_Crylink(WR_CHECKAMMO2)) + if(!(self.items & IT_UNLIMITED_WEAPON_AMMO)) + { + // ran out of ammo! + self.cnt = WEP_CRYLINK; + self.switchweapon = w_getbestweapon(self); + } + } + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/weapons/g_crylink.md3"); + precache_model("models/weapons/v_crylink.md3"); + precache_model("models/weapons/h_crylink.iqm"); + precache_sound("weapons/crylink_fire.wav"); + precache_sound("weapons/crylink_fire2.wav"); + precache_sound("weapons/crylink_linkjoin.wav"); + CRYLINK_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_CHECKAMMO1: + { + // don't "run out of ammo" and switch weapons while waiting for release + if(self.crylink_lastgroup && self.crylink_waitrelease) + return TRUE; + + ammo_amount = self.WEP_AMMO(CRYLINK) >= WEP_CVAR_PRI(crylink, ammo); + ammo_amount += self.(weapon_load[WEP_CRYLINK]) >= WEP_CVAR_PRI(crylink, ammo); + return ammo_amount; + } + case WR_CHECKAMMO2: + { + // don't "run out of ammo" and switch weapons while waiting for release + if(self.crylink_lastgroup && self.crylink_waitrelease) + return TRUE; + + ammo_amount = self.WEP_AMMO(CRYLINK) >= WEP_CVAR_SEC(crylink, ammo); + ammo_amount += self.(weapon_load[WEP_CRYLINK]) >= WEP_CVAR_SEC(crylink, ammo); + return ammo_amount; + } + case WR_CONFIG: + { + CRYLINK_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_RELOAD: + { + W_Reload(min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo)), "weapons/reload.wav"); + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + return WEAPON_CRYLINK_SUICIDE; + } + case WR_KILLMESSAGE: + { + return WEAPON_CRYLINK_MURDER; + } + } + return FALSE; +} +#endif +#ifdef CSQC +float W_Crylink(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 2; + if(w_deathtype & HITTYPE_SECONDARY) + { + pointparticles(particleeffectnum("crylink_impact"), org2, '0 0 0', 1); + if(!w_issilent) + sound(self, CH_SHOTS, "weapons/crylink_impact2.wav", VOL_BASE, ATTN_NORM); + } + else + { + pointparticles(particleeffectnum("crylink_impactbig"), org2, '0 0 0', 1); + if(!w_issilent) + sound(self, CH_SHOTS, "weapons/crylink_impact.wav", VOL_BASE, ATTN_NORM); + } + + return TRUE; + } + case WR_INIT: + { + precache_sound("weapons/crylink_impact2.wav"); + precache_sound("weapons/crylink_impact.wav"); + return TRUE; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_devastator.qc b/qcsrc/common/weapons/w_devastator.qc new file mode 100644 index 0000000000..cff16702df --- /dev/null +++ b/qcsrc/common/weapons/w_devastator.qc @@ -0,0 +1,684 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ DEVASTATOR, +/* function */ W_Devastator, +/* ammotype */ ammo_rockets, +/* impulse */ 9, +/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH, +/* rating */ BOT_PICKUP_RATING_HIGH, +/* color */ '1 1 0', +/* modelname */ "rl", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairrocketlauncher 0.5875", +/* wepimg */ "weaponrocketlauncher", +/* refname */ "devastator", +/* wepname */ _("Devastator") +); + +#define DEVASTATOR_SETTINGS(w_cvar,w_prop) DEVASTATOR_SETTINGS_LIST(w_cvar, w_prop, DEVASTATOR, devastator) +#define DEVASTATOR_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, NONE, ammo) \ + w_cvar(id, sn, NONE, animtime) \ + w_cvar(id, sn, NONE, damage) \ + w_cvar(id, sn, NONE, damageforcescale) \ + w_cvar(id, sn, NONE, detonatedelay) \ + w_cvar(id, sn, NONE, edgedamage) \ + w_cvar(id, sn, NONE, force) \ + w_cvar(id, sn, NONE, guidedelay) \ + w_cvar(id, sn, NONE, guidegoal) \ + w_cvar(id, sn, NONE, guiderate) \ + w_cvar(id, sn, NONE, guideratedelay) \ + w_cvar(id, sn, NONE, guidestop) \ + w_cvar(id, sn, NONE, health) \ + w_cvar(id, sn, NONE, lifetime) \ + w_cvar(id, sn, NONE, radius) \ + w_cvar(id, sn, NONE, refire) \ + w_cvar(id, sn, NONE, remote_damage) \ + w_cvar(id, sn, NONE, remote_edgedamage) \ + w_cvar(id, sn, NONE, remote_force) \ + w_cvar(id, sn, NONE, remote_jump_damage) \ + w_cvar(id, sn, NONE, remote_jump_radius) \ + w_cvar(id, sn, NONE, remote_jump_velocity_z_add) \ + w_cvar(id, sn, NONE, remote_jump_velocity_z_max) \ + w_cvar(id, sn, NONE, remote_jump_velocity_z_min) \ + w_cvar(id, sn, NONE, remote_radius) \ + w_cvar(id, sn, NONE, speed) \ + w_cvar(id, sn, NONE, speedaccel) \ + w_cvar(id, sn, NONE, speedstart) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +DEVASTATOR_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +.float rl_release; +.float rl_detonate_later; +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_devastator(void) { weapon_defaultspawnfunc(WEP_DEVASTATOR); } +void spawnfunc_weapon_rocketlauncher(void) { spawnfunc_weapon_devastator(); } + +void W_Devastator_Unregister(void) +{ + if(self.realowner && self.realowner.lastrocket == self) + { + self.realowner.lastrocket = world; + // self.realowner.rl_release = 1; + } +} + +void W_Devastator_Explode(void) +{ + W_Devastator_Unregister(); + + if(other.takedamage == DAMAGE_AIM) + if(IS_PLAYER(other)) + if(DIFF_TEAM(self.realowner, other)) + if(other.deadflag == DEAD_NO) + if(IsFlying(other)) + Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); + + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + + RadiusDamage( + self, + self.realowner, + WEP_CVAR(devastator, damage), + WEP_CVAR(devastator, edgedamage), + WEP_CVAR(devastator, radius), + world, + world, + WEP_CVAR(devastator, force), + self.projectiledeathtype, + other + ); + + if(self.realowner.weapon == WEP_DEVASTATOR) + { + if(self.realowner.WEP_AMMO(DEVASTATOR) < WEP_CVAR(devastator, ammo)) + { + self.realowner.cnt = WEP_DEVASTATOR; + ATTACK_FINISHED(self.realowner) = time; + self.realowner.switchweapon = w_getbestweapon(self.realowner); + } + } + remove(self); +} + +void W_Devastator_DoRemoteExplode(void) +{ + W_Devastator_Unregister(); + + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + + float handled_as_rocketjump = FALSE; + + entity head = WarpZone_FindRadius( + self.origin, + WEP_CVAR(devastator, remote_jump_radius), + FALSE + ); + + while(head) + { + if(head.takedamage && (head == self.realowner)) + { + float distance_to_head = vlen(self.origin - head.WarpZone_findradius_nearest); + if(distance_to_head <= WEP_CVAR(devastator, remote_jump_radius)) + { + // we handled this as a rocketjump :) + handled_as_rocketjump = TRUE; + + // modify velocity + head.velocity_x *= 0.9; + head.velocity_y *= 0.9; + head.velocity_z = bound( + WEP_CVAR(devastator, remote_jump_velocity_z_min), + head.velocity_z + WEP_CVAR(devastator, remote_jump_velocity_z_add), + WEP_CVAR(devastator, remote_jump_velocity_z_max) + ); + + // now do the damage + RadiusDamage( + self, + head, + WEP_CVAR(devastator, remote_jump_damage), + WEP_CVAR(devastator, remote_jump_damage), + WEP_CVAR(devastator, remote_jump_radius), + world, + head, + 0, + self.projectiledeathtype | HITTYPE_BOUNCE, + world + ); + break; + } + } + head = head.chain; + } + + RadiusDamage( + self, + self.realowner, + WEP_CVAR(devastator, remote_damage), + WEP_CVAR(devastator, remote_edgedamage), + WEP_CVAR(devastator, remote_radius), + (handled_as_rocketjump ? head : world), + world, + WEP_CVAR(devastator, remote_force), + self.projectiledeathtype | HITTYPE_BOUNCE, + world + ); + + if(self.realowner.weapon == WEP_DEVASTATOR) + { + if(self.realowner.WEP_AMMO(DEVASTATOR) < WEP_CVAR(devastator, ammo)) + { + self.realowner.cnt = WEP_DEVASTATOR; + ATTACK_FINISHED(self.realowner) = time; + self.realowner.switchweapon = w_getbestweapon(self.realowner); + } + } + remove(self); +} + +void W_Devastator_RemoteExplode(void) +{ + if(self.realowner.deadflag == DEAD_NO) + if(self.realowner.lastrocket) + { + if((self.spawnshieldtime >= 0) + ? (time >= self.spawnshieldtime) // timer + : (vlen(NearestPointOnBox(self.realowner, self.origin) - self.origin) > WEP_CVAR(devastator, remote_radius)) // safety device + ) + { + W_Devastator_DoRemoteExplode(); + } + } +} + +vector W_Devastator_SteerTo(vector thisdir, vector goaldir, float maxturn_cos) +{ + if(thisdir * goaldir > maxturn_cos) + return goaldir; + if(thisdir * goaldir < -0.9998) // less than 1 degree and opposite + return thisdir; // refuse to guide (better than letting a numerical error happen) + float f, m2; + vector v; + // solve: + // g = normalize(thisdir + goaldir * X) + // thisdir * g = maxturn + // + // gg = thisdir + goaldir * X + // (thisdir * gg)^2 = maxturn^2 * (gg * gg) + // + // (1 + (thisdir * goaldir) * X)^2 = maxturn^2 * (1 + X*X + 2 * X * thisdir * goaldir) + f = thisdir * goaldir; + // (1 + f * X)^2 = maxturn^2 * (1 + X*X + 2 * X * f) + // 0 = (m^2 - f^2) * x^2 + (2 * f * (m^2 - 1)) * x + (m^2 - 1) + m2 = maxturn_cos * maxturn_cos; + v = solve_quadratic(m2 - f * f, 2 * f * (m2 - 1), m2 - 1); + return normalize(thisdir + goaldir * v_y); // the larger solution! +} +// assume thisdir == -goaldir: +// f == -1 +// v = solve_qadratic(m2 - 1, -2 * (m2 - 1), m2 - 1) +// (m2 - 1) x^2 - 2 * (m2 - 1) * x + (m2 - 1) = 0 +// x^2 - 2 * x + 1 = 0 +// (x - 1)^2 = 0 +// x = 1 +// normalize(thisdir + goaldir) +// normalize(0) + +void W_Devastator_Think(void) +{ + vector desireddir, olddir, newdir, desiredorigin, goal; + float velspeed, f; + self.nextthink = time; + if(time > self.cnt) + { + other = world; + self.projectiledeathtype |= HITTYPE_BOUNCE; + W_Devastator_Explode(); + return; + } + + // accelerate + makevectors(self.angles_x * '-1 0 0' + self.angles_y * '0 1 0'); + velspeed = WEP_CVAR(devastator, speed) * g_weaponspeedfactor - (self.velocity * v_forward); + if(velspeed > 0) + self.velocity = self.velocity + v_forward * min(WEP_CVAR(devastator, speedaccel) * g_weaponspeedfactor * frametime, velspeed); + + // laser guided, or remote detonation + if(self.realowner.weapon == WEP_DEVASTATOR) + { + if(self == self.realowner.lastrocket) + if(!self.realowner.rl_release) + if(!self.BUTTON_ATCK2) + if(WEP_CVAR(devastator, guiderate)) + if(time > self.pushltime) + if(self.realowner.deadflag == DEAD_NO) + { + f = WEP_CVAR(devastator, guideratedelay); + if(f) + f = bound(0, (time - self.pushltime) / f, 1); + else + f = 1; + + velspeed = vlen(self.velocity); + + makevectors(self.realowner.v_angle); + desireddir = WarpZone_RefSys_TransformVelocity(self.realowner, self, v_forward); + desiredorigin = WarpZone_RefSys_TransformOrigin(self.realowner, self, self.realowner.origin + self.realowner.view_ofs); + olddir = normalize(self.velocity); + + // now it gets tricky... we want to move like some curve to approximate the target direction + // but we are limiting the rate at which we can turn! + goal = desiredorigin + ((self.origin - desiredorigin) * desireddir + WEP_CVAR(devastator, guidegoal)) * desireddir; + newdir = W_Devastator_SteerTo(olddir, normalize(goal - self.origin), cos(WEP_CVAR(devastator, guiderate) * f * frametime * DEG2RAD)); + + self.velocity = newdir * velspeed; + self.angles = vectoangles(self.velocity); + + if(!self.count) + { + pointparticles(particleeffectnum("rocket_guide"), self.origin, self.velocity, 1); + // TODO add a better sound here + sound(self.realowner, CH_WEAPON_B, "weapons/rocket_mode.wav", VOL_BASE, ATTN_NORM); + self.count = 1; + } + } + + if(self.rl_detonate_later) + W_Devastator_RemoteExplode(); + } + + if(self.csqcprojectile_clientanimate == 0) + UpdateCSQCProjectile(self); +} + +void W_Devastator_Touch(void) +{ + if(WarpZone_Projectile_Touch()) + { + if(wasfreed(self)) + W_Devastator_Unregister(); + return; + } + W_Devastator_Unregister(); + W_Devastator_Explode(); +} + +void W_Devastator_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if(self.health <= 0) + return; + + if(!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions + return; // g_projectiles_damage says to halt + + self.health = self.health - damage; + self.angles = vectoangles(self.velocity); + + if(self.health <= 0) + W_PrepareExplosionByDamage(attacker, W_Devastator_Explode); +} + +void W_Devastator_Attack(void) +{ + entity missile; + entity flash; + + W_DecreaseAmmo(WEP_CVAR(devastator, ammo)); + + W_SetupShot_ProjectileSize(self, '-3 -3 -3', '3 3 3', FALSE, 5, "weapons/rocket_fire.wav", CH_WEAPON_A, WEP_CVAR(devastator, damage)); + pointparticles(particleeffectnum("rocketlauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + missile = WarpZone_RefSys_SpawnSameRefSys(self); + missile.owner = missile.realowner = self; + self.lastrocket = missile; + if(WEP_CVAR(devastator, detonatedelay) >= 0) + missile.spawnshieldtime = time + WEP_CVAR(devastator, detonatedelay); + else + missile.spawnshieldtime = -1; + missile.pushltime = time + WEP_CVAR(devastator, guidedelay); + missile.classname = "rocket"; + missile.bot_dodge = TRUE; + missile.bot_dodgerating = WEP_CVAR(devastator, damage) * 2; // * 2 because it can be detonated inflight which makes it even more dangerous + + missile.takedamage = DAMAGE_YES; + missile.damageforcescale = WEP_CVAR(devastator, damageforcescale); + missile.health = WEP_CVAR(devastator, health); + missile.event_damage = W_Devastator_Damage; + missile.damagedbycontents = TRUE; + + missile.movetype = MOVETYPE_FLY; + PROJECTILE_MAKETRIGGER(missile); + missile.projectiledeathtype = WEP_DEVASTATOR; + 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(devastator, speedstart), 0); + missile.angles = vectoangles(missile.velocity); + + missile.touch = W_Devastator_Touch; + missile.think = W_Devastator_Think; + missile.nextthink = time; + missile.cnt = time + WEP_CVAR(devastator, lifetime); + missile.flags = FL_PROJECTILE; + missile.missile_flags = MIF_SPLASH; + + CSQCProjectile(missile, WEP_CVAR(devastator, guiderate) == 0 && WEP_CVAR(devastator, speedaccel) == 0, PROJECTILE_ROCKET, FALSE); // because of fly sound + + // muzzle flash for 1st person view + flash = spawn(); + setmodel(flash, "models/flash.md3"); // precision set below + SUB_SetFade(flash, time, 0.1); + flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; + W_AttachToShotorg(flash, '5 0 0'); + + // common properties + other = missile; MUTATOR_CALLHOOK(EditProjectile); +} + +float W_Devastator(float req) +{ + entity rock; + float rockfound; + float ammo_amount; + switch(req) + { + #if 0 + case WR_AIM: + { + // aim and decide to fire if appropriate + self.BUTTON_ATCK = bot_aim(WEP_CVAR(devastator, speed), 0, WEP_CVAR(devastator, lifetime), FALSE); + if(skill >= 2) // skill 0 and 1 bots won't detonate rockets! + { + // decide whether to detonate rockets + entity missile, targetlist, targ; + targetlist = findchainfloat(bot_attack, TRUE); + for(missile = world; (missile = find(missile, classname, "rocket")); ) if(missile.realowner == self) + { + targ = targetlist; + while(targ) + { + if(targ != missile.realowner && vlen(targ.origin - missile.origin) < WEP_CVAR(devastator, radius)) + { + self.BUTTON_ATCK2 = TRUE; + break; + } + targ = targ.chain; + } + } + + if(self.BUTTON_ATCK2) self.BUTTON_ATCK = FALSE; + } + + return TRUE; + } + #else + case WR_AIM: + { + // aim and decide to fire if appropriate + self.BUTTON_ATCK = bot_aim(WEP_CVAR(devastator, speed), 0, WEP_CVAR(devastator, lifetime), FALSE); + if(skill >= 2) // skill 0 and 1 bots won't detonate rockets! + { + // decide whether to detonate rockets + entity missile, targetlist, targ; + float edgedamage, coredamage, edgeradius, recipricoledgeradius, d; + float selfdamage, teamdamage, enemydamage; + edgedamage = WEP_CVAR(devastator, edgedamage); + coredamage = WEP_CVAR(devastator, damage); + edgeradius = WEP_CVAR(devastator, radius); + recipricoledgeradius = 1 / edgeradius; + selfdamage = 0; + teamdamage = 0; + enemydamage = 0; + targetlist = findchainfloat(bot_attack, TRUE); + missile = find(world, classname, "rocket"); + while(missile) + { + if(missile.realowner != self) + { + missile = find(missile, classname, "rocket"); + continue; + } + targ = targetlist; + while(targ) + { + d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - missile.origin); + d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000); + // count potential damage according to type of target + if(targ == self) + selfdamage = selfdamage + d; + else if(targ.team == self.team && teamplay) + teamdamage = teamdamage + d; + else if(bot_shouldattack(targ)) + enemydamage = enemydamage + d; + targ = targ.chain; + } + missile = find(missile, classname, "rocket"); + } + float desirabledamage; + desirabledamage = enemydamage; + if(time > self.invincible_finished && time > self.spawnshieldtime) + desirabledamage = desirabledamage - selfdamage * autocvar_g_balance_selfdamagepercent; + if(teamplay && self.team) + desirabledamage = desirabledamage - teamdamage; + + missile = find(world, classname, "rocket"); + while(missile) + { + if(missile.realowner != self) + { + missile = find(missile, classname, "rocket"); + continue; + } + makevectors(missile.v_angle); + targ = targetlist; + if(skill > 9) // normal players only do this for the target they are tracking + { + targ = targetlist; + while(targ) + { + if( + (v_forward * normalize(missile.origin - targ.origin)< 0.1) + && desirabledamage > 0.1*coredamage + )self.BUTTON_ATCK2 = TRUE; + targ = targ.chain; + } + }else{ + float distance; distance= bound(300,vlen(self.origin-self.enemy.origin),30000); + //As the distance gets larger, a correct detonation gets near imposible + //Bots are assumed to use the rocket spawnfunc_light to see if the rocket gets near a player + if(v_forward * normalize(missile.origin - self.enemy.origin)< 0.1) + if(IS_PLAYER(self.enemy)) + if(desirabledamage >= 0.1*coredamage) + if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1)) + self.BUTTON_ATCK2 = TRUE; + // dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n"); + } + + missile = find(missile, classname, "rocket"); + } + // if we would be doing at X percent of the core damage, detonate it + // but don't fire a new shot at the same time! + if(desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events + self.BUTTON_ATCK2 = TRUE; + if((skill > 6.5) && (selfdamage > self.health)) + self.BUTTON_ATCK2 = FALSE; + //if(self.BUTTON_ATCK2 == TRUE) + // dprint(ftos(desirabledamage),"\n"); + if(self.BUTTON_ATCK2 == TRUE) self.BUTTON_ATCK = FALSE; + } + + return TRUE; + } + #endif + case WR_THINK: + { + if(WEP_CVAR(devastator, reload_ammo) && self.clip_load < WEP_CVAR(devastator, ammo)) // forced reload + WEP_ACTION(self.weapon, WR_RELOAD); + else + { + if(self.BUTTON_ATCK) + { + if(self.rl_release || WEP_CVAR(devastator, guidestop)) + if(weapon_prepareattack(0, WEP_CVAR(devastator, refire))) + { + W_Devastator_Attack(); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(devastator, animtime), w_ready); + self.rl_release = 0; + } + } + else + self.rl_release = 1; + + if(self.BUTTON_ATCK2) + if(self.switchweapon == WEP_DEVASTATOR) + { + rockfound = 0; + for(rock = world; (rock = find(rock, classname, "rocket")); ) if(rock.realowner == self) + { + if(!rock.rl_detonate_later) + { + rock.rl_detonate_later = TRUE; + rockfound = 1; + } + } + if(rockfound) + sound(self, CH_WEAPON_B, "weapons/rocket_det.wav", VOL_BASE, ATTN_NORM); + } + } + + return TRUE; + } + case WR_INIT: + { + //if(autocvar_sv_precacheweapons) + //{ + precache_model("models/flash.md3"); + precache_model("models/weapons/g_rl.md3"); + precache_model("models/weapons/v_rl.md3"); + precache_model("models/weapons/h_rl.iqm"); + precache_sound("weapons/rocket_det.wav"); + precache_sound("weapons/rocket_fire.wav"); + precache_sound("weapons/rocket_mode.wav"); + //} + DEVASTATOR_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_SETUP: + { + self.rl_release = 1; + return TRUE; + } + case WR_CHECKAMMO1: + { + #if 0 + // don't switch while guiding a missile + if(ATTACK_FINISHED(self) <= time || self.weapon != WEP_DEVASTATOR) + { + ammo_amount = FALSE; + if(WEP_CVAR(devastator, reload_ammo)) + { + if(self.WEP_AMMO(DEVASTATOR) < WEP_CVAR(devastator, ammo) && self.(weapon_load[WEP_DEVASTATOR]) < WEP_CVAR(devastator, ammo)) + ammo_amount = TRUE; + } + else if(self.WEP_AMMO(DEVASTATOR) < WEP_CVAR(devastator, ammo)) + ammo_amount = TRUE; + return !ammo_amount; + } + #endif + #if 0 + if(self.rl_release == 0) + { + printf("W_Devastator(WR_CHECKAMMO1): %d, %.2f, %d: TRUE\n", self.rl_release, self.WEP_AMMO(DEVASTATOR), WEP_CVAR(devastator, ammo)); + return TRUE; + } + else + { + ammo_amount = self.WEP_AMMO(DEVASTATOR) >= WEP_CVAR(devastator, ammo); + ammo_amount += self.(weapon_load[WEP_DEVASTATOR]) >= WEP_CVAR(devastator, ammo); + printf("W_Devastator(WR_CHECKAMMO1): %d, %.2f, %d: %s\n", self.rl_release, self.WEP_AMMO(DEVASTATOR), WEP_CVAR(devastator, ammo), (ammo_amount ? "TRUE" : "FALSE")); + return ammo_amount; + } + #else + ammo_amount = self.WEP_AMMO(DEVASTATOR) >= WEP_CVAR(devastator, ammo); + ammo_amount += self.(weapon_load[WEP_DEVASTATOR]) >= WEP_CVAR(devastator, ammo); + return ammo_amount; + #endif + } + case WR_CHECKAMMO2: + { + return FALSE; + } + case WR_CONFIG: + { + DEVASTATOR_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_RESETPLAYER: + { + self.rl_release = 0; + return TRUE; + } + case WR_RELOAD: + { + W_Reload(WEP_CVAR(devastator, ammo), "weapons/reload.wav"); + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + return WEAPON_DEVASTATOR_SUICIDE; + } + case WR_KILLMESSAGE: + { + if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH)) + return WEAPON_DEVASTATOR_MURDER_SPLASH; + else + return WEAPON_DEVASTATOR_MURDER_DIRECT; + } + } + return FALSE; +} +#endif +#ifdef CSQC +float W_Devastator(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 12; + pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1); + if(!w_issilent) + sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM); + + return TRUE; + } + case WR_INIT: + { + precache_sound("weapons/rocket_impact.wav"); + return TRUE; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_electro.qc b/qcsrc/common/weapons/w_electro.qc new file mode 100644 index 0000000000..3847e2d56e --- /dev/null +++ b/qcsrc/common/weapons/w_electro.qc @@ -0,0 +1,620 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ ELECTRO, +/* function */ W_Electro, +/* ammotype */ ammo_cells, +/* impulse */ 5, +/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH, +/* rating */ BOT_PICKUP_RATING_MID, +/* color */ '0 0.5 1', +/* modelname */ "electro", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairelectro 0.5", +/* wepimg */ "weaponelectro", +/* refname */ "electro", +/* wepname */ _("Electro") +); + +#define ELECTRO_SETTINGS(w_cvar,w_prop) ELECTRO_SETTINGS_LIST(w_cvar, w_prop, ELECTRO, electro) +#define ELECTRO_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, BOTH, ammo) \ + w_cvar(id, sn, BOTH, animtime) \ + w_cvar(id, sn, BOTH, damage) \ + w_cvar(id, sn, BOTH, edgedamage) \ + w_cvar(id, sn, BOTH, force) \ + w_cvar(id, sn, BOTH, radius) \ + w_cvar(id, sn, BOTH, refire) \ + w_cvar(id, sn, BOTH, speed) \ + w_cvar(id, sn, BOTH, spread) \ + w_cvar(id, sn, BOTH, lifetime) \ + w_cvar(id, sn, PRI, comboradius) \ + w_cvar(id, sn, PRI, midaircombo_explode) \ + w_cvar(id, sn, PRI, midaircombo_interval) \ + w_cvar(id, sn, PRI, midaircombo_radius) \ + w_cvar(id, sn, SEC, bouncefactor) \ + w_cvar(id, sn, SEC, bouncestop) \ + w_cvar(id, sn, SEC, count) \ + w_cvar(id, sn, SEC, damageforcescale) \ + w_cvar(id, sn, SEC, damagedbycontents) \ + w_cvar(id, sn, SEC, health) \ + w_cvar(id, sn, SEC, refire2) \ + w_cvar(id, sn, SEC, speed_up) \ + w_cvar(id, sn, SEC, speed_z) \ + w_cvar(id, sn, SEC, touchexplode) \ + w_cvar(id, sn, NONE, combo_comboradius) \ + w_cvar(id, sn, NONE, combo_comboradius_thruwall) \ + w_cvar(id, sn, NONE, combo_damage) \ + w_cvar(id, sn, NONE, combo_edgedamage) \ + w_cvar(id, sn, NONE, combo_force) \ + w_cvar(id, sn, NONE, combo_radius) \ + w_cvar(id, sn, NONE, combo_speed) \ + w_cvar(id, sn, NONE, combo_safeammocheck) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +ELECTRO_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +.float electro_count; +.float electro_secondarytime; +void W_Electro_ExplodeCombo(void); +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_electro(void) { weapon_defaultspawnfunc(WEP_ELECTRO); } + +void W_Electro_TriggerCombo(vector org, float rad, entity own) +{ + entity e = WarpZone_FindRadius(org, rad, !WEP_CVAR(electro, combo_comboradius_thruwall)); + while(e) + { + if(e.classname == "electro_orb") + { + // do we allow thruwall triggering? + if(WEP_CVAR(electro, combo_comboradius_thruwall)) + { + // if distance is greater than thruwall distance, check to make sure it's not through a wall + if(vlen(e.WarpZone_findradius_dist) > WEP_CVAR(electro, combo_comboradius_thruwall)) + { + WarpZone_TraceLine(org, e.origin, MOVE_NOMONSTERS, e); + if(trace_fraction != 1) + { + // trigger is through a wall and outside of thruwall range, abort + e = e.chain; + continue; + } + } + } + + // change owner to whoever caused the combo explosion + e.realowner = own; + e.takedamage = DAMAGE_NO; + e.classname = "electro_orb_chain"; + + // now set the next one to trigger as well + e.think = W_Electro_ExplodeCombo; + + // delay combo chains, looks cooler + e.nextthink = + ( + time + + + (WEP_CVAR(electro, combo_speed) ? + (vlen(e.WarpZone_findradius_dist) / WEP_CVAR(electro, combo_speed)) + : + 0 + ) + ); + } + e = e.chain; + } +} + +void W_Electro_ExplodeCombo(void) +{ + W_Electro_TriggerCombo(self.origin, WEP_CVAR(electro, combo_comboradius), self.realowner); + + self.event_damage = func_null; + + RadiusDamage( + self, + self.realowner, + WEP_CVAR(electro, combo_damage), + WEP_CVAR(electro, combo_edgedamage), + WEP_CVAR(electro, combo_radius), + world, + world, + WEP_CVAR(electro, combo_force), + WEP_ELECTRO | HITTYPE_BOUNCE, // use THIS type for a combo because primary can't bounce + world + ); + + remove(self); +} + +void W_Electro_Explode(void) +{ + if(other.takedamage == DAMAGE_AIM) + if(IS_PLAYER(other)) + if(DIFF_TEAM(self.realowner, other)) + if(other.deadflag == DEAD_NO) + if(IsFlying(other)) + Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_ELECTROBITCH); + + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + + if(self.movetype == MOVETYPE_BOUNCE) + { + RadiusDamage( + self, + self.realowner, + WEP_CVAR_SEC(electro, damage), + WEP_CVAR_SEC(electro, edgedamage), + WEP_CVAR_SEC(electro, radius), + world, + world, + WEP_CVAR_SEC(electro, force), + self.projectiledeathtype, + other + ); + } + else + { + W_Electro_TriggerCombo(self.origin, WEP_CVAR_PRI(electro, comboradius), self.realowner); + RadiusDamage( + self, + self.realowner, + WEP_CVAR_PRI(electro, damage), + WEP_CVAR_PRI(electro, edgedamage), + WEP_CVAR_PRI(electro, radius), + world, + world, + WEP_CVAR_PRI(electro, force), + self.projectiledeathtype, + other + ); + } + + remove(self); +} + +void W_Electro_TouchExplode(void) +{ + PROJECTILE_TOUCH; + W_Electro_Explode(); +} + +void W_Electro_Bolt_Think(void) +{ + if(time >= self.ltime) + { + self.use(); + return; + } + + if(WEP_CVAR_PRI(electro, midaircombo_radius)) + { + float found = 0; + entity e = WarpZone_FindRadius(self.origin, WEP_CVAR_PRI(electro, midaircombo_radius), TRUE); + + // loop through nearby orbs and trigger them + while(e) + { + if(e.classname == "electro_orb") + { + // change owner to whoever caused the combo explosion + e.realowner = self.realowner; + e.takedamage = DAMAGE_NO; + e.classname = "electro_orb_chain"; + + // now set the next one to trigger as well + e.think = W_Electro_ExplodeCombo; + + // delay combo chains, looks cooler + e.nextthink = + ( + time + + + (WEP_CVAR(electro, combo_speed) ? + (vlen(e.WarpZone_findradius_dist) / WEP_CVAR(electro, combo_speed)) + : + 0 + ) + ); + + ++found; + } + e = e.chain; + } + + // if we triggered an orb, should we explode? if not, lets try again next time + if(found && WEP_CVAR_PRI(electro, midaircombo_explode)) + { self.use(); } + else + { self.nextthink = min(time + WEP_CVAR_PRI(electro, midaircombo_interval), self.ltime); } + } + else { self.nextthink = self.ltime; } +} + +void W_Electro_Attack_Bolt(void) +{ + entity proj; + + W_DecreaseAmmo(WEP_CVAR_PRI(electro, ammo)); + + W_SetupShot_ProjectileSize( + self, + '0 0 -3', + '0 0 -3', + FALSE, + 2, + "weapons/electro_fire.wav", + CH_WEAPON_A, + WEP_CVAR_PRI(electro, damage) + ); + + pointparticles(particleeffectnum("electro_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + proj = spawn(); + proj.classname = "electro_bolt"; + proj.owner = proj.realowner = self; + proj.bot_dodge = TRUE; + proj.bot_dodgerating = WEP_CVAR_PRI(electro, damage); + proj.use = W_Electro_Explode; + proj.think = W_Electro_Bolt_Think; + proj.nextthink = time; + proj.ltime = time + WEP_CVAR_PRI(electro, lifetime); + PROJECTILE_MAKETRIGGER(proj); + proj.projectiledeathtype = WEP_ELECTRO; + setorigin(proj, w_shotorg); + + proj.movetype = MOVETYPE_FLY; + W_SetupProjVelocity_PRI(proj, electro); + proj.angles = vectoangles(proj.velocity); + proj.touch = W_Electro_TouchExplode; + setsize(proj, '0 0 -3', '0 0 -3'); + proj.flags = FL_PROJECTILE; + proj.missile_flags = MIF_SPLASH; + + CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO_BEAM, TRUE); + + other = proj; MUTATOR_CALLHOOK(EditProjectile); +} + +void W_Electro_Orb_Touch(void) +{ + PROJECTILE_TOUCH; + if(other.takedamage == DAMAGE_AIM) + { if(WEP_CVAR_SEC(electro, touchexplode)) { W_Electro_Explode(); } } + else + { + //UpdateCSQCProjectile(self); + spamsound(self, CH_SHOTS, "weapons/electro_bounce.wav", VOL_BASE, ATTEN_NORM); + self.projectiledeathtype |= HITTYPE_BOUNCE; + } +} + +void W_Electro_Orb_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if(self.health <= 0) + return; + + // note: combos are usually triggered by W_Electro_TriggerCombo, not damage + float is_combo = (inflictor.classname == "electro_orb_chain" || inflictor.classname == "electro_bolt"); + + if(!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, (is_combo ? 1 : -1))) + return; // g_projectiles_damage says to halt + + self.health = self.health - damage; + if(self.health <= 0) + { + self.takedamage = DAMAGE_NO; + self.nextthink = time; + if(is_combo) + { + // change owner to whoever caused the combo explosion + self.realowner = inflictor.realowner; + self.classname = "electro_orb_chain"; + self.think = W_Electro_ExplodeCombo; + self.nextthink = time + + ( + // bound the length, inflictor may be in a galaxy far far away (warpzones) + min( + WEP_CVAR(electro, combo_radius), + vlen(self.origin - inflictor.origin) + ) + / + // delay combo chains, looks cooler + WEP_CVAR(electro, combo_speed) + ); + } + else + { + self.use = W_Electro_Explode; + self.think = adaptor_think2use; // not _hittype_splash, as this runs "immediately" + } + } +} + +void W_Electro_Attack_Orb(void) +{ + W_DecreaseAmmo(WEP_CVAR_SEC(electro, ammo)); + + W_SetupShot_ProjectileSize( + self, + '0 0 -4', + '0 0 -4', + FALSE, + 2, + "weapons/electro_fire2.wav", + CH_WEAPON_A, + WEP_CVAR_SEC(electro, damage) + ); + + w_shotdir = v_forward; // no TrueAim for grenades please + + pointparticles(particleeffectnum("electro_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + entity proj = spawn(); + proj.classname = "electro_orb"; + proj.owner = proj.realowner = self; + proj.use = W_Electro_Explode; + proj.think = adaptor_think2use_hittype_splash; + proj.bot_dodge = TRUE; + proj.bot_dodgerating = WEP_CVAR_SEC(electro, damage); + proj.nextthink = time + WEP_CVAR_SEC(electro, lifetime); + PROJECTILE_MAKETRIGGER(proj); + proj.projectiledeathtype = WEP_ELECTRO | HITTYPE_SECONDARY; + setorigin(proj, w_shotorg); + + //proj.glow_size = 50; + //proj.glow_color = 45; + proj.movetype = MOVETYPE_BOUNCE; + W_SetupProjVelocity_UP_SEC(proj, electro); + proj.touch = W_Electro_Orb_Touch; + setsize(proj, '0 0 -4', '0 0 -4'); + proj.takedamage = DAMAGE_YES; + proj.damageforcescale = WEP_CVAR_SEC(electro, damageforcescale); + proj.health = WEP_CVAR_SEC(electro, health); + proj.event_damage = W_Electro_Orb_Damage; + proj.flags = FL_PROJECTILE; + proj.damagedbycontents = (WEP_CVAR_SEC(electro, damagedbycontents)); + + proj.bouncefactor = WEP_CVAR_SEC(electro, bouncefactor); + proj.bouncestop = WEP_CVAR_SEC(electro, bouncestop); + proj.missile_flags = MIF_SPLASH | MIF_ARC; + +#if 0 + entity p2; + p2 = spawn(); + copyentity(proj, p2); + setmodel(p2, "models/ebomb.mdl"); + setsize(p2, proj.mins, proj.maxs); +#endif + + CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO, FALSE); // no culling, it has sound + + other = proj; MUTATOR_CALLHOOK(EditProjectile); +} + +void W_Electro_CheckAttack(void) +{ + if(self.electro_count > 1) + if(self.BUTTON_ATCK2) + if(weapon_prepareattack(1, -1)) + { + W_Electro_Attack_Orb(); + self.electro_count -= 1; + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack); + return; + } + // WEAPONTODO: when the player releases the button, cut down the length of refire2? + w_ready(); +} + +.float bot_secondary_electromooth; +float W_Electro(float req) +{ + float ammo_amount; + switch(req) + { + case WR_AIM: + { + self.BUTTON_ATCK = self.BUTTON_ATCK2 = FALSE; + if(vlen(self.origin-self.enemy.origin) > 1000) { self.bot_secondary_electromooth = 0; } + if(self.bot_secondary_electromooth == 0) + { + float shoot; + + if(WEP_CVAR_PRI(electro, speed)) + shoot = bot_aim(WEP_CVAR_PRI(electro, speed), 0, WEP_CVAR_PRI(electro, lifetime), FALSE); + else + shoot = bot_aim(1000000, 0, 0.001, FALSE); + + if(shoot) + { + self.BUTTON_ATCK = TRUE; + if(random() < 0.01) self.bot_secondary_electromooth = 1; + } + } + else + { + if(bot_aim(WEP_CVAR_SEC(electro, speed), WEP_CVAR_SEC(electro, speed_up), WEP_CVAR_SEC(electro, lifetime), TRUE)) + { + self.BUTTON_ATCK2 = TRUE; + if(random() < 0.03) self.bot_secondary_electromooth = 0; + } + } + + return TRUE; + } + case WR_THINK: + { + if(autocvar_g_balance_electro_reload_ammo) // forced reload // WEAPONTODO + { + ammo_amount = 0; + if(self.clip_load >= WEP_CVAR_PRI(electro, ammo)) + ammo_amount = 1; + if(self.clip_load >= WEP_CVAR_SEC(electro, ammo)) + ammo_amount += 1; + + if(!ammo_amount) + { + WEP_ACTION(self.weapon, WR_RELOAD); + return FALSE; + } + + return TRUE; + } + + if(self.BUTTON_ATCK) + { + if(weapon_prepareattack(0, WEP_CVAR_PRI(electro, refire))) + { + W_Electro_Attack_Bolt(); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); + } + } + else if(self.BUTTON_ATCK2) + { + if(time >= self.electro_secondarytime) + if(weapon_prepareattack(1, WEP_CVAR_SEC(electro, refire))) + { + W_Electro_Attack_Orb(); + self.electro_count = WEP_CVAR_SEC(electro, count); + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack); + self.electro_secondarytime = time + WEP_CVAR_SEC(electro, refire2) * W_WeaponRateFactor(); + } + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/weapons/g_electro.md3"); + precache_model("models/weapons/v_electro.md3"); + precache_model("models/weapons/h_electro.iqm"); + precache_sound("weapons/electro_bounce.wav"); + precache_sound("weapons/electro_fire.wav"); + precache_sound("weapons/electro_fire2.wav"); + precache_sound("weapons/electro_impact.wav"); + precache_sound("weapons/electro_impact_combo.wav"); + ELECTRO_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_CHECKAMMO1: + { + ammo_amount = self.WEP_AMMO(ELECTRO) >= WEP_CVAR_PRI(electro, ammo); + ammo_amount += self.(weapon_load[WEP_ELECTRO]) >= WEP_CVAR_PRI(electro, ammo); + return ammo_amount; + } + case WR_CHECKAMMO2: + { + if(WEP_CVAR(electro, combo_safeammocheck)) // true if you can fire at least one secondary blob AND one primary shot after it, otherwise false. + { + ammo_amount = self.WEP_AMMO(ELECTRO) >= WEP_CVAR_SEC(electro, ammo) + WEP_CVAR_PRI(electro, ammo); + ammo_amount += self.(weapon_load[WEP_ELECTRO]) >= WEP_CVAR_SEC(electro, ammo) + WEP_CVAR_PRI(electro, ammo); + } + else + { + ammo_amount = self.WEP_AMMO(ELECTRO) >= WEP_CVAR_SEC(electro, ammo); + ammo_amount += self.(weapon_load[WEP_ELECTRO]) >= WEP_CVAR_SEC(electro, ammo); + } + return ammo_amount; + } + case WR_CONFIG: + { + ELECTRO_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_RESETPLAYER: + { + self.electro_secondarytime = time; + return TRUE; + } + case WR_RELOAD: + { + W_Reload(min(WEP_CVAR_PRI(electro, ammo), WEP_CVAR_SEC(electro, ammo)), "weapons/reload.wav"); + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + if(w_deathtype & HITTYPE_SECONDARY) + return WEAPON_ELECTRO_SUICIDE_ORBS; + else + return WEAPON_ELECTRO_SUICIDE_BOLT; + } + case WR_KILLMESSAGE: + { + if(w_deathtype & HITTYPE_SECONDARY) + { + return WEAPON_ELECTRO_MURDER_ORBS; + } + else + { + if(w_deathtype & HITTYPE_BOUNCE) + return WEAPON_ELECTRO_MURDER_COMBO; + else + return WEAPON_ELECTRO_MURDER_BOLT; + } + } + } + return FALSE; +} +#endif +#ifdef CSQC +float W_Electro(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 6; + if(w_deathtype & HITTYPE_SECONDARY) + { + pointparticles(particleeffectnum("electro_ballexplode"), org2, '0 0 0', 1); + if(!w_issilent) + sound(self, CH_SHOTS, "weapons/electro_impact.wav", VOL_BASE, ATTEN_NORM); + } + else + { + if(w_deathtype & HITTYPE_BOUNCE) + { + // this is sent as "primary (w_deathtype & HITTYPE_BOUNCE)" to distinguish it from (w_deathtype & HITTYPE_SECONDARY) bounced balls + pointparticles(particleeffectnum("electro_combo"), org2, '0 0 0', 1); + if(!w_issilent) + sound(self, CH_SHOTS, "weapons/electro_impact_combo.wav", VOL_BASE, ATTEN_NORM); + } + else + { + pointparticles(particleeffectnum("electro_impact"), org2, '0 0 0', 1); + if(!w_issilent) + sound(self, CH_SHOTS, "weapons/electro_impact.wav", VOL_BASE, ATTEN_NORM); + } + } + + return TRUE; + } + case WR_INIT: + { + precache_sound("weapons/electro_impact.wav"); + precache_sound("weapons/electro_impact_combo.wav"); + return TRUE; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_fireball.qc b/qcsrc/common/weapons/w_fireball.qc new file mode 100644 index 0000000000..a5bca22cd1 --- /dev/null +++ b/qcsrc/common/weapons/w_fireball.qc @@ -0,0 +1,485 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ FIREBALL, +/* function */ W_Fireball, +/* ammotype */ ammo_none, +/* impulse */ 9, +/* flags */ WEP_FLAG_SUPERWEAPON | WEP_TYPE_SPLASH, +/* rating */ BOT_PICKUP_RATING_MID, +/* color */ '1 0.5 0', +/* modelname */ "fireball", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairfireball", +/* wepimg */ "weaponfireball", +/* refname */ "fireball", +/* wepname */ _("Fireball") +); + +#define FIREBALL_SETTINGS(w_cvar,w_prop) FIREBALL_SETTINGS_LIST(w_cvar, w_prop, FIREBALL, fireball) +#define FIREBALL_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, BOTH, animtime) \ + w_cvar(id, sn, BOTH, refire) \ + w_cvar(id, sn, BOTH, damage) \ + w_cvar(id, sn, BOTH, damageforcescale) \ + w_cvar(id, sn, BOTH, speed) \ + w_cvar(id, sn, BOTH, spread) \ + w_cvar(id, sn, BOTH, lifetime) \ + w_cvar(id, sn, BOTH, laserburntime) \ + w_cvar(id, sn, BOTH, laserdamage) \ + w_cvar(id, sn, BOTH, laseredgedamage) \ + w_cvar(id, sn, BOTH, laserradius) \ + w_cvar(id, sn, PRI, edgedamage) \ + w_cvar(id, sn, PRI, force) \ + w_cvar(id, sn, PRI, radius) \ + w_cvar(id, sn, PRI, health) \ + w_cvar(id, sn, PRI, refire2) \ + w_cvar(id, sn, PRI, bfgdamage) \ + w_cvar(id, sn, PRI, bfgforce) \ + w_cvar(id, sn, PRI, bfgradius) \ + w_cvar(id, sn, SEC, damagetime) \ + w_cvar(id, sn, SEC, speed_up) \ + w_cvar(id, sn, SEC, speed_z) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +FIREBALL_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +.float bot_primary_fireballmooth; // whatever a mooth is +.vector fireball_impactvec; +.float fireball_primarytime; +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_fireball(void) { weapon_defaultspawnfunc(WEP_FIREBALL); } + +void W_Fireball_Explode(void) +{ + entity e; + float dist; + float points; + vector dir; + float d; + + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + + // 1. dist damage + d = (self.realowner.health + self.realowner.armorvalue); + RadiusDamage(self, self.realowner, WEP_CVAR_PRI(fireball, damage), WEP_CVAR_PRI(fireball, edgedamage), WEP_CVAR_PRI(fireball, radius), world, world, WEP_CVAR_PRI(fireball, force), self.projectiledeathtype, other); + if(self.realowner.health + self.realowner.armorvalue >= d) + if(!self.cnt) + { + modeleffect_spawn("models/sphere/sphere.md3", 0, 0, self.origin, '0 0 0', '0 0 0', '0 0 0', 0, WEP_CVAR_PRI(fireball, bfgradius), 0.2, 0.05, 0.25); + + // 2. bfg effect + // NOTE: this cannot be made warpzone aware by design. So, better intentionally ignore warpzones here. + for(e = findradius(self.origin, WEP_CVAR_PRI(fireball, bfgradius)); e; e = e.chain) + if(e != self.realowner) if(e.takedamage == DAMAGE_AIM) if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self)) + { + // can we see fireball? + traceline(e.origin + e.view_ofs, self.origin, MOVE_NORMAL, e); + if(/* trace_startsolid || */ trace_fraction != 1) // startsolid should be never happening anyway + continue; + // can we see player who shot fireball? + traceline(e.origin + e.view_ofs, self.realowner.origin + self.realowner.view_ofs, MOVE_NORMAL, e); + if(trace_ent != self.realowner) + if(/* trace_startsolid || */ trace_fraction != 1) + continue; + dist = vlen(self.origin - e.origin - e.view_ofs); + points = (1 - sqrt(dist / WEP_CVAR_PRI(fireball, bfgradius))); + if(points <= 0) + continue; + dir = normalize(e.origin + e.view_ofs - self.origin); + + if(accuracy_isgooddamage(self.realowner, e)) + accuracy_add(self.realowner, WEP_FIREBALL, 0, WEP_CVAR_PRI(fireball, bfgdamage) * points); + + Damage(e, self, self.realowner, WEP_CVAR_PRI(fireball, bfgdamage) * points, self.projectiledeathtype | HITTYPE_BOUNCE | HITTYPE_SPLASH, e.origin + e.view_ofs, WEP_CVAR_PRI(fireball, bfgforce) * dir); + pointparticles(particleeffectnum("fireball_bfgdamage"), e.origin, -1 * dir, 1); + } + } + + remove(self); +} + +void W_Fireball_TouchExplode(void) +{ + PROJECTILE_TOUCH; + W_Fireball_Explode(); +} + +void W_Fireball_LaserPlay(float dt, float dist, float damage, float edgedamage, float burntime) +{ + entity e; + float d; + vector p; + + if(damage <= 0) + return; + + RandomSelection_Init(); + for(e = WarpZone_FindRadius(self.origin, dist, TRUE); e; e = e.chain) + if(e != self.realowner) if(e.takedamage == DAMAGE_AIM) if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self)) + { + p = e.origin; + p_x += e.mins_x + random() * (e.maxs_x - e.mins_x); + p_y += e.mins_y + random() * (e.maxs_y - e.mins_y); + p_z += e.mins_z + random() * (e.maxs_z - e.mins_z); + d = vlen(WarpZone_UnTransformOrigin(e, self.origin) - p); + if(d < dist) + { + e.fireball_impactvec = p; + RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e)); + } + } + if(RandomSelection_chosen_ent) + { + d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec); + d = damage + (edgedamage - damage) * (d / dist); + Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE); + //trailparticles(self, particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec); + pointparticles(particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1); + } +} + +void W_Fireball_Think(void) +{ + if(time > self.pushltime) + { + self.cnt = 1; + self.projectiledeathtype |= HITTYPE_SPLASH; + W_Fireball_Explode(); + return; + } + + W_Fireball_LaserPlay(0.1, WEP_CVAR_PRI(fireball, laserradius), WEP_CVAR_PRI(fireball, laserdamage), WEP_CVAR_PRI(fireball, laseredgedamage), WEP_CVAR_PRI(fireball, laserburntime)); + + self.nextthink = time + 0.1; +} + +void W_Fireball_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if(self.health <= 0) + return; + + if(!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions + return; // g_projectiles_damage says to halt + + self.health = self.health - damage; + if(self.health <= 0) + { + self.cnt = 1; + W_PrepareExplosionByDamage(attacker, W_Fireball_Explode); + } +} + +void W_Fireball_Attack1(void) +{ + entity proj; + + W_SetupShot_ProjectileSize(self, '-16 -16 -16', '16 16 16', FALSE, 2, "weapons/fireball_fire2.wav", CH_WEAPON_A, WEP_CVAR_PRI(fireball, damage) + WEP_CVAR_PRI(fireball, bfgdamage)); + + pointparticles(particleeffectnum("fireball_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + proj = spawn(); + proj.classname = "plasma_prim"; + proj.owner = proj.realowner = self; + proj.bot_dodge = TRUE; + proj.bot_dodgerating = WEP_CVAR_PRI(fireball, damage); + proj.pushltime = time + WEP_CVAR_PRI(fireball, lifetime); + proj.use = W_Fireball_Explode; + proj.think = W_Fireball_Think; + proj.nextthink = time; + proj.health = WEP_CVAR_PRI(fireball, health); + proj.team = self.team; + proj.event_damage = W_Fireball_Damage; + proj.takedamage = DAMAGE_YES; + proj.damageforcescale = WEP_CVAR_PRI(fireball, damageforcescale); + PROJECTILE_MAKETRIGGER(proj); + proj.projectiledeathtype = WEP_FIREBALL; + setorigin(proj, w_shotorg); + + proj.movetype = MOVETYPE_FLY; + W_SetupProjVelocity_PRI(proj, fireball); + proj.angles = vectoangles(proj.velocity); + proj.touch = W_Fireball_TouchExplode; + setsize(proj, '-16 -16 -16', '16 16 16'); + proj.flags = FL_PROJECTILE; + proj.missile_flags = MIF_SPLASH | MIF_PROXY; + + CSQCProjectile(proj, TRUE, PROJECTILE_FIREBALL, TRUE); + + other = proj; MUTATOR_CALLHOOK(EditProjectile); +} + +void W_Fireball_AttackEffect(float i, vector f_diff) +{ + W_SetupShot_ProjectileSize(self, '-16 -16 -16', '16 16 16', FALSE, 0, "", 0, 0); + w_shotorg += f_diff_x * v_up + f_diff_y * v_right; + pointparticles(particleeffectnum("fireball_preattack_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); +} + +void W_Fireball_Attack1_Frame4(void) +{ + W_Fireball_Attack1(); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), w_ready); +} + +void W_Fireball_Attack1_Frame3(void) +{ + W_Fireball_AttackEffect(0, '+1.25 +3.75 0'); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame4); +} + +void W_Fireball_Attack1_Frame2(void) +{ + W_Fireball_AttackEffect(0, '-1.25 +3.75 0'); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame3); +} + +void W_Fireball_Attack1_Frame1(void) +{ + W_Fireball_AttackEffect(1, '+1.25 -3.75 0'); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame2); +} + +void W_Fireball_Attack1_Frame0(void) +{ + W_Fireball_AttackEffect(0, '-1.25 -3.75 0'); + sound(self, CH_WEAPON_SINGLE, "weapons/fireball_prefire2.wav", VOL_BASE, ATTEN_NORM); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame1); +} + +void W_Fireball_Firemine_Think(void) +{ + if(time > self.pushltime) + { + remove(self); + return; + } + + // make it "hot" once it leaves its owner + if(self.owner) + { + if(vlen(self.origin - self.owner.origin - self.owner.view_ofs) > WEP_CVAR_SEC(fireball, laserradius)) + { + self.cnt += 1; + if(self.cnt == 3) + self.owner = world; + } + else + self.cnt = 0; + } + + W_Fireball_LaserPlay(0.1, WEP_CVAR_SEC(fireball, laserradius), WEP_CVAR_SEC(fireball, laserdamage), WEP_CVAR_SEC(fireball, laseredgedamage), WEP_CVAR_SEC(fireball, laserburntime)); + + self.nextthink = time + 0.1; +} + +void W_Fireball_Firemine_Touch(void) +{ + PROJECTILE_TOUCH; + if(other.takedamage == DAMAGE_AIM) + if(Fire_AddDamage(other, self.realowner, WEP_CVAR_SEC(fireball, damage), WEP_CVAR_SEC(fireball, damagetime), self.projectiledeathtype) >= 0) + { + remove(self); + return; + } + self.projectiledeathtype |= HITTYPE_BOUNCE; +} + +void W_Fireball_Attack2(void) +{ + entity proj; + vector f_diff; + float c; + + c = mod(self.bulletcounter, 4); + switch(c) + { + case 0: + f_diff = '-1.25 -3.75 0'; + break; + case 1: + f_diff = '+1.25 -3.75 0'; + break; + case 2: + f_diff = '-1.25 +3.75 0'; + break; + case 3: + default: + f_diff = '+1.25 +3.75 0'; + break; + } + W_SetupShot_ProjectileSize(self, '-4 -4 -4', '4 4 4', FALSE, 2, "weapons/fireball_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(fireball, damage)); + traceline(w_shotorg, w_shotorg + f_diff_x * v_up + f_diff_y * v_right, MOVE_NORMAL, self); + w_shotorg = trace_endpos; + + pointparticles(particleeffectnum("fireball_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + proj = spawn(); + proj.owner = proj.realowner = self; + proj.classname = "grenade"; + proj.bot_dodge = TRUE; + proj.bot_dodgerating = WEP_CVAR_SEC(fireball, damage); + proj.movetype = MOVETYPE_BOUNCE; + proj.projectiledeathtype = WEP_FIREBALL | HITTYPE_SECONDARY; + proj.touch = W_Fireball_Firemine_Touch; + PROJECTILE_MAKETRIGGER(proj); + setsize(proj, '-4 -4 -4', '4 4 4'); + setorigin(proj, w_shotorg); + proj.think = W_Fireball_Firemine_Think; + proj.nextthink = time; + proj.damageforcescale = WEP_CVAR_SEC(fireball, damageforcescale); + proj.pushltime = time + WEP_CVAR_SEC(fireball, lifetime); + W_SetupProjVelocity_UP_SEC(proj, fireball); + + proj.angles = vectoangles(proj.velocity); + proj.flags = FL_PROJECTILE; + proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC; + + CSQCProjectile(proj, TRUE, PROJECTILE_FIREMINE, TRUE); + + other = proj; MUTATOR_CALLHOOK(EditProjectile); +} + +float W_Fireball(float req) +{ + switch(req) + { + case WR_AIM: + { + self.BUTTON_ATCK = FALSE; + self.BUTTON_ATCK2 = FALSE; + if(self.bot_primary_fireballmooth == 0) + { + if(bot_aim(WEP_CVAR_PRI(fireball, speed), 0, WEP_CVAR_PRI(fireball, lifetime), FALSE)) + { + self.BUTTON_ATCK = TRUE; + if(random() < 0.02) self.bot_primary_fireballmooth = 0; + } + } + else + { + if(bot_aim(WEP_CVAR_SEC(fireball, speed), WEP_CVAR_SEC(fireball, speed_up), WEP_CVAR_SEC(fireball, lifetime), TRUE)) + { + self.BUTTON_ATCK2 = TRUE; + if(random() < 0.01) self.bot_primary_fireballmooth = 1; + } + } + + return TRUE; + } + case WR_THINK: + { + if(self.BUTTON_ATCK) + { + if(time >= self.fireball_primarytime) + if(weapon_prepareattack(0, WEP_CVAR_PRI(fireball, refire))) + { + W_Fireball_Attack1_Frame0(); + self.fireball_primarytime = time + WEP_CVAR_PRI(fireball, refire2) * W_WeaponRateFactor(); + } + } + else if(self.BUTTON_ATCK2) + { + if(weapon_prepareattack(1, WEP_CVAR_SEC(fireball, refire))) + { + W_Fireball_Attack2(); + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(fireball, animtime), w_ready); + } + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/weapons/g_fireball.md3"); + precache_model("models/weapons/v_fireball.md3"); + precache_model("models/weapons/h_fireball.iqm"); + precache_model("models/sphere/sphere.md3"); + precache_sound("weapons/fireball_fire.wav"); + precache_sound("weapons/fireball_fire2.wav"); + precache_sound("weapons/fireball_prefire2.wav"); + FIREBALL_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_SETUP: + { + self.ammo_field = ammo_none; + return TRUE; + } + case WR_CHECKAMMO1: + case WR_CHECKAMMO2: + { + return TRUE; // fireball has infinite ammo + } + case WR_CONFIG: + { + FIREBALL_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_RESETPLAYER: + { + self.fireball_primarytime = time; + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + if(w_deathtype & HITTYPE_SECONDARY) + return WEAPON_FIREBALL_SUICIDE_FIREMINE; + else + return WEAPON_FIREBALL_SUICIDE_BLAST; + } + case WR_KILLMESSAGE: + { + if(w_deathtype & HITTYPE_SECONDARY) + return WEAPON_FIREBALL_MURDER_FIREMINE; + else + return WEAPON_FIREBALL_MURDER_BLAST; + } + } + return FALSE; +} +#endif +#ifdef CSQC +float W_Fireball(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + vector org2; + if(w_deathtype & HITTYPE_SECONDARY) + { + // firemine goes out silently + } + else + { + org2 = w_org + w_backoff * 16; + pointparticles(particleeffectnum("fireball_explode"), org2, '0 0 0', 1); + if(!w_issilent) + sound(self, CH_SHOTS, "weapons/fireball_impact2.wav", VOL_BASE, ATTEN_NORM * 0.25); // long range boom + } + + return TRUE; + } + case WR_INIT: + { + precache_sound("weapons/fireball_impact2.wav"); + return TRUE; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_hagar.qc b/qcsrc/common/weapons/w_hagar.qc new file mode 100644 index 0000000000..fe3abf0a9a --- /dev/null +++ b/qcsrc/common/weapons/w_hagar.qc @@ -0,0 +1,559 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ HAGAR, +/* function */ W_Hagar, +/* ammotype */ ammo_rockets, +/* impulse */ 8, +/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH, +/* rating */ BOT_PICKUP_RATING_MID, +/* color */ '1 1 0.5', +/* modelname */ "hagar", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairhagar 0.8", +/* wepimg */ "weaponhagar", +/* refname */ "hagar", +/* wepname */ _("Hagar") +); + +#define HAGAR_SETTINGS(w_cvar,w_prop) HAGAR_SETTINGS_LIST(w_cvar, w_prop, HAGAR, hagar) +#define HAGAR_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, BOTH, ammo) \ + w_cvar(id, sn, BOTH, damage) \ + w_cvar(id, sn, BOTH, edgedamage) \ + w_cvar(id, sn, BOTH, force) \ + w_cvar(id, sn, BOTH, radius) \ + w_cvar(id, sn, BOTH, refire) \ + w_cvar(id, sn, BOTH, speed) \ + w_cvar(id, sn, BOTH, spread) \ + w_cvar(id, sn, BOTH, damageforcescale) \ + w_cvar(id, sn, BOTH, health) \ + w_cvar(id, sn, PRI, lifetime) \ + w_cvar(id, sn, SEC, load) \ + w_cvar(id, sn, SEC, load_max) \ + w_cvar(id, sn, SEC, load_abort) \ + w_cvar(id, sn, SEC, load_animtime) \ + w_cvar(id, sn, SEC, load_hold) \ + w_cvar(id, sn, SEC, load_speed) \ + w_cvar(id, sn, SEC, load_releasedeath) \ + w_cvar(id, sn, SEC, load_spread) \ + w_cvar(id, sn, SEC, load_spread_bias) \ + w_cvar(id, sn, SEC, load_linkexplode) \ + w_cvar(id, sn, SEC, lifetime_min) \ + w_cvar(id, sn, SEC, lifetime_rand) \ + w_cvar(id, sn, NONE, secondary) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +HAGAR_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_hagar(void) { weapon_defaultspawnfunc(WEP_HAGAR); } + +// NO bounce protection, as bounces are limited! + +void W_Hagar_Explode(void) +{ + self.event_damage = func_null; + RadiusDamage(self, self.realowner, WEP_CVAR_PRI(hagar, damage), WEP_CVAR_PRI(hagar, edgedamage), WEP_CVAR_PRI(hagar, radius), world, world, WEP_CVAR_PRI(hagar, force), self.projectiledeathtype, other); + + remove(self); +} + +void W_Hagar_Explode2(void) +{ + self.event_damage = func_null; + RadiusDamage(self, self.realowner, WEP_CVAR_SEC(hagar, damage), WEP_CVAR_SEC(hagar, edgedamage), WEP_CVAR_SEC(hagar, radius), world, world, WEP_CVAR_SEC(hagar, force), self.projectiledeathtype, other); + + remove(self); +} + +void W_Hagar_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if(self.health <= 0) + return; + + float is_linkexplode = ( ((inflictor.owner != world) ? (inflictor.owner == self.owner) : TRUE) + && (inflictor.projectiledeathtype & HITTYPE_SECONDARY) + && (self.projectiledeathtype & HITTYPE_SECONDARY)); + + if(is_linkexplode) + is_linkexplode = (is_linkexplode && WEP_CVAR_SEC(hagar, load_linkexplode)); + else + is_linkexplode = -1; // not secondary load, so continue as normal without exception. + + if(!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, is_linkexplode)) + return; // g_projectiles_damage says to halt + + self.health = self.health - damage; + self.angles = vectoangles(self.velocity); + + if(self.health <= 0) + W_PrepareExplosionByDamage(attacker, self.think); +} + +void W_Hagar_Touch(void) +{ + PROJECTILE_TOUCH; + self.use(); +} + +void W_Hagar_Touch2(void) +{ + PROJECTILE_TOUCH; + + if(self.cnt > 0 || other.takedamage == DAMAGE_AIM) { + self.use(); + } else { + self.cnt++; + pointparticles(particleeffectnum("hagar_bounce"), self.origin, self.velocity, 1); + self.angles = vectoangles(self.velocity); + self.owner = world; + self.projectiledeathtype |= HITTYPE_BOUNCE; + } +} + +void W_Hagar_Attack(void) +{ + entity missile; + + W_DecreaseAmmo(WEP_CVAR_PRI(hagar, ammo)); + + W_SetupShot(self, FALSE, 2, "weapons/hagar_fire.wav", CH_WEAPON_A, WEP_CVAR_PRI(hagar, damage)); + + pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + missile = spawn(); + missile.owner = missile.realowner = self; + missile.classname = "missile"; + missile.bot_dodge = TRUE; + missile.bot_dodgerating = WEP_CVAR_PRI(hagar, damage); + + missile.takedamage = DAMAGE_YES; + missile.health = WEP_CVAR_PRI(hagar, health); + missile.damageforcescale = WEP_CVAR_PRI(hagar, damageforcescale); + missile.event_damage = W_Hagar_Damage; + missile.damagedbycontents = TRUE; + + missile.touch = W_Hagar_Touch; + missile.use = W_Hagar_Explode; + missile.think = adaptor_think2use_hittype_splash; + missile.nextthink = time + WEP_CVAR_PRI(hagar, lifetime); + PROJECTILE_MAKETRIGGER(missile); + missile.projectiledeathtype = WEP_HAGAR; + setorigin(missile, w_shotorg); + setsize(missile, '0 0 0', '0 0 0'); + + missile.movetype = MOVETYPE_FLY; + W_SetupProjVelocity_PRI(missile, hagar); + + missile.angles = vectoangles(missile.velocity); + missile.flags = FL_PROJECTILE; + missile.missile_flags = MIF_SPLASH; + + CSQCProjectile(missile, TRUE, PROJECTILE_HAGAR, TRUE); + + other = missile; MUTATOR_CALLHOOK(EditProjectile); +} + +void W_Hagar_Attack2(void) +{ + entity missile; + + W_DecreaseAmmo(WEP_CVAR_SEC(hagar, ammo)); + + W_SetupShot(self, FALSE, 2, "weapons/hagar_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage)); + + pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + missile = spawn(); + missile.owner = missile.realowner = self; + missile.classname = "missile"; + missile.bot_dodge = TRUE; + missile.bot_dodgerating = WEP_CVAR_SEC(hagar, damage); + + missile.takedamage = DAMAGE_YES; + missile.health = WEP_CVAR_SEC(hagar, health); + missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale); + missile.event_damage = W_Hagar_Damage; + missile.damagedbycontents = TRUE; + + missile.touch = W_Hagar_Touch2; + missile.cnt = 0; + missile.use = W_Hagar_Explode2; + missile.think = adaptor_think2use_hittype_splash; + missile.nextthink = time + WEP_CVAR_SEC(hagar, lifetime_min) + random() * WEP_CVAR_SEC(hagar, lifetime_rand); + PROJECTILE_MAKETRIGGER(missile); + missile.projectiledeathtype = WEP_HAGAR | HITTYPE_SECONDARY; + setorigin(missile, w_shotorg); + setsize(missile, '0 0 0', '0 0 0'); + + missile.movetype = MOVETYPE_BOUNCEMISSILE; + W_SetupProjVelocity_SEC(missile, hagar); + + missile.angles = vectoangles(missile.velocity); + missile.flags = FL_PROJECTILE; + missile.missile_flags = MIF_SPLASH; + + CSQCProjectile(missile, TRUE, PROJECTILE_HAGAR_BOUNCING, TRUE); + + other = missile; MUTATOR_CALLHOOK(EditProjectile); +} + +.float hagar_loadstep, hagar_loadblock, hagar_loadbeep, hagar_warning; +void W_Hagar_Attack2_Load_Release(void) +{ + // time to release the rockets we've loaded + + entity missile; + float counter, shots, spread_pershot; + vector s; + vector forward, right, up; + + if(!self.hagar_load) + return; + + weapon_prepareattack_do(1, WEP_CVAR_SEC(hagar, refire)); + + W_SetupShot(self, FALSE, 2, "weapons/hagar_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage)); + pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + forward = v_forward; + right = v_right; + up = v_up; + + shots = self.hagar_load; + missile = world; + for(counter = 0; counter < shots; ++counter) + { + missile = spawn(); + missile.owner = missile.realowner = self; + missile.classname = "missile"; + missile.bot_dodge = TRUE; + missile.bot_dodgerating = WEP_CVAR_SEC(hagar, damage); + + missile.takedamage = DAMAGE_YES; + missile.health = WEP_CVAR_SEC(hagar, health); + missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale); + missile.event_damage = W_Hagar_Damage; + missile.damagedbycontents = TRUE; + + missile.touch = W_Hagar_Touch; // not bouncy + missile.use = W_Hagar_Explode2; + missile.think = adaptor_think2use_hittype_splash; + missile.nextthink = time + WEP_CVAR_SEC(hagar, lifetime_min) + random() * WEP_CVAR_SEC(hagar, lifetime_rand); + PROJECTILE_MAKETRIGGER(missile); + missile.projectiledeathtype = WEP_HAGAR | HITTYPE_SECONDARY; + setorigin(missile, w_shotorg); + setsize(missile, '0 0 0', '0 0 0'); + missile.movetype = MOVETYPE_FLY; + missile.missile_flags = MIF_SPLASH; + + // per-shot spread calculation: the more shots there are, the less spread is applied (based on the bias cvar) + spread_pershot = ((shots - 1) / (WEP_CVAR_SEC(hagar, load_max) - 1)); + spread_pershot = (1 - (spread_pershot * WEP_CVAR_SEC(hagar, load_spread_bias))); + spread_pershot = (WEP_CVAR_SEC(hagar, spread) * spread_pershot * g_weaponspreadfactor); + + // pattern spread calculation + s = '0 0 0'; + if(counter == 0) + s = '0 0 0'; + else + { + makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1))); + s_y = v_forward_x; + s_z = v_forward_y; + } + s = s * WEP_CVAR_SEC(hagar, load_spread) * g_weaponspreadfactor; + + W_SetupProjVelocity_Explicit(missile, w_shotdir + right * s_y + up * s_z, v_up, WEP_CVAR_SEC(hagar, speed), 0, 0, spread_pershot, FALSE); + + missile.angles = vectoangles(missile.velocity); + missile.flags = FL_PROJECTILE; + + CSQCProjectile(missile, TRUE, PROJECTILE_HAGAR, TRUE); + + other = missile; MUTATOR_CALLHOOK(EditProjectile); + } + + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(hagar, load_animtime), w_ready); + self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, refire) * W_WeaponRateFactor(); + self.hagar_load = 0; +} + +void W_Hagar_Attack2_Load(void) +{ + // loadable hagar secondary attack, must always run each frame + + if(time < game_starttime) + return; + + float loaded, enough_ammo; + loaded = self.hagar_load >= WEP_CVAR_SEC(hagar, load_max); + + // this is different than WR_CHECKAMMO when it comes to reloading + if(autocvar_g_balance_hagar_reload_ammo) + enough_ammo = self.(weapon_load[WEP_HAGAR]) >= WEP_CVAR_SEC(hagar, ammo); + else + enough_ammo = self.WEP_AMMO(HAGAR) >= WEP_CVAR_SEC(hagar, ammo); + + if(self.BUTTON_ATCK2) + { + if(self.BUTTON_ATCK && WEP_CVAR_SEC(hagar, load_abort)) + { + if(self.hagar_load) + { + // if we pressed primary fire while loading, unload all rockets and abort + self.weaponentity.state = WS_READY; + W_DecreaseAmmo(WEP_CVAR_SEC(hagar, ammo) * self.hagar_load * -1); // give back ammo + self.hagar_load = 0; + sound(self, CH_WEAPON_A, "weapons/hagar_beep.wav", VOL_BASE, ATTN_NORM); + + // pause until we can load rockets again, once we re-press the alt fire button + self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_speed) * W_WeaponRateFactor(); + + // require letting go of the alt fire button before we can load again + self.hagar_loadblock = TRUE; + } + } + else + { + // check if we can attempt to load another rocket + if(!loaded && enough_ammo) + { + if(!self.hagar_loadblock && self.hagar_loadstep < time) + { + W_DecreaseAmmo(WEP_CVAR_SEC(hagar, ammo)); + self.weaponentity.state = WS_INUSE; + self.hagar_load += 1; + sound(self, CH_WEAPON_B, "weapons/hagar_load.wav", VOL_BASE * 0.8, ATTN_NORM); // sound is too loud according to most + + if(self.hagar_load >= WEP_CVAR_SEC(hagar, load_max)) + self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_hold) * W_WeaponRateFactor(); + else + self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_speed) * W_WeaponRateFactor(); + } + } + else if(!self.hagar_loadbeep && self.hagar_load) // prevents the beep from playing each frame + { + // if this is the last rocket we can load, play a beep sound to notify the player + sound(self, CH_WEAPON_A, "weapons/hagar_beep.wav", VOL_BASE, ATTN_NORM); + self.hagar_loadbeep = TRUE; + } + } + } + else if(self.hagar_loadblock) + { + // the alt fire button has been released, so re-enable loading if blocked + self.hagar_loadblock = FALSE; + } + + if(self.hagar_load) + { + // play warning sound if we're about to release + if((loaded || !enough_ammo) && self.hagar_loadstep - 0.5 < time && WEP_CVAR_SEC(hagar, load_hold) >= 0) + { + if(!self.hagar_warning && self.hagar_load) // prevents the beep from playing each frame + { + // we're about to automatically release after holding time, play a beep sound to notify the player + sound(self, CH_WEAPON_A, "weapons/hagar_beep.wav", VOL_BASE, ATTN_NORM); + self.hagar_warning = TRUE; + } + } + + // release if player let go of button or if they've held it in too long + if(!self.BUTTON_ATCK2 || ((loaded || !enough_ammo) && self.hagar_loadstep < time && WEP_CVAR_SEC(hagar, load_hold) >= 0)) + { + self.weaponentity.state = WS_READY; + W_Hagar_Attack2_Load_Release(); + } + } + else + { + self.hagar_loadbeep = FALSE; + self.hagar_warning = FALSE; + } + + // we aren't checking ammo during an attack, so we must do it here + if(!(WEP_ACTION(self.weapon, WR_CHECKAMMO1) + WEP_ACTION(self.weapon, WR_CHECKAMMO2))) + { + // note: this doesn't force the switch + W_SwitchToOtherWeapon(self); + return; + } +} + +float W_Hagar(float req) +{ + float ammo_amount; + switch(req) + { + case WR_AIM: + { + if(random()>0.15) + self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(hagar, speed), 0, WEP_CVAR_PRI(hagar, lifetime), FALSE); + else // not using secondary_speed since these are only 15% and should cause some ricochets without re-aiming + self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_PRI(hagar, speed), 0, WEP_CVAR_PRI(hagar, lifetime), FALSE); + + return TRUE; + } + case WR_THINK: + { + float loadable_secondary; + loadable_secondary = (WEP_CVAR_SEC(hagar, load) && WEP_CVAR(hagar, secondary)); + + if(loadable_secondary) + W_Hagar_Attack2_Load(); // must always run each frame + if(autocvar_g_balance_hagar_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo))) // forced reload + WEP_ACTION(self.weapon, WR_RELOAD); + else if(self.BUTTON_ATCK && !self.hagar_load && !self.hagar_loadblock) // not while secondary is loaded or awaiting reset + { + if(weapon_prepareattack(0, WEP_CVAR_PRI(hagar, refire))) + { + W_Hagar_Attack(); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hagar, refire), w_ready); + } + } + else if(self.BUTTON_ATCK2 && !loadable_secondary && WEP_CVAR(hagar, secondary)) + { + if(weapon_prepareattack(1, WEP_CVAR_SEC(hagar, refire))) + { + W_Hagar_Attack2(); + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(hagar, refire), w_ready); + } + } + + return TRUE; + } + case WR_GONETHINK: + { + // we lost the weapon and want to prepare switching away + if(self.hagar_load) + { + self.weaponentity.state = WS_READY; + W_Hagar_Attack2_Load_Release(); + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/weapons/g_hagar.md3"); + precache_model("models/weapons/v_hagar.md3"); + precache_model("models/weapons/h_hagar.iqm"); + precache_sound("weapons/hagar_fire.wav"); + precache_sound("weapons/hagar_load.wav"); + precache_sound("weapons/hagar_beep.wav"); + HAGAR_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_SETUP: + { + self.hagar_loadblock = FALSE; + + if(self.hagar_load) + { + W_DecreaseAmmo(WEP_CVAR_SEC(hagar, ammo) * self.hagar_load * -1); // give back ammo if necessary + self.hagar_load = 0; + } + + return TRUE; + } + case WR_CHECKAMMO1: + { + ammo_amount = self.WEP_AMMO(HAGAR) >= WEP_CVAR_PRI(hagar, ammo); + ammo_amount += self.(weapon_load[WEP_HAGAR]) >= WEP_CVAR_PRI(hagar, ammo); + return ammo_amount; + } + case WR_CHECKAMMO2: + { + ammo_amount = self.WEP_AMMO(HAGAR) >= WEP_CVAR_SEC(hagar, ammo); + ammo_amount += self.(weapon_load[WEP_HAGAR]) >= WEP_CVAR_SEC(hagar, ammo); + return ammo_amount; + } + case WR_CONFIG: + { + HAGAR_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_RESETPLAYER: + { + self.hagar_load = 0; + return TRUE; + } + case WR_PLAYERDEATH: + { + // if we have any rockets loaded when we die, release them + if(self.hagar_load && WEP_CVAR_SEC(hagar, load_releasedeath)) + W_Hagar_Attack2_Load_Release(); + + return TRUE; + } + case WR_RELOAD: + { + if(!self.hagar_load) // require releasing loaded rockets first + W_Reload(min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo)), "weapons/reload.wav"); + + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + return WEAPON_HAGAR_SUICIDE; + } + case WR_KILLMESSAGE: + { + if(w_deathtype & HITTYPE_SECONDARY) + return WEAPON_HAGAR_MURDER_BURST; + else + return WEAPON_HAGAR_MURDER_SPRAY; + } + } + return FALSE; +} +#endif +#ifdef CSQC +float W_Hagar(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 6; + pointparticles(particleeffectnum("hagar_explode"), org2, '0 0 0', 1); + if(!w_issilent) + { + if(w_random<0.15) + sound(self, CH_SHOTS, "weapons/hagexp1.wav", VOL_BASE, ATTN_NORM); + else if(w_random<0.7) + sound(self, CH_SHOTS, "weapons/hagexp2.wav", VOL_BASE, ATTN_NORM); + else + sound(self, CH_SHOTS, "weapons/hagexp3.wav", VOL_BASE, ATTN_NORM); + } + + return TRUE; + } + case WR_INIT: + { + precache_sound("weapons/hagexp1.wav"); + precache_sound("weapons/hagexp2.wav"); + precache_sound("weapons/hagexp3.wav"); + return TRUE; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_hlac.qc b/qcsrc/common/weapons/w_hlac.qc new file mode 100644 index 0000000000..d3dbed2f9c --- /dev/null +++ b/qcsrc/common/weapons/w_hlac.qc @@ -0,0 +1,313 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ HLAC, +/* function */ W_HLAC, +/* ammotype */ ammo_cells, +/* impulse */ 6, +/* flags */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH, +/* rating */ BOT_PICKUP_RATING_MID, +/* color */ '0 1 0', +/* modelname */ "hlac", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairhlac 0.6", +/* wepimg */ "weaponhlac", +/* refname */ "hlac", +/* wepname */ _("Heavy Laser Assault Cannon") +); + +#define HLAC_SETTINGS(w_cvar,w_prop) HLAC_SETTINGS_LIST(w_cvar, w_prop, HLAC, hlac) +#define HLAC_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, BOTH, ammo) \ + w_cvar(id, sn, BOTH, animtime) \ + w_cvar(id, sn, BOTH, damage) \ + w_cvar(id, sn, BOTH, edgedamage) \ + w_cvar(id, sn, BOTH, force) \ + w_cvar(id, sn, BOTH, lifetime) \ + w_cvar(id, sn, BOTH, radius) \ + w_cvar(id, sn, BOTH, refire) \ + w_cvar(id, sn, BOTH, speed) \ + w_cvar(id, sn, BOTH, spread_crouchmod) \ + w_cvar(id, sn, PRI, spread_add) \ + w_cvar(id, sn, PRI, spread_max) \ + w_cvar(id, sn, PRI, spread_min) \ + w_cvar(id, sn, NONE, secondary) \ + w_cvar(id, sn, SEC, shots) \ + w_cvar(id, sn, SEC, spread) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +HLAC_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_hlac(void) { weapon_defaultspawnfunc(WEP_HLAC); } + +void W_HLAC_Touch(void) +{ + float isprimary; + + PROJECTILE_TOUCH; + + self.event_damage = func_null; + + isprimary = !(self.projectiledeathtype & HITTYPE_SECONDARY); + + RadiusDamage(self, self.realowner, WEP_CVAR_BOTH(hlac, isprimary, damage), WEP_CVAR_BOTH(hlac, isprimary, edgedamage), WEP_CVAR_BOTH(hlac, isprimary, radius), world, world, WEP_CVAR_BOTH(hlac, isprimary, force), self.projectiledeathtype, other); + + remove(self); +} + +void W_HLAC_Attack(void) +{ + entity missile; + float spread; + + W_DecreaseAmmo(WEP_CVAR_PRI(hlac, ammo)); + + spread = WEP_CVAR_PRI(hlac, spread_min) + (WEP_CVAR_PRI(hlac, spread_add) * self.misc_bulletcounter); + spread = min(spread,WEP_CVAR_PRI(hlac, spread_max)); + if(self.crouch) + spread = spread * WEP_CVAR_PRI(hlac, spread_crouchmod); + + W_SetupShot(self, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_A, WEP_CVAR_PRI(hlac, damage)); + pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + if(!autocvar_g_norecoil) + { + self.punchangle_x = random() - 0.5; + self.punchangle_y = random() - 0.5; + } + + missile = spawn(); + missile.owner = missile.realowner = self; + missile.classname = "hlacbolt"; + missile.bot_dodge = TRUE; + + missile.bot_dodgerating = WEP_CVAR_PRI(hlac, damage); + + missile.movetype = MOVETYPE_FLY; + PROJECTILE_MAKETRIGGER(missile); + + setorigin(missile, w_shotorg); + setsize(missile, '0 0 0', '0 0 0'); + + W_SetupProjVelocity_Basic(missile, WEP_CVAR_PRI(hlac, speed), spread); + //missile.angles = vectoangles(missile.velocity); // csqc + + missile.touch = W_HLAC_Touch; + missile.think = SUB_Remove; + + missile.nextthink = time + WEP_CVAR_PRI(hlac, lifetime); + + missile.flags = FL_PROJECTILE; + missile.projectiledeathtype = WEP_HLAC; + + CSQCProjectile(missile, TRUE, PROJECTILE_HLAC, TRUE); + + other = missile; MUTATOR_CALLHOOK(EditProjectile); +} + +void W_HLAC_Attack2(void) +{ + entity missile; + float spread; + + spread = WEP_CVAR_SEC(hlac, spread); + + + if(self.crouch) + spread = spread * WEP_CVAR_SEC(hlac, spread_crouchmod); + + W_SetupShot(self, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(hlac, damage)); + pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + missile = spawn(); + missile.owner = missile.realowner = self; + missile.classname = "hlacbolt"; + missile.bot_dodge = TRUE; + + missile.bot_dodgerating = WEP_CVAR_SEC(hlac, damage); + + missile.movetype = MOVETYPE_FLY; + PROJECTILE_MAKETRIGGER(missile); + + setorigin(missile, w_shotorg); + setsize(missile, '0 0 0', '0 0 0'); + + W_SetupProjVelocity_Basic(missile, WEP_CVAR_SEC(hlac, speed), spread); + //missile.angles = vectoangles(missile.velocity); // csqc + + missile.touch = W_HLAC_Touch; + missile.think = SUB_Remove; + + missile.nextthink = time + WEP_CVAR_SEC(hlac, lifetime); + + missile.flags = FL_PROJECTILE; + missile.missile_flags = MIF_SPLASH; + missile.projectiledeathtype = WEP_HLAC | HITTYPE_SECONDARY; + + CSQCProjectile(missile, TRUE, PROJECTILE_HLAC, TRUE); + + other = missile; MUTATOR_CALLHOOK(EditProjectile); +} + +// weapon frames +void W_HLAC_Attack_Frame(void) +{ + if(self.weapon != self.switchweapon) // abort immediately if switching + { + w_ready(); + return; + } + + if(self.BUTTON_ATCK) + { + if(!WEP_ACTION(self.weapon, WR_CHECKAMMO1)) + if(!(self.items & IT_UNLIMITED_WEAPON_AMMO)) + { + W_SwitchWeapon_Force(self, w_getbestweapon(self)); + w_ready(); + return; + } + + ATTACK_FINISHED(self) = time + WEP_CVAR_PRI(hlac, refire) * W_WeaponRateFactor(); + W_HLAC_Attack(); + self.misc_bulletcounter = self.misc_bulletcounter + 1; + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame); + } + else + { + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hlac, animtime), w_ready); + } +} + +void W_HLAC_Attack2_Frame(void) +{ + float i; + + W_DecreaseAmmo(WEP_CVAR_SEC(hlac, ammo)); + + for(i=WEP_CVAR_SEC(hlac, shots);i>0;--i) + W_HLAC_Attack2(); + + if(!autocvar_g_norecoil) + { + self.punchangle_x = random() - 0.5; + self.punchangle_y = random() - 0.5; + } +} + +float W_HLAC(float req) +{ + float ammo_amount; + switch(req) + { + case WR_AIM: + { + self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(hlac, speed), 0, WEP_CVAR_PRI(hlac, lifetime), FALSE); + return TRUE; + } + case WR_THINK: + { + if(autocvar_g_balance_hlac_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(hlac, ammo), WEP_CVAR_SEC(hlac, ammo))) // forced reload + WEP_ACTION(self.weapon, WR_RELOAD); + else if(self.BUTTON_ATCK) + { + if(weapon_prepareattack(0, WEP_CVAR_PRI(hlac, refire))) + { + self.misc_bulletcounter = 0; + W_HLAC_Attack(); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame); + } + } + + else if(self.BUTTON_ATCK2 && WEP_CVAR(hlac, secondary)) + { + if(weapon_prepareattack(1, WEP_CVAR_SEC(hlac, refire))) + { + W_HLAC_Attack2_Frame(); + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(hlac, animtime), w_ready); + } + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/weapons/g_hlac.md3"); + precache_model("models/weapons/v_hlac.md3"); + precache_model("models/weapons/h_hlac.iqm"); + precache_sound("weapons/lasergun_fire.wav"); + HLAC_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_CHECKAMMO1: + { + ammo_amount = self.WEP_AMMO(HLAC) >= WEP_CVAR_PRI(hlac, ammo); + ammo_amount += self.(weapon_load[WEP_HLAC]) >= WEP_CVAR_PRI(hlac, ammo); + return ammo_amount; + } + case WR_CHECKAMMO2: + { + ammo_amount = self.WEP_AMMO(HLAC) >= WEP_CVAR_SEC(hlac, ammo); + ammo_amount += self.(weapon_load[WEP_HLAC]) >= WEP_CVAR_SEC(hlac, ammo); + return ammo_amount; + } + case WR_CONFIG: + { + HLAC_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_RELOAD: + { + W_Reload(min(WEP_CVAR_PRI(hlac, ammo), WEP_CVAR_SEC(hlac, ammo)), "weapons/reload.wav"); + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + return WEAPON_HLAC_SUICIDE; + } + case WR_KILLMESSAGE: + { + return WEAPON_HLAC_MURDER; + } + } + return FALSE; +} +#endif +#ifdef CSQC +float W_HLAC(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 6; + pointparticles(particleeffectnum("laser_impact"), org2, w_backoff * 1000, 1); + if(!w_issilent) + sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM); + + return TRUE; + } + case WR_INIT: + { + precache_sound("weapons/laserimpact.wav"); + return TRUE; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_hook.qc b/qcsrc/common/weapons/w_hook.qc new file mode 100644 index 0000000000..931d3e0ad1 --- /dev/null +++ b/qcsrc/common/weapons/w_hook.qc @@ -0,0 +1,367 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ HOOK, +/* function */ W_Hook, +/* ammotype */ ammo_fuel, +/* impulse */ 0, +/* flags */ WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH, +/* rating */ 0, +/* color */ '0 0.5 0', +/* modelname */ "hookgun", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairhook 0.5", +/* wepimg */ "weaponhook", +/* refname */ "hook", +/* wepname */ _("Grappling Hook") +); + +#define HOOK_SETTINGS(w_cvar,w_prop) HOOK_SETTINGS_LIST(w_cvar, w_prop, HOOK, hook) +#define HOOK_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, BOTH, animtime) \ + w_cvar(id, sn, BOTH, refire) \ + w_cvar(id, sn, PRI, ammo) \ + w_cvar(id, sn, PRI, hooked_ammo) \ + w_cvar(id, sn, PRI, hooked_time_free) \ + w_cvar(id, sn, PRI, hooked_time_max) \ + w_cvar(id, sn, SEC, damage) \ + w_cvar(id, sn, SEC, duration) \ + w_cvar(id, sn, SEC, edgedamage) \ + w_cvar(id, sn, SEC, force) \ + w_cvar(id, sn, SEC, gravity) \ + w_cvar(id, sn, SEC, lifetime) \ + w_cvar(id, sn, SEC, power) \ + w_cvar(id, sn, SEC, radius) \ + w_cvar(id, sn, SEC, speed) \ + w_cvar(id, sn, SEC, health) \ + w_cvar(id, sn, SEC, damageforcescale) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +HOOK_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) + +.float dmg; +.float dmg_edge; +.float dmg_radius; +.float dmg_force; +.float dmg_power; +.float dmg_duration; +.float dmg_last; +.float hook_refire; +.float hook_time_hooked; +.float hook_time_fueldecrease; +#endif +#else +#ifdef SVQC + +void spawnfunc_weapon_hook(void) +{ + if(g_grappling_hook) // offhand hook + { + startitem_failed = TRUE; + remove(self); + return; + } + weapon_defaultspawnfunc(WEP_HOOK); +} + +void W_Hook_ExplodeThink(void) +{ + float dt, dmg_remaining_next, f; + + dt = time - self.teleport_time; + dmg_remaining_next = pow(bound(0, 1 - dt / self.dmg_duration, 1), self.dmg_power); + + f = self.dmg_last - dmg_remaining_next; + self.dmg_last = dmg_remaining_next; + + RadiusDamage(self, self.realowner, self.dmg * f, self.dmg_edge * f, self.dmg_radius, self.realowner, world, self.dmg_force * f, self.projectiledeathtype, world); + self.projectiledeathtype |= HITTYPE_BOUNCE; + //RadiusDamage(self, world, self.dmg * f, self.dmg_edge * f, self.dmg_radius, world, world, self.dmg_force * f, self.projectiledeathtype, world); + + if(dt < self.dmg_duration) + self.nextthink = time + 0.05; // soon + else + remove(self); +} + +void W_Hook_Explode2(void) +{ + self.event_damage = func_null; + self.touch = func_null; + self.effects |= EF_NODRAW; + + self.think = W_Hook_ExplodeThink; + self.nextthink = time; + self.dmg = WEP_CVAR_SEC(hook, damage); + self.dmg_edge = WEP_CVAR_SEC(hook, edgedamage); + self.dmg_radius = WEP_CVAR_SEC(hook, radius); + self.dmg_force = WEP_CVAR_SEC(hook, force); + self.dmg_power = WEP_CVAR_SEC(hook, power); + self.dmg_duration = WEP_CVAR_SEC(hook, duration); + self.teleport_time = time; + self.dmg_last = 1; + self.movetype = MOVETYPE_NONE; +} + +void W_Hook_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if(self.health <= 0) + return; + + if(!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions + return; // g_projectiles_damage says to halt + + self.health = self.health - damage; + + if(self.health <= 0) + W_PrepareExplosionByDamage(self.realowner, W_Hook_Explode2); +} + +void W_Hook_Touch2(void) +{ + PROJECTILE_TOUCH; + self.use(); +} + +void W_Hook_Attack2(void) +{ + entity gren; + + //W_DecreaseAmmo(WEP_CVAR_SEC(hook, ammo)); // WEAPONTODO: Figure out how to handle ammo with hook secondary (gravitybomb) + W_SetupShot(self, FALSE, 4, "weapons/hookbomb_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(hook, damage)); + + gren = spawn(); + gren.owner = gren.realowner = self; + gren.classname = "hookbomb"; + gren.bot_dodge = TRUE; + gren.bot_dodgerating = WEP_CVAR_SEC(hook, damage); + gren.movetype = MOVETYPE_TOSS; + PROJECTILE_MAKETRIGGER(gren); + gren.projectiledeathtype = WEP_HOOK | HITTYPE_SECONDARY; + setorigin(gren, w_shotorg); + setsize(gren, '0 0 0', '0 0 0'); + + gren.nextthink = time + WEP_CVAR_SEC(hook, lifetime); + gren.think = adaptor_think2use_hittype_splash; + gren.use = W_Hook_Explode2; + gren.touch = W_Hook_Touch2; + + gren.takedamage = DAMAGE_YES; + gren.health = WEP_CVAR_SEC(hook, health); + gren.damageforcescale = WEP_CVAR_SEC(hook, damageforcescale); + gren.event_damage = W_Hook_Damage; + gren.damagedbycontents = TRUE; + gren.missile_flags = MIF_SPLASH | MIF_ARC; + + gren.velocity = '0 0 1' * WEP_CVAR_SEC(hook, speed); + if(autocvar_g_projectiles_newton_style) + gren.velocity = gren.velocity + self.velocity; + + gren.gravity = WEP_CVAR_SEC(hook, gravity); + //W_SetupProjVelocity_Basic(gren); // just falling down! + + gren.angles = '0 0 0'; + gren.flags = FL_PROJECTILE; + + CSQCProjectile(gren, TRUE, PROJECTILE_HOOKBOMB, TRUE); + + other = gren; MUTATOR_CALLHOOK(EditProjectile); +} + +float W_Hook(float req) +{ + float hooked_time_max, hooked_fuel; + + switch(req) + { + case WR_AIM: + { + // no bot AI for hook (yet?) + return TRUE; + } + case WR_THINK: + { + if(self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK)) + { + if(!self.hook) + if(!(self.hook_state & HOOK_WAITING_FOR_RELEASE)) + if(!(self.hook_state & HOOK_FIRING)) + if(time > self.hook_refire) + if(weapon_prepareattack(0, -1)) + { + W_DecreaseAmmo(WEP_CVAR_PRI(hook, ammo)); + self.hook_state |= HOOK_FIRING; + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hook, animtime), w_ready); + } + } + + if(self.BUTTON_ATCK2) + { + if(weapon_prepareattack(1, WEP_CVAR_SEC(hook, refire))) + { + W_Hook_Attack2(); + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(hook, animtime), w_ready); + } + } + + if(self.hook) + { + // if hooked, no bombs, and increase the timer + self.hook_refire = max(self.hook_refire, time + WEP_CVAR_PRI(hook, refire) * W_WeaponRateFactor()); + + // hook also inhibits health regeneration, but only for 1 second + if(!(self.items & IT_UNLIMITED_WEAPON_AMMO)) + self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen); + } + + if(self.hook && self.hook.state == 1) + { + hooked_time_max = WEP_CVAR_PRI(hook, hooked_time_max); + if(hooked_time_max > 0) + { + if( time > self.hook_time_hooked + hooked_time_max ) + self.hook_state |= HOOK_REMOVING; + } + + hooked_fuel = WEP_CVAR_PRI(hook, hooked_ammo); + if(hooked_fuel > 0) + { + if( time > self.hook_time_fueldecrease ) + { + if(!(self.items & IT_UNLIMITED_WEAPON_AMMO)) + { + if( self.ammo_fuel >= (time - self.hook_time_fueldecrease) * hooked_fuel ) + { + W_DecreaseAmmo((time - self.hook_time_fueldecrease) * hooked_fuel); + self.hook_time_fueldecrease = time; + // decrease next frame again + } + else + { + self.ammo_fuel = 0; + self.hook_state |= HOOK_REMOVING; + W_SwitchWeapon_Force(self, w_getbestweapon(self)); + } + } + } + } + } + else + { + self.hook_time_hooked = time; + self.hook_time_fueldecrease = time + WEP_CVAR_PRI(hook, hooked_time_free); + } + + if(self.BUTTON_CROUCH) + { + self.hook_state &= ~HOOK_PULLING; + if(self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK)) + self.hook_state &= ~HOOK_RELEASING; + else + self.hook_state |= HOOK_RELEASING; + } + else + { + self.hook_state |= HOOK_PULLING; + self.hook_state &= ~HOOK_RELEASING; + + if(self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK)) + { + // already fired + if(self.hook) + self.hook_state |= HOOK_WAITING_FOR_RELEASE; + } + else + { + self.hook_state |= HOOK_REMOVING; + self.hook_state &= ~HOOK_WAITING_FOR_RELEASE; + } + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/weapons/g_hookgun.md3"); + precache_model("models/weapons/v_hookgun.md3"); + precache_model("models/weapons/h_hookgun.iqm"); + precache_sound("weapons/hook_impact.wav"); // done by g_hook.qc + precache_sound("weapons/hook_fire.wav"); + precache_sound("weapons/hookbomb_fire.wav"); + HOOK_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_SETUP: + { + self.hook_state &= ~HOOK_WAITING_FOR_RELEASE; + return TRUE; + } + case WR_CHECKAMMO1: + { + if(self.hook) + return self.ammo_fuel > 0; + else + return self.ammo_fuel >= WEP_CVAR_PRI(hook, ammo); + } + case WR_CHECKAMMO2: + { + // infinite ammo for now + return TRUE; // self.ammo_cells >= WEP_CVAR_SEC(hook, ammo); // WEAPONTODO: see above + } + case WR_CONFIG: + { + HOOK_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_RESETPLAYER: + { + self.hook_refire = time; + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + return FALSE; + } + case WR_KILLMESSAGE: + { + return WEAPON_HOOK_MURDER; + } + } + return FALSE; +} +#endif +#ifdef CSQC +float W_Hook(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 2; + pointparticles(particleeffectnum("hookbomb_explode"), org2, '0 0 0', 1); + if(!w_issilent) + sound(self, CH_SHOTS, "weapons/hookbomb_impact.wav", VOL_BASE, ATTN_NORM); + + return TRUE; + } + case WR_INIT: + { + precache_sound("weapons/hookbomb_impact.wav"); + return TRUE; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_machinegun.qc b/qcsrc/common/weapons/w_machinegun.qc new file mode 100644 index 0000000000..9c69c8d824 --- /dev/null +++ b/qcsrc/common/weapons/w_machinegun.qc @@ -0,0 +1,407 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ MACHINEGUN, +/* function */ W_MachineGun, +/* ammotype */ ammo_nails, +/* impulse */ 3, +/* flags */ WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN, +/* rating */ BOT_PICKUP_RATING_MID, +/* color */ '1 1 0', +/* modelname */ "uzi", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairuzi 0.6", +/* wepimg */ "weaponuzi", +/* refname */ "machinegun", +/* wepname */ _("Machine Gun") +); + +#define MACHINEGUN_SETTINGS(w_cvar,w_prop) MACHINEGUN_SETTINGS_LIST(w_cvar, w_prop, MACHINEGUN, machinegun) +#define MACHINEGUN_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, NONE, spread_min) \ + w_cvar(id, sn, NONE, spread_max) \ + w_cvar(id, sn, NONE, spread_add) \ + w_cvar(id, sn, NONE, mode) \ + w_cvar(id, sn, NONE, first) \ + w_cvar(id, sn, NONE, first_damage) \ + w_cvar(id, sn, NONE, first_force) \ + w_cvar(id, sn, NONE, first_refire) \ + w_cvar(id, sn, NONE, first_spread) \ + w_cvar(id, sn, NONE, first_ammo) \ + w_cvar(id, sn, NONE, solidpenetration) \ + w_cvar(id, sn, NONE, sustained_damage) \ + w_cvar(id, sn, NONE, sustained_force) \ + w_cvar(id, sn, NONE, sustained_refire) \ + w_cvar(id, sn, NONE, sustained_spread) \ + w_cvar(id, sn, NONE, sustained_ammo) \ + w_cvar(id, sn, NONE, burst) \ + w_cvar(id, sn, NONE, burst_refire) \ + w_cvar(id, sn, NONE, burst_refire2) \ + w_cvar(id, sn, NONE, burst_animtime) \ + w_cvar(id, sn, NONE, burst_speed) \ + w_cvar(id, sn, NONE, burst_ammo) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +MACHINEGUN_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +#endif +#else +#ifdef SVQC + +void spawnfunc_weapon_machinegun(void) +{ + if(autocvar_sv_q3acompat_machineshotgunswap) + if(self.classname != "droppedweapon") + { + weapon_defaultspawnfunc(WEP_SHOCKWAVE); + return; + } + weapon_defaultspawnfunc(WEP_MACHINEGUN); +} +void spawnfunc_weapon_uzi(void) { spawnfunc_weapon_machinegun(); } + +void W_MachineGun_MuzzleFlash_Think(void) +{ + self.frame = self.frame + 2; + self.scale = self.scale * 0.5; + self.alpha = self.alpha - 0.25; + self.nextthink = time + 0.05; + + if(self.alpha <= 0) + { + self.think = SUB_Remove; + self.nextthink = time; + self.realowner.muzzle_flash = world; + return; + } + +} + +void W_MachineGun_MuzzleFlash(void) +{ + if(self.muzzle_flash == world) + self.muzzle_flash = spawn(); + + // muzzle flash for 1st person view + setmodel(self.muzzle_flash, "models/uziflash.md3"); // precision set below + + self.muzzle_flash.scale = 0.75; + self.muzzle_flash.think = W_MachineGun_MuzzleFlash_Think; + self.muzzle_flash.nextthink = time + 0.02; + self.muzzle_flash.frame = 2; + self.muzzle_flash.alpha = 0.75; + self.muzzle_flash.angles_z = random() * 180; + self.muzzle_flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; + self.muzzle_flash.owner = self.muzzle_flash.realowner = self; +} + +void W_MachineGun_Attack(float deathtype) +{ + W_SetupShot(self, TRUE, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, ((self.misc_bulletcounter == 1) ? WEP_CVAR(machinegun, first_damage) : WEP_CVAR(machinegun, sustained_damage))); + if(!autocvar_g_norecoil) + { + self.punchangle_x = random() - 0.5; + self.punchangle_y = random() - 0.5; + } + + // this attack_finished just enforces a cooldown at the end of a burst + ATTACK_FINISHED(self) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(); + + if(self.misc_bulletcounter == 1) + fireBullet(w_shotorg, w_shotdir, WEP_CVAR(machinegun, first_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, first_damage), WEP_CVAR(machinegun, first_force), deathtype, 0); + else + fireBullet(w_shotorg, w_shotdir, WEP_CVAR(machinegun, sustained_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), deathtype, 0); + + pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + W_MachineGun_MuzzleFlash(); + W_AttachToShotorg(self.muzzle_flash, '5 0 0'); + + // casing code + if(autocvar_g_casings >= 2) + 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, self); + + if(self.misc_bulletcounter == 1) + W_DecreaseAmmo(WEP_CVAR(machinegun, first_ammo)); + else + W_DecreaseAmmo(WEP_CVAR(machinegun, sustained_ammo)); +} + +// weapon frames +void W_MachineGun_Attack_Frame(void) +{ + if(self.weapon != self.switchweapon) // abort immediately if switching + { + w_ready(); + return; + } + if(self.BUTTON_ATCK) + { + if(!WEP_ACTION(self.weapon, WR_CHECKAMMO2)) + if(!(self.items & IT_UNLIMITED_WEAPON_AMMO)) + { + W_SwitchWeapon_Force(self, w_getbestweapon(self)); + w_ready(); + return; + } + self.misc_bulletcounter = self.misc_bulletcounter + 1; + W_MachineGun_Attack(WEP_MACHINEGUN); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame); + } + else + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), w_ready); +} + + +void W_MachineGun_Attack_Auto(void) +{ + float machinegun_spread; + + if(!self.BUTTON_ATCK) + { + w_ready(); + return; + } + + if(!WEP_ACTION(self.weapon, WR_CHECKAMMO1)) + if(!(self.items & IT_UNLIMITED_WEAPON_AMMO)) + { + W_SwitchWeapon_Force(self, w_getbestweapon(self)); + w_ready(); + return; + } + + W_DecreaseAmmo(WEP_CVAR(machinegun, sustained_ammo)); + + W_SetupShot(self, TRUE, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage)); + if(!autocvar_g_norecoil) + { + self.punchangle_x = random() - 0.5; + self.punchangle_y = random() - 0.5; + } + + machinegun_spread = bound(WEP_CVAR(machinegun, spread_min), WEP_CVAR(machinegun, spread_min) + (WEP_CVAR(machinegun, spread_add) * self.misc_bulletcounter), WEP_CVAR(machinegun, spread_max)); + fireBullet(w_shotorg, w_shotdir, machinegun_spread, WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), WEP_MACHINEGUN, 0); + + self.misc_bulletcounter = self.misc_bulletcounter + 1; + + pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + W_MachineGun_MuzzleFlash(); + W_AttachToShotorg(self.muzzle_flash, '5 0 0'); + + if(autocvar_g_casings >= 2) // casing code + 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, self); + + ATTACK_FINISHED(self) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Auto); +} + +void W_MachineGun_Attack_Burst(void) +{ + W_SetupShot(self, TRUE, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage)); + if(!autocvar_g_norecoil) + { + self.punchangle_x = random() - 0.5; + self.punchangle_y = random() - 0.5; + } + + fireBullet(w_shotorg, w_shotdir, WEP_CVAR(machinegun, burst_speed), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), WEP_MACHINEGUN, 0); + + pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + W_MachineGun_MuzzleFlash(); + W_AttachToShotorg(self.muzzle_flash, '5 0 0'); + + if(autocvar_g_casings >= 2) // casing code + 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, self); + + self.misc_bulletcounter = self.misc_bulletcounter + 1; + if(self.misc_bulletcounter == 0) + { + ATTACK_FINISHED(self) = time + WEP_CVAR(machinegun, burst_refire2) * W_WeaponRateFactor(); + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(machinegun, burst_animtime), w_ready); + } + else + { + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(machinegun, burst_refire), W_MachineGun_Attack_Burst); + } + +} + +float W_MachineGun(float req) +{ + float ammo_amount; + switch(req) + { + case WR_AIM: + { + if(vlen(self.origin-self.enemy.origin) < 3000 - bound(0, skill, 10) * 200) + self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE); + else + self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE); + + return TRUE; + } + case WR_THINK: + { + if(WEP_CVAR(machinegun, reload_ammo) && self.clip_load < min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo))) // forced reload + WEP_ACTION(self.weapon, WR_RELOAD); + else if(WEP_CVAR(machinegun, mode) == 1) + { + if(self.BUTTON_ATCK) + if(weapon_prepareattack(0, 0)) + { + self.misc_bulletcounter = 0; + W_MachineGun_Attack_Auto(); + } + + if(self.BUTTON_ATCK2) + if(weapon_prepareattack(1, 0)) + { + if(!WEP_ACTION(self.weapon, WR_CHECKAMMO2)) + if(!(self.items & IT_UNLIMITED_WEAPON_AMMO)) + { + W_SwitchWeapon_Force(self, w_getbestweapon(self)); + w_ready(); + return FALSE; + } + + W_DecreaseAmmo(WEP_CVAR(machinegun, burst_ammo)); + + self.misc_bulletcounter = WEP_CVAR(machinegun, burst) * -1; + W_MachineGun_Attack_Burst(); + } + } + else + { + + if(self.BUTTON_ATCK) + if(weapon_prepareattack(0, 0)) + { + self.misc_bulletcounter = 1; + W_MachineGun_Attack(WEP_MACHINEGUN); // sets attack_finished + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame); + } + + if(self.BUTTON_ATCK2 && WEP_CVAR(machinegun, first)) + if(weapon_prepareattack(1, 0)) + { + self.misc_bulletcounter = 1; + W_MachineGun_Attack(WEP_MACHINEGUN | HITTYPE_SECONDARY); // sets attack_finished + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(machinegun, first_refire), w_ready); + } + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/uziflash.md3"); + precache_model("models/weapons/g_uzi.md3"); + precache_model("models/weapons/v_uzi.md3"); + precache_model("models/weapons/h_uzi.iqm"); + precache_sound("weapons/uzi_fire.wav"); + MACHINEGUN_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_CHECKAMMO1: + { + if(WEP_CVAR(machinegun, mode) == 1) + ammo_amount = self.WEP_AMMO(MACHINEGUN) >= WEP_CVAR(machinegun, sustained_ammo); + else + ammo_amount = self.WEP_AMMO(MACHINEGUN) >= WEP_CVAR(machinegun, first_ammo); + + if(WEP_CVAR(machinegun, reload_ammo)) + { + if(WEP_CVAR(machinegun, mode) == 1) + ammo_amount += self.(weapon_load[WEP_MACHINEGUN]) >= WEP_CVAR(machinegun, sustained_ammo); + else + ammo_amount += self.(weapon_load[WEP_MACHINEGUN]) >= WEP_CVAR(machinegun, first_ammo); + } + return ammo_amount; + } + case WR_CHECKAMMO2: + { + if(WEP_CVAR(machinegun, mode) == 1) + ammo_amount = self.WEP_AMMO(MACHINEGUN) >= WEP_CVAR(machinegun, burst_ammo); + else + ammo_amount = self.WEP_AMMO(MACHINEGUN) >= WEP_CVAR(machinegun, first_ammo); + + if(WEP_CVAR(machinegun, reload_ammo)) + { + if(WEP_CVAR(machinegun, mode) == 1) + ammo_amount += self.(weapon_load[WEP_MACHINEGUN]) >= WEP_CVAR(machinegun, burst_ammo); + else + ammo_amount += self.(weapon_load[WEP_MACHINEGUN]) >= WEP_CVAR(machinegun, first_ammo); + } + return ammo_amount; + } + case WR_CONFIG: + { + MACHINEGUN_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_RELOAD: + { + W_Reload(min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo)), "weapons/reload.wav"); + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + return WEAPON_THINKING_WITH_PORTALS; + } + case WR_KILLMESSAGE: + { + if(w_deathtype & HITTYPE_SECONDARY) + return WEAPON_MACHINEGUN_MURDER_SNIPE; + else + return WEAPON_MACHINEGUN_MURDER_SPRAY; + } + } + return FALSE; +} +#endif +#ifdef CSQC +float W_MachineGun(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 2; + pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1); + if(!w_issilent) + if(w_random < 0.05) + sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTN_NORM); + else if(w_random < 0.1) + sound(self, CH_SHOTS, "weapons/ric2.wav", VOL_BASE, ATTN_NORM); + else if(w_random < 0.2) + sound(self, CH_SHOTS, "weapons/ric3.wav", VOL_BASE, ATTN_NORM); + + return TRUE; + } + case WR_INIT: + { + precache_sound("weapons/ric1.wav"); + precache_sound("weapons/ric2.wav"); + precache_sound("weapons/ric3.wav"); + return TRUE; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_minelayer.qc b/qcsrc/common/weapons/w_minelayer.qc new file mode 100644 index 0000000000..e0e9c6256f --- /dev/null +++ b/qcsrc/common/weapons/w_minelayer.qc @@ -0,0 +1,620 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ MINE_LAYER, +/* function */ W_MineLayer, +/* ammotype */ ammo_rockets, +/* impulse */ 4, +/* flags */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH, +/* rating */ BOT_PICKUP_RATING_HIGH, +/* color */ '0.75 1 0', +/* modelname */ "minelayer", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairminelayer 0.9", +/* wepimg */ "weaponminelayer", +/* refname */ "minelayer", +/* wepname */ _("Mine Layer") +); + +#define MINELAYER_SETTINGS(w_cvar,w_prop) MINELAYER_SETTINGS_LIST(w_cvar, w_prop, MINE_LAYER, minelayer) +#define MINELAYER_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, NONE, ammo) \ + w_cvar(id, sn, NONE, animtime) \ + w_cvar(id, sn, NONE, damage) \ + w_cvar(id, sn, NONE, damageforcescale) \ + w_cvar(id, sn, NONE, detonatedelay) \ + w_cvar(id, sn, NONE, edgedamage) \ + w_cvar(id, sn, NONE, force) \ + w_cvar(id, sn, NONE, health) \ + w_cvar(id, sn, NONE, lifetime) \ + w_cvar(id, sn, NONE, lifetime_countdown) \ + w_cvar(id, sn, NONE, limit) \ + w_cvar(id, sn, NONE, protection) \ + w_cvar(id, sn, NONE, proximityradius) \ + w_cvar(id, sn, NONE, radius) \ + w_cvar(id, sn, NONE, refire) \ + w_cvar(id, sn, NONE, remote_damage) \ + w_cvar(id, sn, NONE, remote_edgedamage) \ + w_cvar(id, sn, NONE, remote_force) \ + w_cvar(id, sn, NONE, remote_radius) \ + w_cvar(id, sn, NONE, speed) \ + w_cvar(id, sn, NONE, time) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +MINELAYER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +void W_MineLayer_Think(void); +.float minelayer_detonate, mine_explodeanyway; +.float mine_time; +.vector mine_orientation; +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_minelayer(void) { weapon_defaultspawnfunc(WEP_MINE_LAYER); } + +void W_MineLayer_Stick(entity to) +{ + spamsound(self, CH_SHOTS, "weapons/mine_stick.wav", VOL_BASE, ATTN_NORM); + + // in order for mines to face properly when sticking to the ground, they must be a server side entity rather than a csqc projectile + + entity newmine; + newmine = spawn(); + newmine.classname = self.classname; + + newmine.bot_dodge = self.bot_dodge; + newmine.bot_dodgerating = self.bot_dodgerating; + + newmine.owner = self.owner; + newmine.realowner = self.realowner; + setsize(newmine, '-4 -4 -4', '4 4 4'); + setorigin(newmine, self.origin); + setmodel(newmine, "models/mine.md3"); + newmine.angles = vectoangles(-trace_plane_normal); // face against the surface + + newmine.mine_orientation = -trace_plane_normal; + + newmine.takedamage = self.takedamage; + newmine.damageforcescale = self.damageforcescale; + newmine.health = self.health; + newmine.event_damage = self.event_damage; + newmine.spawnshieldtime = self.spawnshieldtime; + newmine.damagedbycontents = TRUE; + + newmine.movetype = MOVETYPE_NONE; // lock the mine in place + newmine.projectiledeathtype = self.projectiledeathtype; + + newmine.mine_time = self.mine_time; + + newmine.touch = func_null; + newmine.think = W_MineLayer_Think; + newmine.nextthink = time; + newmine.cnt = self.cnt; + newmine.flags = self.flags; + + remove(self); + self = newmine; + + if(to) + SetMovetypeFollow(self, to); +} + +void W_MineLayer_Explode(void) +{ + if(other.takedamage == DAMAGE_AIM) + if(IS_PLAYER(other)) + if(DIFF_TEAM(self.realowner, other)) + if(other.deadflag == DEAD_NO) + if(IsFlying(other)) + Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); + + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + + RadiusDamage(self, self.realowner, WEP_CVAR(minelayer, damage), WEP_CVAR(minelayer, edgedamage), WEP_CVAR(minelayer, radius), world, world, WEP_CVAR(minelayer, force), self.projectiledeathtype, other); + + if(self.realowner.weapon == WEP_MINE_LAYER) + { + entity oldself; + oldself = self; + self = self.realowner; + if(!WEP_ACTION(WEP_MINE_LAYER, WR_CHECKAMMO1)) + { + self.cnt = WEP_MINE_LAYER; + ATTACK_FINISHED(self) = time; + self.switchweapon = w_getbestweapon(self); + } + self = oldself; + } + self.realowner.minelayer_mines -= 1; + remove(self); +} + +void W_MineLayer_DoRemoteExplode(void) +{ + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + + if(self.movetype == MOVETYPE_NONE || self.movetype == MOVETYPE_FOLLOW) + self.velocity = self.mine_orientation; // particle fx and decals need .velocity + + RadiusDamage(self, self.realowner, WEP_CVAR(minelayer, remote_damage), WEP_CVAR(minelayer, remote_edgedamage), WEP_CVAR(minelayer, remote_radius), world, world, WEP_CVAR(minelayer, remote_force), self.projectiledeathtype | HITTYPE_BOUNCE, world); + + if(self.realowner.weapon == WEP_MINE_LAYER) + { + entity oldself; + oldself = self; + self = self.realowner; + if(!WEP_ACTION(WEP_MINE_LAYER, WR_CHECKAMMO1)) + { + self.cnt = WEP_MINE_LAYER; + ATTACK_FINISHED(self) = time; + self.switchweapon = w_getbestweapon(self); + } + self = oldself; + } + self.realowner.minelayer_mines -= 1; + remove(self); +} + +void W_MineLayer_RemoteExplode(void) +{ + if(self.realowner.deadflag == DEAD_NO) + if((self.spawnshieldtime >= 0) + ? (time >= self.spawnshieldtime) // timer + : (vlen(NearestPointOnBox(self.realowner, self.origin) - self.origin) > WEP_CVAR(minelayer, remote_radius)) // safety device + ) + { + W_MineLayer_DoRemoteExplode(); + } +} + +void W_MineLayer_ProximityExplode(void) +{ + // make sure no friend is in the mine's radius. If there is any, explosion is delayed until he's at a safe distance + if(WEP_CVAR(minelayer, protection) && self.mine_explodeanyway == 0) + { + entity head; + head = findradius(self.origin, WEP_CVAR(minelayer, radius)); + while(head) + { + if(head == self.realowner || SAME_TEAM(head, self.realowner)) + return; + head = head.chain; + } + } + + self.mine_time = 0; + W_MineLayer_Explode(); +} + +float W_MineLayer_Count(entity e) +{ + float minecount = 0; + entity mine; + for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.realowner == e) + minecount += 1; + + return minecount; +} + +void W_MineLayer_Think(void) +{ + entity head; + + self.nextthink = time; + + if(self.movetype == MOVETYPE_FOLLOW) + { + if(LostMovetypeFollow(self)) + { + UnsetMovetypeFollow(self); + self.movetype = MOVETYPE_NONE; + } + } + + // our lifetime has expired, it's time to die - mine_time just allows us to play a sound for this + // TODO: replace this mine_trigger.wav sound with a real countdown + if((time > self.cnt) && (!self.mine_time)) + { + if(WEP_CVAR(minelayer, lifetime_countdown) > 0) + spamsound(self, CH_SHOTS, "weapons/mine_trigger.wav", VOL_BASE, ATTN_NORM); + self.mine_time = time + WEP_CVAR(minelayer, lifetime_countdown); + self.mine_explodeanyway = 1; // make the mine super aggressive -- Samual: Rather, make it not care if a team mate is near. + } + + // a player's mines shall explode if he disconnects or dies + // TODO: Do this on team change too -- Samual: But isn't a player killed when they switch teams? + if(!IS_PLAYER(self.realowner) || self.realowner.deadflag != DEAD_NO || self.realowner.frozen) + { + other = world; + self.projectiledeathtype |= HITTYPE_BOUNCE; + W_MineLayer_Explode(); + return; + } + + // set the mine for detonation when a foe gets close enough + head = findradius(self.origin, WEP_CVAR(minelayer, proximityradius)); + while(head) + { + if(IS_PLAYER(head) && head.deadflag == DEAD_NO && !head.frozen) + if(head != self.realowner && DIFF_TEAM(head, self.realowner)) // don't trigger for team mates + if(!self.mine_time) + { + spamsound(self, CH_SHOTS, "weapons/mine_trigger.wav", VOL_BASE, ATTN_NORM); + self.mine_time = time + WEP_CVAR(minelayer, time); + } + head = head.chain; + } + + // explode if it's time to + if(self.mine_time && time >= self.mine_time) + { + W_MineLayer_ProximityExplode(); + return; + } + + // remote detonation + if(self.realowner.weapon == WEP_MINE_LAYER) + if(self.realowner.deadflag == DEAD_NO) + if(self.minelayer_detonate) + W_MineLayer_RemoteExplode(); +} + +void W_MineLayer_Touch(void) +{ + if(self.movetype == MOVETYPE_NONE || self.movetype == MOVETYPE_FOLLOW) + return; // we're already a stuck mine, why do we get called? TODO does this even happen? + + if(WarpZone_Projectile_Touch()) + { + if(wasfreed(self)) + self.realowner.minelayer_mines -= 1; + return; + } + + if(other && IS_PLAYER(other) && other.deadflag == DEAD_NO) + { + // hit a player + // don't stick + } + else + { + W_MineLayer_Stick(other); + } +} + +void W_MineLayer_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if(self.health <= 0) + return; + + float is_from_enemy = (inflictor.realowner != self.realowner); + + if(!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, (is_from_enemy ? 1 : -1))) + return; // g_projectiles_damage says to halt + + self.health = self.health - damage; + self.angles = vectoangles(self.velocity); + + if(self.health <= 0) + W_PrepareExplosionByDamage(attacker, W_MineLayer_Explode); +} + +void W_MineLayer_Attack(void) +{ + entity mine; + entity flash; + + // scan how many mines we placed, and return if we reached our limit + if(WEP_CVAR(minelayer, limit)) + { + if(self.minelayer_mines >= WEP_CVAR(minelayer, limit)) + { + // the refire delay keeps this message from being spammed + sprint(self, strcat("minelayer: You cannot place more than ^2", ftos(WEP_CVAR(minelayer, limit)), " ^7mines at a time\n") ); + play2(self, "weapons/unavailable.wav"); + return; + } + } + + W_DecreaseAmmo(WEP_CVAR(minelayer, ammo)); + + W_SetupShot_ProjectileSize(self, '-4 -4 -4', '4 4 4', FALSE, 5, "weapons/mine_fire.wav", CH_WEAPON_A, WEP_CVAR(minelayer, damage)); + pointparticles(particleeffectnum("rocketlauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + mine = WarpZone_RefSys_SpawnSameRefSys(self); + mine.owner = mine.realowner = self; + if(WEP_CVAR(minelayer, detonatedelay) >= 0) + mine.spawnshieldtime = time + WEP_CVAR(minelayer, detonatedelay); + else + mine.spawnshieldtime = -1; + mine.classname = "mine"; + mine.bot_dodge = TRUE; + mine.bot_dodgerating = WEP_CVAR(minelayer, damage) * 2; // * 2 because it can detonate inflight which makes it even more dangerous + + mine.takedamage = DAMAGE_YES; + mine.damageforcescale = WEP_CVAR(minelayer, damageforcescale); + mine.health = WEP_CVAR(minelayer, health); + mine.event_damage = W_MineLayer_Damage; + mine.damagedbycontents = TRUE; + + mine.movetype = MOVETYPE_TOSS; + PROJECTILE_MAKETRIGGER(mine); + mine.projectiledeathtype = WEP_MINE_LAYER; + setsize(mine, '-4 -4 -4', '4 4 4'); // give it some size so it can be shot + + setorigin(mine, w_shotorg - v_forward * 4); // move it back so it hits the wall at the right point + W_SetupProjVelocity_Basic(mine, WEP_CVAR(minelayer, speed), 0); + mine.angles = vectoangles(mine.velocity); + + mine.touch = W_MineLayer_Touch; + mine.think = W_MineLayer_Think; + mine.nextthink = time; + mine.cnt = time + (WEP_CVAR(minelayer, lifetime) - WEP_CVAR(minelayer, lifetime_countdown)); + mine.flags = FL_PROJECTILE; + mine.missile_flags = MIF_SPLASH | MIF_ARC | MIF_PROXY; + + CSQCProjectile(mine, TRUE, PROJECTILE_MINE, TRUE); + + // muzzle flash for 1st person view + flash = spawn(); + setmodel(flash, "models/flash.md3"); // precision set below + SUB_SetFade(flash, time, 0.1); + flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; + W_AttachToShotorg(flash, '5 0 0'); + + // common properties + + other = mine; MUTATOR_CALLHOOK(EditProjectile); + + self.minelayer_mines = W_MineLayer_Count(self); +} + +float W_MineLayer_PlacedMines(float detonate) +{ + entity mine; + float minfound = 0; + + for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.realowner == self) + { + if(detonate) + { + if(!mine.minelayer_detonate) + { + mine.minelayer_detonate = TRUE; + minfound = 1; + } + } + else + minfound = 1; + } + return minfound; +} + +float W_MineLayer(float req) +{ + entity mine; + float ammo_amount; + switch(req) + { + case WR_AIM: + { + // aim and decide to fire if appropriate + if(self.minelayer_mines >= WEP_CVAR(minelayer, limit)) + self.BUTTON_ATCK = FALSE; + else + self.BUTTON_ATCK = bot_aim(WEP_CVAR(minelayer, speed), 0, WEP_CVAR(minelayer, lifetime), FALSE); + if(skill >= 2) // skill 0 and 1 bots won't detonate mines! + { + // decide whether to detonate mines + entity targetlist, targ; + float edgedamage, coredamage, edgeradius, recipricoledgeradius, d; + float selfdamage, teamdamage, enemydamage; + edgedamage = WEP_CVAR(minelayer, edgedamage); + coredamage = WEP_CVAR(minelayer, damage); + edgeradius = WEP_CVAR(minelayer, radius); + recipricoledgeradius = 1 / edgeradius; + selfdamage = 0; + teamdamage = 0; + enemydamage = 0; + targetlist = findchainfloat(bot_attack, TRUE); + mine = find(world, classname, "mine"); + while(mine) + { + if(mine.realowner != self) + { + mine = find(mine, classname, "mine"); + continue; + } + targ = targetlist; + while(targ) + { + d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - mine.origin); + d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000); + // count potential damage according to type of target + if(targ == self) + selfdamage = selfdamage + d; + else if(targ.team == self.team && teamplay) + teamdamage = teamdamage + d; + else if(bot_shouldattack(targ)) + enemydamage = enemydamage + d; + targ = targ.chain; + } + mine = find(mine, classname, "mine"); + } + float desirabledamage; + desirabledamage = enemydamage; + if(time > self.invincible_finished && time > self.spawnshieldtime) + desirabledamage = desirabledamage - selfdamage * autocvar_g_balance_selfdamagepercent; + if(teamplay && self.team) + desirabledamage = desirabledamage - teamdamage; + + mine = find(world, classname, "mine"); + while(mine) + { + if(mine.realowner != self) + { + mine = find(mine, classname, "mine"); + continue; + } + makevectors(mine.v_angle); + targ = targetlist; + if(skill > 9) // normal players only do this for the target they are tracking + { + targ = targetlist; + while(targ) + { + if( + (v_forward * normalize(mine.origin - targ.origin)< 0.1) + && desirabledamage > 0.1*coredamage + )self.BUTTON_ATCK2 = TRUE; + targ = targ.chain; + } + }else{ + float distance; distance= bound(300,vlen(self.origin-self.enemy.origin),30000); + //As the distance gets larger, a correct detonation gets near imposible + //Bots are assumed to use the mine spawnfunc_light to see if the mine gets near a player + if(v_forward * normalize(mine.origin - self.enemy.origin)< 0.1) + if(IS_PLAYER(self.enemy)) + if(desirabledamage >= 0.1*coredamage) + if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1)) + self.BUTTON_ATCK2 = TRUE; + // dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n"); + } + + mine = find(mine, classname, "mine"); + } + // if we would be doing at X percent of the core damage, detonate it + // but don't fire a new shot at the same time! + if(desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events + self.BUTTON_ATCK2 = TRUE; + if((skill > 6.5) && (selfdamage > self.health)) + self.BUTTON_ATCK2 = FALSE; + //if(self.BUTTON_ATCK2 == TRUE) + // dprint(ftos(desirabledamage),"\n"); + if(self.BUTTON_ATCK2 == TRUE) self.BUTTON_ATCK = FALSE; + } + + return TRUE; + } + case WR_THINK: + { + if(autocvar_g_balance_minelayer_reload_ammo && self.clip_load < WEP_CVAR(minelayer, ammo)) // forced reload + { + // not if we're holding the minelayer without enough ammo, but can detonate existing mines + if(!(W_MineLayer_PlacedMines(FALSE) && self.WEP_AMMO(MINE_LAYER) < WEP_CVAR(minelayer, ammo))) + WEP_ACTION(self.weapon, WR_RELOAD); + } + else if(self.BUTTON_ATCK) + { + if(weapon_prepareattack(0, WEP_CVAR(minelayer, refire))) + { + W_MineLayer_Attack(); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(minelayer, animtime), w_ready); + } + } + + if(self.BUTTON_ATCK2) + { + if(W_MineLayer_PlacedMines(TRUE)) + sound(self, CH_WEAPON_B, "weapons/mine_det.wav", VOL_BASE, ATTN_NORM); + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/flash.md3"); + precache_model("models/mine.md3"); + precache_model("models/weapons/g_minelayer.md3"); + precache_model("models/weapons/v_minelayer.md3"); + precache_model("models/weapons/h_minelayer.iqm"); + precache_sound("weapons/mine_det.wav"); + precache_sound("weapons/mine_fire.wav"); + precache_sound("weapons/mine_stick.wav"); + precache_sound("weapons/mine_trigger.wav"); + MINELAYER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_CHECKAMMO1: + { + // don't switch while placing a mine + if(ATTACK_FINISHED(self) <= time || self.weapon != WEP_MINE_LAYER) + { + ammo_amount = self.WEP_AMMO(MINE_LAYER) >= WEP_CVAR(minelayer, ammo); + ammo_amount += self.(weapon_load[WEP_MINE_LAYER]) >= WEP_CVAR(minelayer, ammo); + return ammo_amount; + } + return TRUE; + } + case WR_CHECKAMMO2: + { + if(W_MineLayer_PlacedMines(FALSE)) + return TRUE; + else + return FALSE; + } + case WR_CONFIG: + { + MINELAYER_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_RESETPLAYER: + { + self.minelayer_mines = 0; + return TRUE; + } + case WR_RELOAD: + { + W_Reload(WEP_CVAR(minelayer, ammo), "weapons/reload.wav"); + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + return WEAPON_MINELAYER_SUICIDE; + } + case WR_KILLMESSAGE: + { + return WEAPON_MINELAYER_MURDER; + } + } + return FALSE; +} +#endif +#ifdef CSQC +float W_MineLayer(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 12; + pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1); + if(!w_issilent) + sound(self, CH_SHOTS, "weapons/mine_exp.wav", VOL_BASE, ATTN_NORM); + + return TRUE; + } + case WR_INIT: + { + precache_sound("weapons/mine_exp.wav"); + return TRUE; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_mortar.qc b/qcsrc/common/weapons/w_mortar.qc new file mode 100644 index 0000000000..de40fcb393 --- /dev/null +++ b/qcsrc/common/weapons/w_mortar.qc @@ -0,0 +1,490 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ MORTAR, +/* function */ W_Mortar, +/* ammotype */ ammo_rockets, +/* impulse */ 4, +/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH, +/* rating */ BOT_PICKUP_RATING_MID, +/* color */ '1 0 0', +/* modelname */ "gl", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairgrenadelauncher 0.7", +/* wepimg */ "weapongrenadelauncher", +/* refname */ "mortar", +/* wepname */ _("Mortar") +); + +#define MORTAR_SETTINGS(w_cvar,w_prop) MORTAR_SETTINGS_LIST(w_cvar, w_prop, MORTAR, mortar) +#define MORTAR_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, BOTH, ammo) \ + w_cvar(id, sn, BOTH, animtime) \ + w_cvar(id, sn, NONE, bouncefactor) \ + w_cvar(id, sn, NONE, bouncestop) \ + w_cvar(id, sn, BOTH, damage) \ + w_cvar(id, sn, BOTH, damageforcescale) \ + w_cvar(id, sn, BOTH, edgedamage) \ + w_cvar(id, sn, BOTH, force) \ + w_cvar(id, sn, BOTH, health) \ + w_cvar(id, sn, BOTH, lifetime) \ + w_cvar(id, sn, SEC, lifetime_bounce) \ + w_cvar(id, sn, BOTH, lifetime_stick) \ + w_cvar(id, sn, BOTH, radius) \ + w_cvar(id, sn, BOTH, refire) \ + w_cvar(id, sn, SEC, remote_detonateprimary) \ + w_cvar(id, sn, PRI, remote_minbouncecnt) \ + w_cvar(id, sn, BOTH, speed) \ + w_cvar(id, sn, BOTH, speed_up) \ + w_cvar(id, sn, BOTH, speed_z) \ + w_cvar(id, sn, BOTH, spread) \ + w_cvar(id, sn, BOTH, type) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +MORTAR_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +.float gl_detonate_later; +.float gl_bouncecnt; +#endif +#else +#ifdef SVQC + +void spawnfunc_weapon_mortar(void) { weapon_defaultspawnfunc(WEP_MORTAR); } +void spawnfunc_weapon_grenadelauncher(void) { spawnfunc_weapon_mortar(); } + +void W_Mortar_Grenade_Explode(void) +{ + if(other.takedamage == DAMAGE_AIM) + if(IS_PLAYER(other)) + if(DIFF_TEAM(self.realowner, other)) + if(other.deadflag == DEAD_NO) + if(IsFlying(other)) + Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); + + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + + if(self.movetype == MOVETYPE_NONE) + self.velocity = self.oldvelocity; + + RadiusDamage(self, self.realowner, WEP_CVAR_PRI(mortar, damage), WEP_CVAR_PRI(mortar, edgedamage), WEP_CVAR_PRI(mortar, radius), world, world, WEP_CVAR_PRI(mortar, force), self.projectiledeathtype, other); + + remove(self); +} + +void W_Mortar_Grenade_Explode2(void) +{ + if(other.takedamage == DAMAGE_AIM) + if(IS_PLAYER(other)) + if(DIFF_TEAM(self.realowner, other)) + if(other.deadflag == DEAD_NO) + if(IsFlying(other)) + Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); + + self.event_damage = func_null; + self.takedamage = DAMAGE_NO; + + if(self.movetype == MOVETYPE_NONE) + self.velocity = self.oldvelocity; + + RadiusDamage(self, self.realowner, WEP_CVAR_SEC(mortar, damage), WEP_CVAR_SEC(mortar, edgedamage), WEP_CVAR_SEC(mortar, radius), world, world, WEP_CVAR_SEC(mortar, force), self.projectiledeathtype, other); + + remove(self); +} + + +void W_Mortar_Grenade_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if(self.health <= 0) + return; + + if(!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions + return; // g_projectiles_damage says to halt + + self.health = self.health - damage; + + if(self.health <= 0) + W_PrepareExplosionByDamage(attacker, self.use); +} + +void W_Mortar_Grenade_Think1(void) +{ + self.nextthink = time; + if(time > self.cnt) + { + other = world; + self.projectiledeathtype |= HITTYPE_BOUNCE; + W_Mortar_Grenade_Explode(); + return; + } + if(self.gl_detonate_later && self.gl_bouncecnt >= WEP_CVAR_PRI(mortar, remote_minbouncecnt)) + W_Mortar_Grenade_Explode(); +} + +void W_Mortar_Grenade_Touch1(void) +{ + PROJECTILE_TOUCH; + if(other.takedamage == DAMAGE_AIM || WEP_CVAR_PRI(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile + { + self.use(); + } + else if(WEP_CVAR_PRI(mortar, type) == 1) // bounce + { + float r; + r = random() * 6; + if(r < 1) + spamsound(self, CH_SHOTS, "weapons/grenade_bounce1.wav", VOL_BASE, ATTN_NORM); + else if(r < 2) + spamsound(self, CH_SHOTS, "weapons/grenade_bounce2.wav", VOL_BASE, ATTN_NORM); + else if(r < 3) + spamsound(self, CH_SHOTS, "weapons/grenade_bounce3.wav", VOL_BASE, ATTN_NORM); + else if(r < 4) + spamsound(self, CH_SHOTS, "weapons/grenade_bounce4.wav", VOL_BASE, ATTN_NORM); + else if(r < 5) + spamsound(self, CH_SHOTS, "weapons/grenade_bounce5.wav", VOL_BASE, ATTN_NORM); + else + spamsound(self, CH_SHOTS, "weapons/grenade_bounce6.wav", VOL_BASE, ATTN_NORM); + pointparticles(particleeffectnum("hagar_bounce"), self.origin, self.velocity, 1); + self.projectiledeathtype |= HITTYPE_BOUNCE; + self.gl_bouncecnt += 1; + } + else if(WEP_CVAR_PRI(mortar, type) == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick + { + spamsound(self, CH_SHOTS, "weapons/grenade_stick.wav", VOL_BASE, ATTN_NORM); + + // let it stick whereever it is + self.oldvelocity = self.velocity; + self.velocity = '0 0 0'; + self.movetype = MOVETYPE_NONE; // also disables gravity + self.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO + UpdateCSQCProjectile(self); + + // do not respond to any more touches + self.solid = SOLID_NOT; + + self.nextthink = min(self.nextthink, time + WEP_CVAR_PRI(mortar, lifetime_stick)); + } +} + +void W_Mortar_Grenade_Touch2(void) +{ + PROJECTILE_TOUCH; + if(other.takedamage == DAMAGE_AIM || WEP_CVAR_SEC(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile + { + self.use(); + } + else if(WEP_CVAR_SEC(mortar, type) == 1) // bounce + { + float r; + r = random() * 6; + if(r < 1) + spamsound(self, CH_SHOTS, "weapons/grenade_bounce1.wav", VOL_BASE, ATTN_NORM); + else if(r < 2) + spamsound(self, CH_SHOTS, "weapons/grenade_bounce2.wav", VOL_BASE, ATTN_NORM); + else if(r < 3) + spamsound(self, CH_SHOTS, "weapons/grenade_bounce3.wav", VOL_BASE, ATTN_NORM); + else if(r < 4) + spamsound(self, CH_SHOTS, "weapons/grenade_bounce4.wav", VOL_BASE, ATTN_NORM); + else if(r < 5) + spamsound(self, CH_SHOTS, "weapons/grenade_bounce5.wav", VOL_BASE, ATTN_NORM); + else + spamsound(self, CH_SHOTS, "weapons/grenade_bounce6.wav", VOL_BASE, ATTN_NORM); + pointparticles(particleeffectnum("hagar_bounce"), self.origin, self.velocity, 1); + self.projectiledeathtype |= HITTYPE_BOUNCE; + self.gl_bouncecnt += 1; + + if(WEP_CVAR_SEC(mortar, lifetime_bounce) && self.gl_bouncecnt == 1) + self.nextthink = time + WEP_CVAR_SEC(mortar, lifetime_bounce); + + } + else if(WEP_CVAR_SEC(mortar, type) == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick + { + spamsound(self, CH_SHOTS, "weapons/grenade_stick.wav", VOL_BASE, ATTN_NORM); + + // let it stick whereever it is + self.oldvelocity = self.velocity; + self.velocity = '0 0 0'; + self.movetype = MOVETYPE_NONE; // also disables gravity + self.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO + UpdateCSQCProjectile(self); + + // do not respond to any more touches + self.solid = SOLID_NOT; + + self.nextthink = min(self.nextthink, time + WEP_CVAR_SEC(mortar, lifetime_stick)); + } +} + +void W_Mortar_Attack(void) +{ + entity gren; + + W_DecreaseAmmo(WEP_CVAR_PRI(mortar, ammo)); + + W_SetupShot_ProjectileSize(self, '-3 -3 -3', '3 3 3', FALSE, 4, "weapons/grenade_fire.wav", CH_WEAPON_A, WEP_CVAR_PRI(mortar, damage)); + w_shotdir = v_forward; // no TrueAim for grenades please + + pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + gren = spawn(); + gren.owner = gren.realowner = self; + gren.classname = "grenade"; + gren.bot_dodge = TRUE; + gren.bot_dodgerating = WEP_CVAR_PRI(mortar, damage); + gren.movetype = MOVETYPE_BOUNCE; + gren.bouncefactor = WEP_CVAR(mortar, bouncefactor); + gren.bouncestop = WEP_CVAR(mortar, bouncestop); + PROJECTILE_MAKETRIGGER(gren); + gren.projectiledeathtype = WEP_MORTAR; + setorigin(gren, w_shotorg); + setsize(gren, '-3 -3 -3', '3 3 3'); + + gren.cnt = time + WEP_CVAR_PRI(mortar, lifetime); + gren.nextthink = time; + gren.think = W_Mortar_Grenade_Think1; + gren.use = W_Mortar_Grenade_Explode; + gren.touch = W_Mortar_Grenade_Touch1; + + gren.takedamage = DAMAGE_YES; + gren.health = WEP_CVAR_PRI(mortar, health); + gren.damageforcescale = WEP_CVAR_PRI(mortar, damageforcescale); + gren.event_damage = W_Mortar_Grenade_Damage; + gren.damagedbycontents = TRUE; + gren.missile_flags = MIF_SPLASH | MIF_ARC; + W_SetupProjVelocity_UP_PRI(gren, mortar); + + gren.angles = vectoangles(gren.velocity); + gren.flags = FL_PROJECTILE; + + if(WEP_CVAR_PRI(mortar, type) == 0 || WEP_CVAR_PRI(mortar, type) == 2) + CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE, TRUE); + else + CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE_BOUNCING, TRUE); + + other = gren; MUTATOR_CALLHOOK(EditProjectile); +} + +void W_Mortar_Attack2(void) +{ + entity gren; + + W_DecreaseAmmo(WEP_CVAR_SEC(mortar, ammo)); + + W_SetupShot_ProjectileSize(self, '-3 -3 -3', '3 3 3', FALSE, 4, "weapons/grenade_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(mortar, damage)); + w_shotdir = v_forward; // no TrueAim for grenades please + + pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + gren = spawn(); + gren.owner = gren.realowner = self; + gren.classname = "grenade"; + gren.bot_dodge = TRUE; + gren.bot_dodgerating = WEP_CVAR_SEC(mortar, damage); + gren.movetype = MOVETYPE_BOUNCE; + gren.bouncefactor = WEP_CVAR(mortar, bouncefactor); + gren.bouncestop = WEP_CVAR(mortar, bouncestop); + PROJECTILE_MAKETRIGGER(gren); + gren.projectiledeathtype = WEP_MORTAR | HITTYPE_SECONDARY; + setorigin(gren, w_shotorg); + setsize(gren, '-3 -3 -3', '3 3 3'); + + gren.nextthink = time + WEP_CVAR_SEC(mortar, lifetime); + gren.think = adaptor_think2use_hittype_splash; + gren.use = W_Mortar_Grenade_Explode2; + gren.touch = W_Mortar_Grenade_Touch2; + + gren.takedamage = DAMAGE_YES; + gren.health = WEP_CVAR_SEC(mortar, health); + gren.damageforcescale = WEP_CVAR_SEC(mortar, damageforcescale); + gren.event_damage = W_Mortar_Grenade_Damage; + gren.damagedbycontents = TRUE; + gren.missile_flags = MIF_SPLASH | MIF_ARC; + W_SetupProjVelocity_UP_SEC(gren, mortar); + + gren.angles = vectoangles(gren.velocity); + gren.flags = FL_PROJECTILE; + + if(WEP_CVAR_SEC(mortar, type) == 0 || WEP_CVAR_SEC(mortar, type) == 2) + CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE, TRUE); + else + CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE_BOUNCING, TRUE); + + other = gren; MUTATOR_CALLHOOK(EditProjectile); +} + +.float bot_secondary_grenademooth; +float W_Mortar(float req) +{ + entity nade; + float nadefound; + float ammo_amount; + switch(req) + { + case WR_AIM: + { + self.BUTTON_ATCK = FALSE; + self.BUTTON_ATCK2 = FALSE; + if(self.bot_secondary_grenademooth == 0) // WEAPONTODO: merge this into using WEP_CVAR_BOTH + { + if(bot_aim(WEP_CVAR_PRI(mortar, speed), WEP_CVAR_PRI(mortar, speed_up), WEP_CVAR_PRI(mortar, lifetime), TRUE)) + { + self.BUTTON_ATCK = TRUE; + if(random() < 0.01) self.bot_secondary_grenademooth = 1; + } + } + else + { + if(bot_aim(WEP_CVAR_SEC(mortar, speed), WEP_CVAR_SEC(mortar, speed_up), WEP_CVAR_SEC(mortar, lifetime), TRUE)) + { + self.BUTTON_ATCK2 = TRUE; + if(random() < 0.02) self.bot_secondary_grenademooth = 0; + } + } + + return TRUE; + } + /*case WR_CALCINFO: + { + wepinfo_pri_refire = max3(sys_frametime, WEP_CVAR_PRI(mortar, refire), WEP_CVAR_PRI(mortar, animtime)); + wepinfo_pri_dps = (WEP_CVAR_PRI(mortar, damage) * (1 / wepinfo_pri_refire)); + wepinfo_pri_speed = (1 / max(1, (10000 / max(1, WEP_CVAR_PRI(mortar, speed))))); + + // for the range calculation, closer to 1 is better + wepinfo_pri_range_max = 2000 * wepinfo_pri_speed; + wepinfo_pri_range = wepinfo_pri_speed * WEP_CVAR_PRI(mortar, + + wepinfo_sec_refire = max3(sys_frametime, WEP_CVAR_SEC(mortar, refire), WEP_CVAR_SEC(mortar, animtime)); + wepinfo_sec_dps = (WEP_CVAR_SEC(mortar, damage) * (1 / wepinfo_sec_refire)); + + wepinfo_sec_dps = (WEP_CVAR_SEC(mortar, damage) * (1 / max3(sys_frametime, WEP_CVAR_SEC(mortar, refire), WEP_CVAR_SEC(mortar, animtime)))); + wepinfo_ter_dps = 0; + */ + case WR_THINK: + { + if(autocvar_g_balance_mortar_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo))) // forced reload + WEP_ACTION(self.weapon, WR_RELOAD); + else if(self.BUTTON_ATCK) + { + if(weapon_prepareattack(0, WEP_CVAR_PRI(mortar, refire))) + { + W_Mortar_Attack(); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(mortar, animtime), w_ready); + } + } + else if(self.BUTTON_ATCK2) + { + if(WEP_CVAR_SEC(mortar, remote_detonateprimary)) + { + nadefound = 0; + for(nade = world; (nade = find(nade, classname, "grenade")); ) if(nade.realowner == self) + { + if(!nade.gl_detonate_later) + { + nade.gl_detonate_later = TRUE; + nadefound = 1; + } + } + if(nadefound) + sound(self, CH_WEAPON_B, "weapons/rocket_det.wav", VOL_BASE, ATTN_NORM); + } + else if(weapon_prepareattack(1, WEP_CVAR_SEC(mortar, refire))) + { + W_Mortar_Attack2(); + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(mortar, animtime), w_ready); + } + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/weapons/g_gl.md3"); + precache_model("models/weapons/v_gl.md3"); + precache_model("models/weapons/h_gl.iqm"); + precache_sound("weapons/grenade_bounce1.wav"); + precache_sound("weapons/grenade_bounce2.wav"); + precache_sound("weapons/grenade_bounce3.wav"); + precache_sound("weapons/grenade_bounce4.wav"); + precache_sound("weapons/grenade_bounce5.wav"); + precache_sound("weapons/grenade_bounce6.wav"); + precache_sound("weapons/grenade_stick.wav"); + precache_sound("weapons/grenade_fire.wav"); + MORTAR_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_CHECKAMMO1: + { + ammo_amount = self.WEP_AMMO(MORTAR) >= WEP_CVAR_PRI(mortar, ammo); + ammo_amount += self.(weapon_load[WEP_MORTAR]) >= WEP_CVAR_PRI(mortar, ammo); + return ammo_amount; + } + case WR_CHECKAMMO2: + { + ammo_amount = self.WEP_AMMO(MORTAR) >= WEP_CVAR_SEC(mortar, ammo); + ammo_amount += self.(weapon_load[WEP_MORTAR]) >= WEP_CVAR_SEC(mortar, ammo); + return ammo_amount; + } + case WR_CONFIG: + { + MORTAR_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_RELOAD: + { + W_Reload(min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo)), "weapons/reload.wav"); // WEAPONTODO + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + if(w_deathtype & HITTYPE_SECONDARY) + return WEAPON_MORTAR_SUICIDE_BOUNCE; + else + return WEAPON_MORTAR_SUICIDE_EXPLODE; + } + case WR_KILLMESSAGE: + { + if(w_deathtype & HITTYPE_SECONDARY) + return WEAPON_MORTAR_MURDER_BOUNCE; + else + return WEAPON_MORTAR_MURDER_EXPLODE; + } + } + return FALSE; +} +#endif +#ifdef CSQC +float W_Mortar(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 12; + pointparticles(particleeffectnum("grenade_explode"), org2, '0 0 0', 1); + if(!w_issilent) + sound(self, CH_SHOTS, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM); + + return TRUE; + } + case WR_INIT: + { + precache_sound("weapons/grenade_impact.wav"); + return TRUE; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_porto.qc b/qcsrc/common/weapons/w_porto.qc new file mode 100644 index 0000000000..49fb47e43e --- /dev/null +++ b/qcsrc/common/weapons/w_porto.qc @@ -0,0 +1,422 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ PORTO, +/* function */ W_Porto, +/* ammotype */ ammo_none, +/* impulse */ 0, +/* flags */ WEP_TYPE_OTHER | WEP_FLAG_SUPERWEAPON, +/* rating */ 0, +/* color */ '0.5 0.5 0.5', +/* modelname */ "porto", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairporto 0.6", +/* wepimg */ "weaponporto", +/* refname */ "porto", +/* wepname */ _("Port-O-Launch") +); + +#define PORTO_SETTINGS(w_cvar,w_prop) PORTO_SETTINGS_LIST(w_cvar, w_prop, PORTO, porto) +#define PORTO_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, BOTH, animtime) \ + w_cvar(id, sn, BOTH, lifetime) \ + w_cvar(id, sn, BOTH, refire) \ + w_cvar(id, sn, BOTH, speed) \ + w_cvar(id, sn, NONE, secondary) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +PORTO_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +.entity porto_current; +.vector porto_v_angle; // holds "held" view angles +.float porto_v_angle_held; +.vector right_vector; +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_porto(void) { weapon_defaultspawnfunc(WEP_PORTO); } + +void W_Porto_Success(void) +{ + if(self.realowner == world) + { + objerror("Cannot succeed successfully: no owner\n"); + return; + } + + self.realowner.porto_current = world; + remove(self); +} + +string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo); +void W_Porto_Fail(float failhard) +{ + if(self.realowner == world) + { + objerror("Cannot fail successfully: no owner\n"); + return; + } + + // no portals here! + if(self.cnt < 0) + { + Portal_ClearWithID(self.realowner, self.portal_id); + } + + self.realowner.porto_current = world; + + if(self.cnt < 0 && !failhard && self.realowner.playerid == self.playerid && self.realowner.deadflag == DEAD_NO && !(self.realowner.weapons & WEPSET_PORTO)) + { + setsize(self, '-16 -16 0', '16 16 32'); + setorigin(self, self.origin + trace_plane_normal); + if(move_out_of_solid(self)) + { + self.flags = FL_ITEM; + self.velocity = trigger_push_calculatevelocity(self.origin, self.realowner, 128); + tracetoss(self, self); + if(vlen(trace_endpos - self.realowner.origin) < 128) + { + W_ThrowNewWeapon(self.realowner, WEP_PORTO, 0, self.origin, self.velocity); + centerprint(self.realowner, "^1Portal deployment failed.\n\n^2Catch it to try again!"); + } + } + } + remove(self); +} + +void W_Porto_Remove(entity p) +{ + if(p.porto_current.realowner == p && p.porto_current.classname == "porto") + { + entity oldself; + oldself = self; + self = p.porto_current; + W_Porto_Fail(1); + self = oldself; + } +} + +void W_Porto_Think(void) +{ + trace_plane_normal = '0 0 0'; + if(self.realowner.playerid != self.playerid) + remove(self); + else + W_Porto_Fail(0); +} + +void W_Porto_Touch(void) +{ + vector norm; + + // do not use PROJECTILE_TOUCH here + // FIXME but DO handle warpzones! + + if(other.classname == "portal") + return; // handled by the portal + + norm = trace_plane_normal; + if(trace_ent.iscreature) + { + traceline(trace_ent.origin, trace_ent.origin + '0 0 2' * PL_MIN_z, MOVE_WORLDONLY, self); + if(trace_fraction >= 1) + return; + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP) + return; + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) + return; + } + + if(self.realowner.playerid != self.playerid) + { + sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM); + remove(self); + } + else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP) + { + spamsound(self, CH_SHOTS, "porto/bounce.wav", VOL_BASE, ATTEN_NORM); + // just reflect + self.right_vector = self.right_vector - 2 * trace_plane_normal * (self.right_vector * trace_plane_normal); + self.angles = vectoangles(self.velocity - 2 * trace_plane_normal * (self.velocity * trace_plane_normal)); + } + else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) + { + sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM); + W_Porto_Fail(0); + if(self.cnt < 0) + Portal_ClearAll_PortalsOnly(self.realowner); + } + else if(self.cnt == 0) + { + // in-portal only + if(Portal_SpawnInPortalAtTrace(self.realowner, self.right_vector, self.portal_id)) + { + sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM); + trace_plane_normal = norm; + centerprint(self.realowner, "^1In^7-portal created."); + W_Porto_Success(); + } + else + { + sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM); + trace_plane_normal = norm; + W_Porto_Fail(0); + } + } + else if(self.cnt == 1) + { + // out-portal only + if(Portal_SpawnOutPortalAtTrace(self.realowner, self.right_vector, self.portal_id)) + { + sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM); + trace_plane_normal = norm; + centerprint(self.realowner, "^4Out^7-portal created."); + W_Porto_Success(); + } + else + { + sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM); + trace_plane_normal = norm; + W_Porto_Fail(0); + } + } + else if(self.effects & EF_RED) + { + self.effects += EF_BLUE - EF_RED; + if(Portal_SpawnInPortalAtTrace(self.realowner, self.right_vector, self.portal_id)) + { + sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM); + trace_plane_normal = norm; + centerprint(self.realowner, "^1In^7-portal created."); + self.right_vector = self.right_vector - 2 * trace_plane_normal * (self.right_vector * norm); + self.angles = vectoangles(self.velocity - 2 * trace_plane_normal * (self.velocity * norm)); + CSQCProjectile(self, TRUE, PROJECTILE_PORTO_BLUE, TRUE); // change type + } + else + { + sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM); + trace_plane_normal = norm; + Portal_ClearAll_PortalsOnly(self.realowner); + W_Porto_Fail(0); + } + } + else + { + if(self.realowner.portal_in.portal_id == self.portal_id) + { + if(Portal_SpawnOutPortalAtTrace(self.realowner, self.right_vector, self.portal_id)) + { + sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM); + trace_plane_normal = norm; + centerprint(self.realowner, "^4Out^7-portal created."); + W_Porto_Success(); + } + else + { + sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM); + Portal_ClearAll_PortalsOnly(self.realowner); + W_Porto_Fail(0); + } + } + else + { + sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM); + Portal_ClearAll_PortalsOnly(self.realowner); + W_Porto_Fail(0); + } + } +} + +void W_Porto_Attack(float type) +{ + entity gren; + + W_SetupShot(self, FALSE, 4, "porto/fire.wav", CH_WEAPON_A, 0); + // always shoot from the eye + w_shotdir = v_forward; + w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward; + + //pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + gren = spawn(); + gren.cnt = type; + gren.owner = gren.realowner = self; + gren.playerid = self.playerid; + gren.classname = "porto"; + gren.bot_dodge = TRUE; + gren.bot_dodgerating = 200; + gren.movetype = MOVETYPE_BOUNCEMISSILE; + PROJECTILE_MAKETRIGGER(gren); + gren.effects = EF_RED; + gren.scale = 4; + setorigin(gren, w_shotorg); + setsize(gren, '0 0 0', '0 0 0'); + + gren.nextthink = time + WEP_CVAR_BOTH(porto, (type <= 0), lifetime); + gren.think = W_Porto_Think; + gren.touch = W_Porto_Touch; + + if(self.items & IT_STRENGTH) + W_SetupProjVelocity_Basic(gren, WEP_CVAR_BOTH(porto, (type <= 0), speed) * autocvar_g_balance_powerup_strength_force, 0); + else + W_SetupProjVelocity_Basic(gren, WEP_CVAR_BOTH(porto, (type <= 0), speed), 0); + + gren.angles = vectoangles(gren.velocity); + gren.flags = FL_PROJECTILE; + + gren.portal_id = time; + self.porto_current = gren; + gren.playerid = self.playerid; + fixedmakevectors(fixedvectoangles(gren.velocity)); + gren.right_vector = v_right; + + gren.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP; + + if(type > 0) + CSQCProjectile(gren, TRUE, PROJECTILE_PORTO_BLUE, TRUE); + else + CSQCProjectile(gren, TRUE, PROJECTILE_PORTO_RED, TRUE); + + other = gren; MUTATOR_CALLHOOK(EditProjectile); +} + +float w_nexball_weapon(float req); // WEAPONTODO +float W_Porto(float req) +{ + //vector v_angle_save; + + if(g_nexball) { return w_nexball_weapon(req); } + + switch(req) + { + case WR_AIM: + { + self.BUTTON_ATCK = FALSE; + self.BUTTON_ATCK2 = FALSE; + if(!WEP_CVAR(porto, secondary)) + if(bot_aim(WEP_CVAR_PRI(porto, speed), 0, WEP_CVAR_PRI(porto, lifetime), FALSE)) + self.BUTTON_ATCK = TRUE; + + return TRUE; + } + case WR_CONFIG: + { + PORTO_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_THINK: + { + if(WEP_CVAR(porto, secondary)) + { + if(self.BUTTON_ATCK) + if(!self.porto_current) + if(!self.porto_forbidden) + if(weapon_prepareattack(0, WEP_CVAR_PRI(porto, refire))) + { + W_Porto_Attack(0); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready); + } + + if(self.BUTTON_ATCK2) + if(!self.porto_current) + if(!self.porto_forbidden) + if(weapon_prepareattack(1, WEP_CVAR_SEC(porto, refire))) + { + W_Porto_Attack(1); + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(porto, animtime), w_ready); + } + } + else + { + if(self.porto_v_angle_held) + { + if(!self.BUTTON_ATCK2) + { + self.porto_v_angle_held = 0; + + ClientData_Touch(self); + } + } + else + { + if(self.BUTTON_ATCK2) + { + self.porto_v_angle = self.v_angle; + self.porto_v_angle_held = 1; + + ClientData_Touch(self); + } + } + if(self.porto_v_angle_held) + makevectors(self.porto_v_angle); // override the previously set angles + + if(self.BUTTON_ATCK) + if(!self.porto_current) + if(!self.porto_forbidden) + if(weapon_prepareattack(0, WEP_CVAR_PRI(porto, refire))) + { + W_Porto_Attack(-1); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready); + } + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/weapons/g_porto.md3"); + precache_model("models/weapons/v_porto.md3"); + precache_model("models/weapons/h_porto.iqm"); + precache_model("models/portal.md3"); + precache_sound("porto/bounce.wav"); + precache_sound("porto/create.wav"); + precache_sound("porto/expire.wav"); + precache_sound("porto/explode.wav"); + precache_sound("porto/fire.wav"); + precache_sound("porto/unsupported.wav"); + PORTO_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_SETUP: + { + self.ammo_field = ammo_none; + return TRUE; + } + case WR_RESETPLAYER: + { + self.porto_current = world; + return TRUE; + } + } + return FALSE; +} +#endif +#ifdef CSQC +float W_Porto(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + print("Since when does Porto send DamageInfo?\n"); + return TRUE; + } + case WR_INIT: + { + // nothing to do + return TRUE; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_rifle.qc b/qcsrc/common/weapons/w_rifle.qc new file mode 100644 index 0000000000..03c396ceba --- /dev/null +++ b/qcsrc/common/weapons/w_rifle.qc @@ -0,0 +1,316 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ RIFLE, +/* function */ W_Rifle, +/* ammotype */ ammo_nails, +/* impulse */ 7, +/* flags */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN, +/* rating */ BOT_PICKUP_RATING_MID, +/* color */ '0.5 1 0', +/* modelname */ "campingrifle", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairrifle 0.5", +/* wepimg */ "weaponrifle", +/* refname */ "rifle", +/* wepname */ _("Rifle") +); + +#define RIFLE_SETTINGS(w_cvar,w_prop) RIFLE_SETTINGS_LIST(w_cvar, w_prop, RIFLE, rifle) +#define RIFLE_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, BOTH, ammo) \ + w_cvar(id, sn, BOTH, animtime) \ + w_cvar(id, sn, BOTH, bullethail) \ + w_cvar(id, sn, BOTH, burstcost) \ + w_cvar(id, sn, BOTH, damage) \ + w_cvar(id, sn, BOTH, force) \ + w_cvar(id, sn, BOTH, refire) \ + w_cvar(id, sn, BOTH, shots) \ + w_cvar(id, sn, BOTH, solidpenetration) \ + w_cvar(id, sn, BOTH, spread) \ + w_cvar(id, sn, BOTH, tracer) \ + w_cvar(id, sn, NONE, bursttime) \ + w_cvar(id, sn, NONE, secondary) \ + w_cvar(id, sn, SEC, reload) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +RIFLE_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +.float rifle_accumulator; +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_rifle(void) { weapon_defaultspawnfunc(WEP_RIFLE); } +void spawnfunc_weapon_campingrifle(void) { spawnfunc_weapon_rifle(); } +void spawnfunc_weapon_sniperrifle(void) { spawnfunc_weapon_rifle(); } + +void W_Rifle_FireBullet(float pSpread, float pDamage, float pForce, float pSolidPenetration, float pAmmo, float deathtype, float pTracer, float pShots, string pSound) +{ + float i; + + W_DecreaseAmmo(pAmmo); + + W_SetupShot(self, TRUE, 2, pSound, CH_WEAPON_A, pDamage * pShots); + + pointparticles(particleeffectnum("rifle_muzzleflash"), w_shotorg, w_shotdir * 2000, 1); + + if(self.BUTTON_ZOOM | self.BUTTON_ZOOMSCRIPT) // if zoomed, shoot from the eye + { + w_shotdir = v_forward; + w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward; + } + + for(i = 0; i < pShots; ++i) + fireBullet(w_shotorg, w_shotdir, pSpread, pSolidPenetration, pDamage, pForce, deathtype, (pTracer ? EF_RED : EF_BLUE)); + + if(autocvar_g_casings >= 2) + 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, self); +} + +void W_Rifle_Attack(void) +{ + W_Rifle_FireBullet(WEP_CVAR_PRI(rifle, spread), WEP_CVAR_PRI(rifle, damage), WEP_CVAR_PRI(rifle, force), WEP_CVAR_PRI(rifle, solidpenetration), WEP_CVAR_PRI(rifle, ammo), WEP_RIFLE, WEP_CVAR_PRI(rifle, tracer), WEP_CVAR_PRI(rifle, shots), "weapons/campingrifle_fire.wav"); +} + +void W_Rifle_Attack2(void) +{ + W_Rifle_FireBullet(WEP_CVAR_SEC(rifle, spread), WEP_CVAR_SEC(rifle, damage), WEP_CVAR_SEC(rifle, force), WEP_CVAR_SEC(rifle, solidpenetration), WEP_CVAR_SEC(rifle, ammo), WEP_RIFLE | HITTYPE_SECONDARY, WEP_CVAR_SEC(rifle, tracer), WEP_CVAR_SEC(rifle, shots), "weapons/campingrifle_fire2.wav"); +} + +.void(void) rifle_bullethail_attackfunc; +.float rifle_bullethail_frame; +.float rifle_bullethail_animtime; +.float rifle_bullethail_refire; +void W_Rifle_BulletHail_Continue(void) +{ + float r, sw, af; + + sw = self.switchweapon; // make it not detect weapon changes as reason to abort firing + af = ATTACK_FINISHED(self); + self.switchweapon = self.weapon; + ATTACK_FINISHED(self) = time; + print(ftos(self.WEP_AMMO(RIFLE)), "\n"); + r = weapon_prepareattack(self.rifle_bullethail_frame == WFRAME_FIRE2, self.rifle_bullethail_refire); + if(self.switchweapon == self.weapon) + self.switchweapon = sw; + if(r) + { + self.rifle_bullethail_attackfunc(); + weapon_thinkf(self.rifle_bullethail_frame, self.rifle_bullethail_animtime, W_Rifle_BulletHail_Continue); + print("thinkf set\n"); + } + else + { + ATTACK_FINISHED(self) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time + print("out of ammo... ", ftos(self.weaponentity.state), "\n"); + } +} + +void W_Rifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire) +{ + // if we get here, we have at least one bullet to fire + AttackFunc(); + if(mode) + { + // continue hail + self.rifle_bullethail_attackfunc = AttackFunc; + self.rifle_bullethail_frame = fr; + self.rifle_bullethail_animtime = animtime; + self.rifle_bullethail_refire = refire; + weapon_thinkf(fr, animtime, W_Rifle_BulletHail_Continue); + } + else + { + // just one shot + weapon_thinkf(fr, animtime, w_ready); + } +} + +.float bot_secondary_riflemooth; +float W_Rifle(float req) +{ + float ammo_amount; + + switch(req) + { + case WR_AIM: + { + self.BUTTON_ATCK=FALSE; + self.BUTTON_ATCK2=FALSE; + if(vlen(self.origin-self.enemy.origin) > 1000) + self.bot_secondary_riflemooth = 0; + if(self.bot_secondary_riflemooth == 0) + { + if(bot_aim(1000000, 0, 0.001, FALSE)) + { + self.BUTTON_ATCK = TRUE; + if(random() < 0.01) self.bot_secondary_riflemooth = 1; + } + } + else + { + if(bot_aim(1000000, 0, 0.001, FALSE)) + { + self.BUTTON_ATCK2 = TRUE; + if(random() < 0.03) self.bot_secondary_riflemooth = 0; + } + } + + return TRUE; + } + case WR_THINK: + { + if(autocvar_g_balance_rifle_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(rifle, ammo), WEP_CVAR_SEC(rifle, ammo))) // forced reload + WEP_ACTION(self.weapon, WR_RELOAD); + else + { + self.rifle_accumulator = bound(time - WEP_CVAR(rifle, bursttime), self.rifle_accumulator, time); + if(self.BUTTON_ATCK) + if(weapon_prepareattack_check(0, WEP_CVAR_PRI(rifle, refire))) + if(time >= self.rifle_accumulator + WEP_CVAR_PRI(rifle, burstcost)) + { + weapon_prepareattack_do(0, WEP_CVAR_PRI(rifle, refire)); + W_Rifle_BulletHail(WEP_CVAR_PRI(rifle, bullethail), W_Rifle_Attack, WFRAME_FIRE1, WEP_CVAR_PRI(rifle, animtime), WEP_CVAR_PRI(rifle, refire)); + self.rifle_accumulator += WEP_CVAR_PRI(rifle, burstcost); + } + if(self.BUTTON_ATCK2) + { + if(WEP_CVAR(rifle, secondary)) + { + if(WEP_CVAR_SEC(rifle, reload)) + WEP_ACTION(self.weapon, WR_RELOAD); + else + { + if(weapon_prepareattack_check(1, WEP_CVAR_SEC(rifle, refire))) + if(time >= self.rifle_accumulator + WEP_CVAR_SEC(rifle, burstcost)) + { + weapon_prepareattack_do(1, WEP_CVAR_SEC(rifle, refire)); + W_Rifle_BulletHail(WEP_CVAR_SEC(rifle, bullethail), W_Rifle_Attack2, WFRAME_FIRE2, WEP_CVAR_SEC(rifle, animtime), WEP_CVAR_PRI(rifle, refire)); + self.rifle_accumulator += WEP_CVAR_SEC(rifle, burstcost); + } + } + } + } + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/weapons/g_campingrifle.md3"); + precache_model("models/weapons/v_campingrifle.md3"); + precache_model("models/weapons/h_campingrifle.iqm"); + precache_sound("weapons/campingrifle_fire.wav"); + precache_sound("weapons/campingrifle_fire2.wav"); + RIFLE_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_CHECKAMMO1: + { + ammo_amount = self.WEP_AMMO(RIFLE) >= WEP_CVAR_PRI(rifle, ammo); + ammo_amount += self.(weapon_load[WEP_RIFLE]) >= WEP_CVAR_PRI(rifle, ammo); + return ammo_amount; + } + case WR_CHECKAMMO2: + { + ammo_amount = self.WEP_AMMO(RIFLE) >= WEP_CVAR_SEC(rifle, ammo); + ammo_amount += self.(weapon_load[WEP_RIFLE]) >= WEP_CVAR_SEC(rifle, ammo); + return ammo_amount; + } + case WR_CONFIG: + { + RIFLE_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_RESETPLAYER: + { + self.rifle_accumulator = time - WEP_CVAR(rifle, bursttime); + return TRUE; + } + case WR_RELOAD: + { + W_Reload(min(WEP_CVAR_PRI(rifle, ammo), WEP_CVAR_SEC(rifle, ammo)), "weapons/reload.wav"); + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + return WEAPON_THINKING_WITH_PORTALS; + } + case WR_KILLMESSAGE: + { + if(w_deathtype & HITTYPE_SECONDARY) + { + if(w_deathtype & HITTYPE_BOUNCE) + return WEAPON_RIFLE_MURDER_HAIL_PIERCING; + else + return WEAPON_RIFLE_MURDER_HAIL; + } + else + { + if(w_deathtype & HITTYPE_BOUNCE) + return WEAPON_RIFLE_MURDER_PIERCING; + else + return WEAPON_RIFLE_MURDER; + } + } + } + return FALSE; +} +#endif +#ifdef CSQC +float W_Rifle(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 2; + pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1); + if(!w_issilent) + { + if(w_random < 0.2) + sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTN_NORM); + else if(w_random < 0.4) + sound(self, CH_SHOTS, "weapons/ric2.wav", VOL_BASE, ATTN_NORM); + else if(w_random < 0.5) + sound(self, CH_SHOTS, "weapons/ric3.wav", VOL_BASE, ATTN_NORM); + } + + return TRUE; + } + case WR_INIT: + { + precache_sound("weapons/ric1.wav"); + precache_sound("weapons/ric2.wav"); + precache_sound("weapons/ric3.wav"); + if(autocvar_cl_reticle && autocvar_cl_reticle_weapon) + { + precache_pic("gfx/reticle_nex"); + } + return TRUE; + } + case WR_ZOOMRETICLE: + { + if(button_zoom || zoomscript_caught) + { + reticle_image = "gfx/reticle_nex"; + return TRUE; + } + else + { + // no weapon specific image for this weapon + return FALSE; + } + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_seeker.qc b/qcsrc/common/weapons/w_seeker.qc new file mode 100644 index 0000000000..7d933d5ca6 --- /dev/null +++ b/qcsrc/common/weapons/w_seeker.qc @@ -0,0 +1,792 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ SEEKER, +/* function */ W_Seeker, +/* ammotype */ ammo_rockets, +/* impulse */ 8, +/* flags */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH, +/* rating */ BOT_PICKUP_RATING_MID, +/* color */ '0.5 1 0', +/* modelname */ "seeker", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairseeker 0.8", +/* wepimg */ "weaponseeker", +/* refname */ "seeker", +/* wepname */ _("T.A.G. Seeker") +); + +#define SEEKER_SETTINGS(w_cvar,w_prop) SEEKER_SETTINGS_LIST(w_cvar, w_prop, SEEKER, seeker) +#define SEEKER_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, NONE, type) \ + w_cvar(id, sn, NONE, flac_ammo) \ + w_cvar(id, sn, NONE, flac_animtime) \ + w_cvar(id, sn, NONE, flac_damage) \ + w_cvar(id, sn, NONE, flac_edgedamage) \ + w_cvar(id, sn, NONE, flac_force) \ + w_cvar(id, sn, NONE, flac_lifetime) \ + w_cvar(id, sn, NONE, flac_lifetime_rand) \ + w_cvar(id, sn, NONE, flac_radius) \ + w_cvar(id, sn, NONE, flac_refire) \ + w_cvar(id, sn, NONE, flac_speed) \ + w_cvar(id, sn, NONE, flac_speed_up) \ + w_cvar(id, sn, NONE, flac_speed_z) \ + w_cvar(id, sn, NONE, flac_spread) \ + w_cvar(id, sn, NONE, missile_accel) \ + w_cvar(id, sn, NONE, missile_ammo) \ + w_cvar(id, sn, NONE, missile_animtime) \ + w_cvar(id, sn, NONE, missile_count) \ + w_cvar(id, sn, NONE, missile_damage) \ + w_cvar(id, sn, NONE, missile_damageforcescale) \ + w_cvar(id, sn, NONE, missile_decel) \ + w_cvar(id, sn, NONE, missile_delay) \ + w_cvar(id, sn, NONE, missile_edgedamage) \ + w_cvar(id, sn, NONE, missile_force) \ + w_cvar(id, sn, NONE, missile_health) \ + w_cvar(id, sn, NONE, missile_lifetime) \ + w_cvar(id, sn, NONE, missile_proxy) \ + w_cvar(id, sn, NONE, missile_proxy_delay) \ + w_cvar(id, sn, NONE, missile_proxy_maxrange) \ + w_cvar(id, sn, NONE, missile_radius) \ + w_cvar(id, sn, NONE, missile_refire) \ + w_cvar(id, sn, NONE, missile_smart) \ + w_cvar(id, sn, NONE, missile_smart_mindist) \ + w_cvar(id, sn, NONE, missile_smart_trace_max) \ + w_cvar(id, sn, NONE, missile_smart_trace_min) \ + w_cvar(id, sn, NONE, missile_speed) \ + w_cvar(id, sn, NONE, missile_speed_max) \ + w_cvar(id, sn, NONE, missile_speed_up) \ + w_cvar(id, sn, NONE, missile_speed_z) \ + w_cvar(id, sn, NONE, missile_spread) \ + w_cvar(id, sn, NONE, missile_turnrate) \ + w_cvar(id, sn, NONE, tag_ammo) \ + w_cvar(id, sn, NONE, tag_animtime) \ + w_cvar(id, sn, NONE, tag_damageforcescale) \ + w_cvar(id, sn, NONE, tag_health) \ + w_cvar(id, sn, NONE, tag_lifetime) \ + w_cvar(id, sn, NONE, tag_refire) \ + w_cvar(id, sn, NONE, tag_speed) \ + w_cvar(id, sn, NONE, tag_spread) \ + w_cvar(id, sn, NONE, tag_tracker_lifetime) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +SEEKER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +.entity tag_target, wps_tag_tracker; +.float tag_time; +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_seeker(void) { weapon_defaultspawnfunc(WEP_SEEKER); } + +// ============================ +// Begin: Missile functions, these are general functions to be manipulated by other code +// ============================ +void W_Seeker_Missile_Explode(void) +{ + self.event_damage = func_null; + RadiusDamage(self, self.realowner, WEP_CVAR(seeker, missile_damage), WEP_CVAR(seeker, missile_edgedamage), WEP_CVAR(seeker, missile_radius), world, world, WEP_CVAR(seeker, missile_force), self.projectiledeathtype, other); + + remove(self); +} + +void W_Seeker_Missile_Touch(void) +{ + PROJECTILE_TOUCH; + + W_Seeker_Missile_Explode(); +} + +void W_Seeker_Missile_Think(void) +{ + entity e; + vector desireddir, olddir, newdir, eorg; + float turnrate; + float dist; + float spd; + + if(time > self.cnt) + { + self.projectiledeathtype |= HITTYPE_SPLASH; + W_Seeker_Missile_Explode(); + } + + spd = vlen(self.velocity); + spd = bound( + spd - WEP_CVAR(seeker, missile_decel) * frametime, + WEP_CVAR(seeker, missile_speed_max), + spd + WEP_CVAR(seeker, missile_accel) * frametime + ); + + if(self.enemy != world) + if(self.enemy.takedamage != DAMAGE_AIM || self.enemy.deadflag != DEAD_NO) + self.enemy = world; + + if(self.enemy != world) + { + e = self.enemy; + eorg = 0.5 * (e.absmin + e.absmax); + turnrate = WEP_CVAR(seeker, missile_turnrate); // how fast to turn + desireddir = normalize(eorg - self.origin); + olddir = normalize(self.velocity); // get my current direction + dist = vlen(eorg - self.origin); + + // Do evasive maneuvers for world objects? ( this should be a cpu hog. :P ) + if(WEP_CVAR(seeker, missile_smart) && (dist > WEP_CVAR(seeker, missile_smart_mindist))) + { + // Is it a better idea (shorter distance) to trace to the target itself? + if( vlen(self.origin + olddir * self.wait) < dist) + traceline(self.origin, self.origin + olddir * self.wait, FALSE, self); + else + traceline(self.origin, eorg, FALSE, self); + + // Setup adaptive tracelength + self.wait = bound(WEP_CVAR(seeker, missile_smart_trace_min), vlen(self.origin - trace_endpos), self.wait = WEP_CVAR(seeker, missile_smart_trace_max)); + + // Calc how important it is that we turn and add this to the desierd (enemy) dir. + desireddir = normalize(((trace_plane_normal * (1 - trace_fraction)) + (desireddir * trace_fraction)) * 0.5); + } + + newdir = normalize(olddir + desireddir * turnrate); // take the average of the 2 directions; not the best method but simple & easy + self.velocity = newdir * spd; // make me fly in the new direction at my flight speed + } + else + dist = 0; + + // Proxy + if(WEP_CVAR(seeker, missile_proxy)) + { + if(dist <= WEP_CVAR(seeker, missile_proxy_maxrange)) + { + if(self.autoswitch == 0) + { + self.autoswitch = time + WEP_CVAR(seeker, missile_proxy_delay); + } + else + { + if(self.autoswitch <= time) + { + W_Seeker_Missile_Explode(); + self.autoswitch = 0; + } + } + } + else + { + if(self.autoswitch != 0) + self.autoswitch = 0; + } + } + /////////////// + + if(self.enemy.deadflag != DEAD_NO) + { + self.enemy = world; + self.cnt = time + 1 + (random() * 4); + self.nextthink = self.cnt; + return; + } + + //self.angles = vectoangles(self.velocity); // turn model in the new flight direction + self.nextthink = time;// + 0.05; // csqc projectiles + UpdateCSQCProjectile(self); +} + + + +void W_Seeker_Missile_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if(self.health <= 0) + return; + + if(!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions + return; // g_projectiles_damage says to halt + + if(self.realowner == attacker) + self.health = self.health - (damage * 0.25); + else + self.health = self.health - damage; + + if(self.health <= 0) + W_PrepareExplosionByDamage(attacker, W_Seeker_Missile_Explode); +} + +/* +void W_Seeker_Missile_Animate(void) +{ + self.frame = self.frame +1; + self.nextthink = time + 0.05; + + if(self.enemy != world) + if(self.enemy.takedamage != DAMAGE_AIM || self.enemy.deadflag != DEAD_NO) + self.enemy = world; + + if(self.frame == 5) + { + self.think = W_Seeker_Missile_Think; + self.nextthink = time;// + cvar("g_balance_seeker_missile_activate_delay"); // cant dealy with csqc projectiles + + if(autocvar_g_balance_seeker_missile_proxy) + self.movetype = MOVETYPE_BOUNCEMISSILE; + else + self.movetype = MOVETYPE_FLYMISSILE; + } + + UpdateCSQCProjectile(self); +} +*/ + +void W_Seeker_Fire_Missile(vector f_diff, entity m_target) +{ + entity missile; + + W_DecreaseAmmo(WEP_CVAR(seeker, missile_ammo)); + + makevectors(self.v_angle); + W_SetupShot_ProjectileSize(self, '-2 -2 -2', '2 2 2', FALSE, 2, "weapons/seeker_fire.wav", CH_WEAPON_A, 0); + w_shotorg += f_diff; + pointparticles(particleeffectnum("seeker_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + //self.detornator = FALSE; + + missile = spawn(); + missile.owner = missile.realowner = self; + missile.classname = "seeker_missile"; + missile.bot_dodge = TRUE; + missile.bot_dodgerating = WEP_CVAR(seeker, missile_damage); + + missile.think = W_Seeker_Missile_Think; + missile.touch = W_Seeker_Missile_Touch; + missile.event_damage = W_Seeker_Missile_Damage; + missile.nextthink = time;// + 0.2;// + cvar("g_balance_seeker_missile_activate_delay"); + missile.cnt = time + WEP_CVAR(seeker, missile_lifetime); + missile.enemy = m_target; + missile.solid = SOLID_BBOX; + missile.scale = 2; + missile.takedamage = DAMAGE_YES; + missile.health = WEP_CVAR(seeker, missile_health); + missile.damageforcescale = WEP_CVAR(seeker, missile_damageforcescale); + missile.damagedbycontents = TRUE; + //missile.think = W_Seeker_Missile_Animate; // csqc projectiles. + + if(missile.enemy != world) + missile.projectiledeathtype = WEP_SEEKER | HITTYPE_SECONDARY; + else + missile.projectiledeathtype = WEP_SEEKER; + + + setorigin(missile, w_shotorg); + setsize(missile, '-4 -4 -4', '4 4 4'); + missile.movetype = MOVETYPE_FLYMISSILE; + missile.flags = FL_PROJECTILE; + missile.missile_flags = MIF_SPLASH | MIF_GUIDED_TAG; + + W_SetupProjVelocity_UP_PRE(missile, seeker, missile_); + + missile.angles = vectoangles(missile.velocity); + + CSQCProjectile(missile, FALSE, PROJECTILE_SEEKER, TRUE); + + other = missile; MUTATOR_CALLHOOK(EditProjectile); +} + +// ============================ +// Begin: FLAC, close range attack meant for defeating rockets which are coming at you. +// ============================ +void W_Seeker_Flac_Explode(void) +{ + self.event_damage = func_null; + + RadiusDamage(self, self.realowner, WEP_CVAR(seeker, flac_damage), WEP_CVAR(seeker, flac_edgedamage), WEP_CVAR(seeker, flac_radius), world, world, WEP_CVAR(seeker, flac_force), self.projectiledeathtype, other); + + remove(self); +} + +void W_Seeker_Flac_Touch(void) +{ + PROJECTILE_TOUCH; + + W_Seeker_Flac_Explode(); +} + +void W_Seeker_Fire_Flac(void) +{ + entity missile; + vector f_diff; + float c; + + W_DecreaseAmmo(WEP_CVAR(seeker, flac_ammo)); + + c = mod(self.bulletcounter, 4); + switch(c) + { + case 0: + f_diff = '-1.25 -3.75 0'; + break; + case 1: + f_diff = '+1.25 -3.75 0'; + break; + case 2: + f_diff = '-1.25 +3.75 0'; + break; + case 3: + default: + f_diff = '+1.25 +3.75 0'; + break; + } + W_SetupShot_ProjectileSize(self, '-2 -2 -2', '2 2 2', FALSE, 2, "weapons/flac_fire.wav", CH_WEAPON_A, WEP_CVAR(seeker, flac_damage)); + w_shotorg += f_diff; + + pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + missile = spawn(); + missile.owner = missile.realowner = self; + missile.classname = "missile"; + missile.bot_dodge = TRUE; + missile.bot_dodgerating = WEP_CVAR(seeker, flac_damage); + missile.touch = W_Seeker_Flac_Explode; + missile.use = W_Seeker_Flac_Explode; + missile.think = adaptor_think2use_hittype_splash; + missile.nextthink = time + WEP_CVAR(seeker, flac_lifetime) + WEP_CVAR(seeker, flac_lifetime_rand); + missile.solid = SOLID_BBOX; + missile.movetype = MOVETYPE_FLY; + missile.projectiledeathtype = WEP_SEEKER; + missile.projectiledeathtype = WEP_SEEKER | HITTYPE_SECONDARY; + missile.flags = FL_PROJECTILE; + missile.missile_flags = MIF_SPLASH; + + // csqc projectiles + //missile.angles = vectoangles(missile.velocity); + //missile.scale = 0.4; // BUG: the model is too big + + setorigin(missile, w_shotorg); + setsize(missile, '-2 -2 -2', '2 2 2'); + + W_SetupProjVelocity_UP_PRE(missile, seeker, flac_); + CSQCProjectile(missile, TRUE, PROJECTILE_FLAC, TRUE); + + other = missile; MUTATOR_CALLHOOK(EditProjectile); +} + +// ============================ +// Begin: Tag and rocket controllers +// ============================ +entity W_Seeker_Tagged_Info(entity isowner, entity istarget) +{ + entity tag; + for(tag = world; (tag = find(tag, classname, "tag_tracker")); ) + if((tag.realowner == isowner) && (tag.tag_target == istarget)) + return tag; + + return world; +} + +void W_Seeker_Attack(void) +{ + entity tracker, closest_target; + + closest_target = world; + for(tracker = world; (tracker = find(tracker, classname, "tag_tracker")); ) if (tracker.realowner == self) + { + if(closest_target) + { + if(vlen(self.origin - tracker.tag_target.origin) < vlen(self.origin - closest_target.origin)) + closest_target = tracker.tag_target; + } + else + closest_target = tracker.tag_target; + } + + traceline(self.origin + self.view_ofs, closest_target.origin, MOVE_NOMONSTERS, self); + if((!closest_target) || ((trace_fraction < 1) && (trace_ent != closest_target))) + closest_target = world; + + W_Seeker_Fire_Missile('0 0 0', closest_target); +} + +void W_Seeker_Vollycontroller_Think(void) // TODO: Merge this with W_Seeker_Attack +{ + float c; + entity oldself,oldenemy; + self.cnt = self.cnt - 1; + + if((!(self.realowner.items & IT_UNLIMITED_AMMO) && self.realowner.WEP_AMMO(SEEKER) < WEP_CVAR(seeker, missile_ammo)) || (self.cnt <= -1) || (self.realowner.deadflag != DEAD_NO) || (self.realowner.switchweapon != WEP_SEEKER)) + { + remove(self); + return; + } + + self.nextthink = time + WEP_CVAR(seeker, missile_delay) * W_WeaponRateFactor(); + + oldself = self; + self = self.realowner; + + oldenemy = self.enemy; + self.enemy = oldself.enemy; + + c = mod(self.cnt, 4); + switch(c) + { + case 0: + W_Seeker_Fire_Missile('-1.25 -3.75 0', self.enemy); + break; + case 1: + W_Seeker_Fire_Missile('+1.25 -3.75 0', self.enemy); + break; + case 2: + W_Seeker_Fire_Missile('-1.25 +3.75 0', self.enemy); + break; + case 3: + default: + W_Seeker_Fire_Missile('+1.25 +3.75 0', self.enemy); + break; + } + + self.enemy = oldenemy; + self = oldself; +} + +void W_Seeker_Tracker_Think(void) +{ + // commit suicide if: You die OR target dies OR you switch away from the seeker OR commit suicide if lifetime is up + if((self.realowner.deadflag != DEAD_NO) || (self.tag_target.deadflag != DEAD_NO) || (self.realowner.switchweapon != WEP_SEEKER) + || (time > self.tag_time + WEP_CVAR(seeker, tag_tracker_lifetime))) + { + if(self) + { + WaypointSprite_Kill(self.tag_target.wps_tag_tracker); + remove(self); + } + return; + } + + // Update the think method information + self.nextthink = time; +} + +// ============================ +// Begin: Tag projectile +// ============================ +void W_Seeker_Tag_Explode(void) +{ + //if(other==self.realowner) + // return; + Damage_DamageInfo(self.origin, 0, 0, 0, self.velocity, WEP_SEEKER | HITTYPE_BOUNCE, other.species, self); + + remove(self); +} + +void W_Seeker_Tag_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) +{ + if(self.health <= 0) + return; + self.health = self.health - damage; + if(self.health <= 0) + W_Seeker_Tag_Explode(); +} + +void W_Seeker_Tag_Touch(void) +{ + vector dir; + vector org2; + entity e; + + PROJECTILE_TOUCH; + + dir = normalize(self.realowner.origin - self.origin); + org2 = findbetterlocation(self.origin, 8); + + te_knightspike(org2); + + self.event_damage = func_null; + Damage_DamageInfo(self.origin, 0, 0, 0, self.velocity, WEP_SEEKER | HITTYPE_BOUNCE | HITTYPE_SECONDARY, other.species, self); + + if(other.takedamage == DAMAGE_AIM && other.deadflag == DEAD_NO) + { + // check to see if this person is already tagged by me + entity tag = W_Seeker_Tagged_Info(self.realowner, other); + + if(tag != world) + { + if(other.wps_tag_tracker && (WEP_CVAR(seeker, type) == 1)) // don't attach another waypointsprite without killing the old one first + WaypointSprite_Kill(other.wps_tag_tracker); + + tag.tag_time = time; + } + else + { + //sprint(self.realowner, strcat("You just tagged ^2", other.netname, "^7 with a tracking device!\n")); + e = spawn(); + e.cnt = WEP_CVAR(seeker, missile_count); + e.classname = "tag_tracker"; + e.owner = self.owner; + e.realowner = self.realowner; + + if(WEP_CVAR(seeker, type) == 1) + { + e.tag_target = other; + e.tag_time = time; + e.think = W_Seeker_Tracker_Think; + } + else + { + e.enemy = other; + e.think = W_Seeker_Vollycontroller_Think; + } + + e.nextthink = time; + } + + if(WEP_CVAR(seeker, type) == 1) + { + WaypointSprite_Spawn("tagged-target", WEP_CVAR(seeker, tag_tracker_lifetime), 0, other, '0 0 64', self.realowner, 0, other, wps_tag_tracker, TRUE, RADARICON_TAGGED, '0.5 1 0'); + WaypointSprite_UpdateRule(other.wps_tag_tracker, 0, SPRITERULE_DEFAULT); + } + } + + remove(self); + return; +} + +void W_Seeker_Fire_Tag(void) +{ + entity missile; + W_DecreaseAmmo(WEP_CVAR(seeker, tag_ammo)); + + W_SetupShot_ProjectileSize(self, '-2 -2 -2', '2 2 2', FALSE, 2, "weapons/tag_fire.wav", CH_WEAPON_A, WEP_CVAR(seeker, missile_damage) * WEP_CVAR(seeker, missile_count)); + + missile = spawn(); + missile.owner = missile.realowner = self; + missile.classname = "seeker_tag"; + missile.bot_dodge = TRUE; + missile.bot_dodgerating = 50; + missile.touch = W_Seeker_Tag_Touch; + missile.think = SUB_Remove; + missile.nextthink = time + WEP_CVAR(seeker, tag_lifetime); + missile.movetype = MOVETYPE_FLY; + missile.solid = SOLID_BBOX; + + missile.takedamage = DAMAGE_YES; + missile.event_damage = W_Seeker_Tag_Damage; + missile.health = WEP_CVAR(seeker, tag_health); + missile.damageforcescale = WEP_CVAR(seeker, tag_damageforcescale); + + setorigin(missile, w_shotorg); + setsize(missile, '-2 -2 -2', '2 2 2'); + + missile.flags = FL_PROJECTILE; + //missile.missile_flags = MIF_..?; + + missile.movetype = MOVETYPE_FLY; + W_SetupProjVelocity_PRE(missile, seeker, tag_); + missile.angles = vectoangles(missile.velocity); + + CSQCProjectile(missile, TRUE, PROJECTILE_TAG, FALSE); // has sound + + other = missile; MUTATOR_CALLHOOK(EditProjectile); +} + +// ============================ +// Begin: Genereal weapon functions +// ============================ + +float W_Seeker(float req) +{ + float ammo_amount; + + switch(req) + { + case WR_AIM: + { + if(WEP_CVAR(seeker, type) == 1) + if(W_Seeker_Tagged_Info(self, self.enemy) != world) + self.BUTTON_ATCK = bot_aim(WEP_CVAR(seeker, missile_speed_max), 0, WEP_CVAR(seeker, missile_lifetime), FALSE); + else + self.BUTTON_ATCK2 = bot_aim(WEP_CVAR(seeker, tag_speed), 0, WEP_CVAR(seeker, tag_lifetime), FALSE); + else + self.BUTTON_ATCK = bot_aim(WEP_CVAR(seeker, tag_speed), 0, WEP_CVAR(seeker, tag_lifetime), FALSE); + return TRUE; + } + case WR_THINK: + { + if(autocvar_g_balance_seeker_reload_ammo && self.clip_load < min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo))) // forced reload + WEP_ACTION(self.weapon, WR_RELOAD); + + else if(self.BUTTON_ATCK) + { + if(WEP_CVAR(seeker, type) == 1) + { + if(weapon_prepareattack(0, WEP_CVAR(seeker, missile_refire))) + { + W_Seeker_Attack(); + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(seeker, missile_animtime), w_ready); + } + } + else + { + if(weapon_prepareattack(0, WEP_CVAR(seeker, tag_refire))) + { + W_Seeker_Fire_Tag(); + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready); + } + } + } + + else if(self.BUTTON_ATCK2) + { + if(WEP_CVAR(seeker, type) == 1) + { + if(weapon_prepareattack(0, WEP_CVAR(seeker, tag_refire))) + { + W_Seeker_Fire_Tag(); + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready); + } + } + else + { + if(weapon_prepareattack(0, WEP_CVAR(seeker, flac_refire))) + { + W_Seeker_Fire_Flac(); + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(seeker, flac_animtime), w_ready); + } + } + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/weapons/g_seeker.md3"); + precache_model("models/weapons/v_seeker.md3"); + precache_model("models/weapons/h_seeker.iqm"); + precache_sound("weapons/tag_fire.wav"); + precache_sound("weapons/flac_fire.wav"); + precache_sound("weapons/seeker_fire.wav"); + SEEKER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_CHECKAMMO1: + { + if(WEP_CVAR(seeker, type) == 1) + { + ammo_amount = self.WEP_AMMO(SEEKER) >= WEP_CVAR(seeker, missile_ammo); + ammo_amount += self.(weapon_load[WEP_SEEKER]) >= WEP_CVAR(seeker, missile_ammo); + } + else + { + ammo_amount = self.WEP_AMMO(SEEKER) >= WEP_CVAR(seeker, tag_ammo); + ammo_amount += self.(weapon_load[WEP_SEEKER]) >= WEP_CVAR(seeker, tag_ammo); + } + return ammo_amount; + } + case WR_CHECKAMMO2: + { + if(WEP_CVAR(seeker, type) == 1) + { + ammo_amount = self.WEP_AMMO(SEEKER) >= WEP_CVAR(seeker, tag_ammo); + ammo_amount += self.(weapon_load[WEP_SEEKER]) >= WEP_CVAR(seeker, tag_ammo); + } + else + { + ammo_amount = self.WEP_AMMO(SEEKER) >= WEP_CVAR(seeker, flac_ammo); + ammo_amount += self.(weapon_load[WEP_SEEKER]) >= WEP_CVAR(seeker, flac_ammo); + } + return ammo_amount; + } + case WR_CONFIG: + { + SEEKER_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_RELOAD: + { + W_Reload(min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo)), "weapons/reload.wav"); + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + return WEAPON_SEEKER_SUICIDE; + } + case WR_KILLMESSAGE: + { + if(w_deathtype & HITTYPE_SECONDARY) + return WEAPON_SEEKER_MURDER_TAG; + else + return WEAPON_SEEKER_MURDER_SPRAY; + } + } + return FALSE; +} +#endif +#ifdef CSQC +float W_Seeker(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 6; + if(w_deathtype & HITTYPE_BOUNCE) + { + if(w_deathtype & HITTYPE_SECONDARY) + { + if(!w_issilent) + sound(self, CH_SHOTS, "weapons/tag_impact.wav", 1, ATTEN_NORM); + } + else + { + pointparticles(particleeffectnum("hagar_explode"), org2, '0 0 0', 1); + if(!w_issilent) + { + if(w_random<0.15) + sound(self, CH_SHOTS, "weapons/tagexp1.wav", 1, ATTEN_NORM); + else if(w_random<0.7) + sound(self, CH_SHOTS, "weapons/tagexp2.wav", 1, ATTEN_NORM); + else + sound(self, CH_SHOTS, "weapons/tagexp3.wav", 1, ATTEN_NORM); + } + } + } + else + { + pointparticles(particleeffectnum("hagar_explode"), org2, '0 0 0', 1); + if(!w_issilent) + { + if(w_random<0.15) + sound(self, CH_SHOTS, "weapons/seekerexp1.wav", 1, ATTEN_NORM); + else if(w_random<0.7) + sound(self, CH_SHOTS, "weapons/seekerexp2.wav", 1, ATTEN_NORM); + else + sound(self, CH_SHOTS, "weapons/seekerexp3.wav", 1, ATTEN_NORM); + } + } + return TRUE; + } + case WR_INIT: + { + precache_sound("weapons/seekerexp1.wav"); + precache_sound("weapons/seekerexp2.wav"); + precache_sound("weapons/seekerexp3.wav"); + precache_sound("weapons/tagexp1.wav"); + precache_sound("weapons/tagexp2.wav"); + precache_sound("weapons/tagexp3.wav"); + precache_sound("weapons/tag_impact.wav"); + return TRUE; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_shockwave.qc b/qcsrc/common/weapons/w_shockwave.qc new file mode 100644 index 0000000000..759dc35eae --- /dev/null +++ b/qcsrc/common/weapons/w_shockwave.qc @@ -0,0 +1,897 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ SHOCKWAVE, +/* function */ W_Shockwave, +/* ammotype */ ammo_none, +/* impulse */ 2, +/* flags */ WEP_FLAG_NORMAL | WEP_TYPE_HITSCAN | WEP_FLAG_CANCLIMB | WEP_FLAG_MUTATORBLOCKED, +/* rating */ BOT_PICKUP_RATING_LOW, +/* color */ '0.5 0.25 0', +/* modelname */ "shotgun", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairshotgun 0.7", +/* wepimg */ "weaponshotgun", +/* refname */ "shockwave", +/* wepname */ _("Shockwave") +); + +#define SHOCKWAVE_SETTINGS(w_cvar,w_prop) SHOCKWAVE_SETTINGS_LIST(w_cvar, w_prop, SHOCKWAVE, shockwave) +#define SHOCKWAVE_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, NONE, blast_animtime) \ + w_cvar(id, sn, NONE, blast_damage) \ + w_cvar(id, sn, NONE, blast_distance) \ + w_cvar(id, sn, NONE, blast_edgedamage) \ + w_cvar(id, sn, NONE, blast_force) \ + w_cvar(id, sn, NONE, blast_force_forwardbias) \ + w_cvar(id, sn, NONE, blast_force_zscale) \ + w_cvar(id, sn, NONE, blast_jump_damage) \ + w_cvar(id, sn, NONE, blast_jump_edgedamage) \ + w_cvar(id, sn, NONE, blast_jump_force) \ + w_cvar(id, sn, NONE, blast_jump_force_velocitybias) \ + w_cvar(id, sn, NONE, blast_jump_force_zscale) \ + w_cvar(id, sn, NONE, blast_jump_multiplier_accuracy) \ + w_cvar(id, sn, NONE, blast_jump_multiplier_distance) \ + w_cvar(id, sn, NONE, blast_jump_multiplier_min) \ + w_cvar(id, sn, NONE, blast_jump_radius) \ + w_cvar(id, sn, NONE, blast_multiplier_accuracy) \ + w_cvar(id, sn, NONE, blast_multiplier_distance) \ + w_cvar(id, sn, NONE, blast_multiplier_min) \ + w_cvar(id, sn, NONE, blast_refire) \ + w_cvar(id, sn, NONE, blast_splash_damage) \ + w_cvar(id, sn, NONE, blast_splash_edgedamage) \ + w_cvar(id, sn, NONE, blast_splash_force) \ + w_cvar(id, sn, NONE, blast_splash_force_forwardbias) \ + w_cvar(id, sn, NONE, blast_splash_multiplier_accuracy) \ + w_cvar(id, sn, NONE, blast_splash_multiplier_distance) \ + w_cvar(id, sn, NONE, blast_splash_multiplier_min) \ + w_cvar(id, sn, NONE, blast_splash_radius) \ + w_cvar(id, sn, NONE, blast_spread_max) \ + w_cvar(id, sn, NONE, blast_spread_min) \ + w_cvar(id, sn, NONE, melee_animtime) \ + w_cvar(id, sn, NONE, melee_damage) \ + w_cvar(id, sn, NONE, melee_delay) \ + w_cvar(id, sn, NONE, melee_force) \ + w_cvar(id, sn, NONE, melee_multihit) \ + w_cvar(id, sn, NONE, melee_no_doubleslap) \ + w_cvar(id, sn, NONE, melee_nonplayerdamage) \ + w_cvar(id, sn, NONE, melee_range) \ + w_cvar(id, sn, NONE, melee_refire) \ + w_cvar(id, sn, NONE, melee_swing_side) \ + w_cvar(id, sn, NONE, melee_swing_up) \ + w_cvar(id, sn, NONE, melee_time) \ + w_cvar(id, sn, NONE, melee_traces) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +SHOCKWAVE_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +#endif +#ifdef CSQC +void Net_ReadShockwaveParticle(void); +.vector sw_shotorg; +.vector sw_shotdir; +.float sw_distance; +.float sw_spread_max; +.float sw_spread_min; +.float sw_time; +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_shockwave(void) +{ + //if(autocvar_sv_q3acompat_machineshockwaveswap) // WEAPONTODO + if(autocvar_sv_q3acompat_machineshotgunswap) + if(self.classname != "droppedweapon") + { + weapon_defaultspawnfunc(WEP_MACHINEGUN); + return; + } + weapon_defaultspawnfunc(WEP_SHOCKWAVE); +} + +#define MAX_SHOCKWAVE_HITS 10 +//#define DEBUG_SHOCKWAVE + +.float swing_prev; +.entity swing_alreadyhit; +.float shockwave_blasttime; +entity shockwave_hit[MAX_SHOCKWAVE_HITS]; +float shockwave_hit_damage[MAX_SHOCKWAVE_HITS]; +vector shockwave_hit_force[MAX_SHOCKWAVE_HITS]; + +// MELEE ATTACK MODE +void W_Shockwave_Melee_Think(void) +{ + // declarations + float i, f, swing, swing_factor, swing_damage, meleetime, is_player; + entity target_victim; + vector targpos; + + // check to see if we can still continue, otherwise give up now + if((self.realowner.deadflag != DEAD_NO) && WEP_CVAR(shockwave, melee_no_doubleslap)) + { + remove(self); + return; + } + + // set start time of melee + if(!self.cnt) + { + self.cnt = time; + W_PlayStrengthSound(self.realowner); + } + + // update values for v_* vectors + makevectors(self.realowner.v_angle); + + // calculate swing percentage based on time + meleetime = WEP_CVAR(shockwave, melee_time) * W_WeaponRateFactor(); + swing = bound(0, (self.cnt + meleetime - time) / meleetime, 10); + f = ((1 - swing) * WEP_CVAR(shockwave, melee_traces)); + + // perform the traces needed for this frame + for(i=self.swing_prev; i < f; ++i) + { + swing_factor = ((1 - (i / WEP_CVAR(shockwave, melee_traces))) * 2 - 1); + + targpos = (self.realowner.origin + self.realowner.view_ofs + + (v_forward * WEP_CVAR(shockwave, melee_range)) + + (v_up * swing_factor * WEP_CVAR(shockwave, melee_swing_up)) + + (v_right * swing_factor * WEP_CVAR(shockwave, melee_swing_side))); + + WarpZone_traceline_antilag( + self.realowner, + (self.realowner.origin + self.realowner.view_ofs), + targpos, + FALSE, + self.realowner, + ANTILAG_LATENCY(self.realowner) + ); + + // draw lightning beams for debugging +#ifdef DEBUG_SHOCKWAVE + te_lightning2(world, targpos, self.realowner.origin + self.realowner.view_ofs + v_forward * 5 - v_up * 5); + te_customflash(targpos, 40, 2, '1 1 1'); +#endif + + is_player = (IS_PLAYER(trace_ent) || trace_ent.classname == "body" || (trace_ent.flags & FL_MONSTER)); + + if((trace_fraction < 1) // if trace is good, apply the damage and remove self if necessary + && (trace_ent.takedamage == DAMAGE_AIM) + && (trace_ent != self.swing_alreadyhit) + && (is_player || WEP_CVAR(shockwave, melee_nonplayerdamage))) + { + target_victim = trace_ent; // so it persists through other calls + + if(is_player) // this allows us to be able to nerf the non-player damage done in e.g. assault or onslaught + swing_damage = (WEP_CVAR(shockwave, melee_damage) * min(1, swing_factor + 1)); + else + swing_damage = (WEP_CVAR(shockwave, melee_nonplayerdamage) * min(1, swing_factor + 1)); + + // trigger damage with this calculated info + Damage( + target_victim, + self.realowner, + self.realowner, + swing_damage, + (WEP_SHOCKWAVE | HITTYPE_SECONDARY), + (self.realowner.origin + self.realowner.view_ofs), + (v_forward * WEP_CVAR(shockwave, melee_force)) + ); + + // handle accuracy + if(accuracy_isgooddamage(self.realowner, target_victim)) + { accuracy_add(self.realowner, WEP_SHOCKWAVE, 0, swing_damage); } + + #ifdef DEBUG_SHOCKWAVE + print(sprintf( + "MELEE: %s hitting %s with %f damage (factor: %f) at %f time.\n", + self.realowner.netname, + target_victim.netname, + swing_damage, + swing_factor, + time + )); + #endif + + // allow multiple hits with one swing, but not against the same player twice + if(WEP_CVAR(shockwave, melee_multihit)) + { + self.swing_alreadyhit = target_victim; + continue; // move along to next trace + } + else + { + remove(self); + return; + } + } + } + + if(time >= self.cnt + meleetime) + { + // melee is finished + remove(self); + return; + } + else + { + // set up next frame + self.swing_prev = i; + self.nextthink = time; + } +} + +void W_Shockwave_Melee(void) +{ + sound(self, CH_WEAPON_A, "weapons/shotgun_melee.wav", VOL_BASE, ATTN_NORM); + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(shockwave, melee_animtime), w_ready); + + entity meleetemp; + meleetemp = spawn(); + meleetemp.owner = meleetemp.realowner = self; + meleetemp.think = W_Shockwave_Melee_Think; + meleetemp.nextthink = time + WEP_CVAR(shockwave, melee_delay) * W_WeaponRateFactor(); + W_SetupShot_Range(self, TRUE, 0, "", 0, WEP_CVAR(shockwave, melee_damage), WEP_CVAR(shockwave, melee_range)); +} + +// SHOCKWAVE ATTACK MODE +float W_Shockwave_Attack_CheckSpread( + vector targetorg, + vector nearest_on_line, + vector sw_shotorg, + vector attack_endpos) +{ + float spreadlimit; + float distance_of_attack = vlen(sw_shotorg - attack_endpos); + float distance_from_line = vlen(targetorg - nearest_on_line); + + spreadlimit = (distance_of_attack ? min(1, (vlen(sw_shotorg - nearest_on_line) / distance_of_attack)) : 1); + spreadlimit = + ( + (WEP_CVAR(shockwave, blast_spread_min) * (1 - spreadlimit)) + + + (WEP_CVAR(shockwave, blast_spread_max) * spreadlimit) + ); + + if( + (spreadlimit && (distance_from_line <= spreadlimit)) + && + ((vlen(normalize(targetorg - sw_shotorg) - normalize(attack_endpos - sw_shotorg)) * RAD2DEG) <= 90) + ) + { return bound(0, (distance_from_line / spreadlimit), 1); } + else + { return FALSE; } +} + +float W_Shockwave_Attack_IsVisible( + entity head, + vector nearest_on_line, + vector sw_shotorg, + vector attack_endpos) +{ + vector nearest_to_attacker = head.WarpZone_findradius_nearest; + vector center = (head.origin + (head.mins + head.maxs) * 0.5); + vector corner; + float i; + + // STEP ONE: Check if the nearest point is clear + if(W_Shockwave_Attack_CheckSpread(nearest_to_attacker, nearest_on_line, sw_shotorg, attack_endpos)) + { + WarpZone_TraceLine(sw_shotorg, nearest_to_attacker, MOVE_NOMONSTERS, self); + if(trace_fraction == 1) { return TRUE; } // yes, the nearest point is clear and we can allow the damage + } + + // STEP TWO: Check if shotorg to center point is clear + if(W_Shockwave_Attack_CheckSpread(center, nearest_on_line, sw_shotorg, attack_endpos)) + { + WarpZone_TraceLine(sw_shotorg, center, MOVE_NOMONSTERS, self); + if(trace_fraction == 1) { return TRUE; } // yes, the center point is clear and we can allow the damage + } + + // STEP THREE: Check each corner to see if they are clear + for(i=1; i<=8; ++i) + { + corner = get_corner_position(head, i); + if(W_Shockwave_Attack_CheckSpread(corner, nearest_on_line, sw_shotorg, attack_endpos)) + { + WarpZone_TraceLine(sw_shotorg, corner, MOVE_NOMONSTERS, self); + if(trace_fraction == 1) { return TRUE; } // yes, this corner is clear and we can allow the damage + } + } + + return FALSE; +} + +float W_Shockwave_Attack_CheckHit( + float queue, + entity head, + vector final_force, + float final_damage) +{ + if(!head) { return FALSE; } + float i; + + for(i = 0; i <= queue; ++i) + { + if(shockwave_hit[i] == head) + { + if(vlen(final_force) > vlen(shockwave_hit_force[i])) { shockwave_hit_force[i] = final_force; } + if(final_damage > shockwave_hit_damage[i]) { shockwave_hit_damage[i] = final_damage; } + return FALSE; + } + } + + shockwave_hit[queue] = head; + shockwave_hit_force[queue] = final_force; + shockwave_hit_damage[queue] = final_damage; + return TRUE; +} + +void W_Shockwave_Send(void) +{ + WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte(MSG_BROADCAST, TE_CSQC_SHOCKWAVEPARTICLE); + WriteCoord(MSG_BROADCAST, w_shotorg_x); + WriteCoord(MSG_BROADCAST, w_shotorg_y); + WriteCoord(MSG_BROADCAST, w_shotorg_z); + WriteCoord(MSG_BROADCAST, w_shotdir_x); + WriteCoord(MSG_BROADCAST, w_shotdir_y); + WriteCoord(MSG_BROADCAST, w_shotdir_z); + WriteShort(MSG_BROADCAST, WEP_CVAR(shockwave, blast_distance)); + WriteByte(MSG_BROADCAST, bound(0, WEP_CVAR(shockwave, blast_spread_max), 255)); + WriteByte(MSG_BROADCAST, bound(0, WEP_CVAR(shockwave, blast_spread_min), 255)); + WriteByte(MSG_BROADCAST, num_for_edict(self)); +} + +void W_Shockwave_Attack(void) +{ + // declarations + float multiplier, multiplier_from_accuracy, multiplier_from_distance; + float final_damage; + vector final_force, center, vel; + entity head; + + float i, queue = 0; + + // set up the shot direction + W_SetupShot(self, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, WEP_CVAR(shockwave, blast_damage)); + vector attack_endpos = (w_shotorg + (w_shotdir * WEP_CVAR(shockwave, blast_distance))); + WarpZone_TraceLine(w_shotorg, attack_endpos, MOVE_NOMONSTERS, self); + vector attack_hitpos = trace_endpos; + float distance_to_end = vlen(w_shotorg - attack_endpos); + float distance_to_hit = vlen(w_shotorg - attack_hitpos); + //entity transform = WarpZone_trace_transform; + + // do the firing effect now + W_Shockwave_Send(); + Damage_DamageInfo( + attack_hitpos, + WEP_CVAR(shockwave, blast_splash_damage), + WEP_CVAR(shockwave, blast_splash_edgedamage), + WEP_CVAR(shockwave, blast_splash_radius), + w_shotdir * WEP_CVAR(shockwave, blast_splash_force), + WEP_SHOCKWAVE, + 0, + self + ); + + // splash damage/jumping trace + head = WarpZone_FindRadius( + attack_hitpos, + max( + WEP_CVAR(shockwave, blast_splash_radius), + WEP_CVAR(shockwave, blast_jump_radius) + ), + FALSE + ); + + while(head) + { + if(head.takedamage) + { + float distance_to_head = vlen(attack_hitpos - head.WarpZone_findradius_nearest); + + if((head == self) && (distance_to_head <= WEP_CVAR(shockwave, blast_jump_radius))) + { + // ======================== + // BLAST JUMP CALCULATION + // ======================== + + // calculate importance of distance and accuracy for this attack + multiplier_from_accuracy = (1 - + (distance_to_head ? + min(1, (distance_to_head / WEP_CVAR(shockwave, blast_jump_radius))) + : + 0 + ) + ); + multiplier_from_distance = (1 - + (distance_to_hit ? + min(1, (distance_to_hit / distance_to_end)) + : + 0 + ) + ); + multiplier = + max( + WEP_CVAR(shockwave, blast_jump_multiplier_min), + ( + (multiplier_from_accuracy * WEP_CVAR(shockwave, blast_jump_multiplier_accuracy)) + + + (multiplier_from_distance * WEP_CVAR(shockwave, blast_jump_multiplier_distance)) + ) + ); + + // calculate damage from multiplier: 1 = "highest" damage, 0 = "lowest" edgedamage + final_damage = + ( + (WEP_CVAR(shockwave, blast_jump_damage) * multiplier) + + + (WEP_CVAR(shockwave, blast_jump_edgedamage) * (1 - multiplier)) + ); + + // figure out the direction of force + vel = normalize(combine_to_vector(head.velocity_x, head.velocity_y, 0)); + vel *= + ( + bound(0, (vlen(vel) / autocvar_sv_maxspeed), 1) + * + WEP_CVAR(shockwave, blast_jump_force_velocitybias) + ); + final_force = normalize((CENTER_OR_VIEWOFS(head) - attack_hitpos) + vel); + + // now multiply the direction by force units + final_force *= (WEP_CVAR(shockwave, blast_jump_force) * multiplier); + final_force_z *= WEP_CVAR(shockwave, blast_jump_force_zscale); + + // trigger damage with this calculated info + Damage( + head, + self, + self, + final_damage, + WEP_SHOCKWAVE, + head.origin, + final_force + ); + + #ifdef DEBUG_SHOCKWAVE + print(sprintf( + "SELF HIT: multiplier = %f, damage = %f, force = %f... " + "multiplier_from_accuracy = %f, multiplier_from_distance = %f.\n", + multiplier, + final_damage, + vlen(final_force), + multiplier_from_accuracy, + multiplier_from_distance + )); + #endif + } + else if(distance_to_head <= WEP_CVAR(shockwave, blast_splash_radius)) + { + // ========================== + // BLAST SPLASH CALCULATION + // ========================== + + // calculate importance of distance and accuracy for this attack + multiplier_from_accuracy = (1 - + (distance_to_head ? + min(1, (distance_to_head / WEP_CVAR(shockwave, blast_splash_radius))) + : + 0 + ) + ); + multiplier_from_distance = (1 - + (distance_to_hit ? + min(1, (distance_to_hit / distance_to_end)) + : + 0 + ) + ); + multiplier = + max( + WEP_CVAR(shockwave, blast_splash_multiplier_min), + ( + (multiplier_from_accuracy * WEP_CVAR(shockwave, blast_splash_multiplier_accuracy)) + + + (multiplier_from_distance * WEP_CVAR(shockwave, blast_splash_multiplier_distance)) + ) + ); + + // calculate damage from multiplier: 1 = "highest" damage, 0 = "lowest" edgedamage + final_damage = + ( + (WEP_CVAR(shockwave, blast_splash_damage) * multiplier) + + + (WEP_CVAR(shockwave, blast_splash_edgedamage) * (1 - multiplier)) + ); + + // figure out the direction of force + final_force = (w_shotdir * WEP_CVAR(shockwave, blast_splash_force_forwardbias)); + final_force = normalize(CENTER_OR_VIEWOFS(head) - (attack_hitpos - final_force)); + //te_lightning2(world, attack_hitpos, (attack_hitpos + (final_force * 200))); + + // now multiply the direction by force units + final_force *= (WEP_CVAR(shockwave, blast_splash_force) * multiplier); + final_force_z *= WEP_CVAR(shockwave, blast_force_zscale); + + // queue damage with this calculated info + if(W_Shockwave_Attack_CheckHit(queue, head, final_force, final_damage)) { queue = min(queue + 1, MAX_SHOCKWAVE_HITS); } + + #ifdef DEBUG_SHOCKWAVE + print(sprintf( + "SPLASH HIT: multiplier = %f, damage = %f, force = %f... " + "multiplier_from_accuracy = %f, multiplier_from_distance = %f.\n", + multiplier, + final_damage, + vlen(final_force), + multiplier_from_accuracy, + multiplier_from_distance + )); + #endif + } + } + head = head.chain; + } + + // cone damage trace + head = WarpZone_FindRadius(w_shotorg, WEP_CVAR(shockwave, blast_distance), FALSE); + while(head) + { + if((head != self) && head.takedamage) + { + // ======================== + // BLAST CONE CALCULATION + // ======================== + + // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) + center = CENTER_OR_VIEWOFS(head); + + // find the closest point on the enemy to the center of the attack + float ang; // angle between shotdir and h + float h; // hypotenuse, which is the distance between attacker to head + float a; // adjacent side, which is the distance between attacker and the point on w_shotdir that is closest to head.origin + + h = vlen(center - self.origin); + ang = acos(dotproduct(normalize(center - self.origin), w_shotdir)); + a = h * cos(ang); + // WEAPONTODO: replace with simpler method + + vector nearest_on_line = (w_shotorg + a * w_shotdir); + vector nearest_to_attacker = WarpZoneLib_NearestPointOnBox(center + head.mins, center + head.maxs, nearest_on_line); + + if((vlen(head.WarpZone_findradius_dist) <= WEP_CVAR(shockwave, blast_distance)) + && (W_Shockwave_Attack_IsVisible(head, nearest_on_line, w_shotorg, attack_endpos))) + { + // calculate importance of distance and accuracy for this attack + multiplier_from_accuracy = (1 - + W_Shockwave_Attack_CheckSpread( + nearest_to_attacker, + nearest_on_line, + w_shotorg, + attack_endpos + ) + ); + multiplier_from_distance = (1 - + (distance_to_hit ? + min(1, (vlen(head.WarpZone_findradius_dist) / distance_to_end)) + : + 0 + ) + ); + multiplier = + max( + WEP_CVAR(shockwave, blast_multiplier_min), + ( + (multiplier_from_accuracy * WEP_CVAR(shockwave, blast_multiplier_accuracy)) + + + (multiplier_from_distance * WEP_CVAR(shockwave, blast_multiplier_distance)) + ) + ); + + // calculate damage from multiplier: 1 = "highest" damage, 0 = "lowest" edgedamage + final_damage = + ( + (WEP_CVAR(shockwave, blast_damage) * multiplier) + + + (WEP_CVAR(shockwave, blast_edgedamage) * (1 - multiplier)) + ); + + // figure out the direction of force + final_force = (w_shotdir * WEP_CVAR(shockwave, blast_force_forwardbias)); + final_force = normalize(center - (nearest_on_line - final_force)); + //te_lightning2(world, nearest_on_line, (attack_hitpos + (final_force * 200))); + + // now multiply the direction by force units + final_force *= (WEP_CVAR(shockwave, blast_force) * multiplier); + final_force_z *= WEP_CVAR(shockwave, blast_force_zscale); + + // queue damage with this calculated info + if(W_Shockwave_Attack_CheckHit(queue, head, final_force, final_damage)) { queue = min(queue + 1, MAX_SHOCKWAVE_HITS); } + + #ifdef DEBUG_SHOCKWAVE + print(sprintf( + "BLAST HIT: multiplier = %f, damage = %f, force = %f... " + "multiplier_from_accuracy = %f, multiplier_from_distance = %f.\n", + multiplier, + final_damage, + vlen(final_force), + multiplier_from_accuracy, + multiplier_from_distance + )); + #endif + } + } + head = head.chain; + } + + for(i = 1; i <= queue; ++i) + { + head = shockwave_hit[i-1]; + final_force = shockwave_hit_force[i-1]; + final_damage = shockwave_hit_damage[i-1]; + + Damage( + head, + self, + self, + final_damage, + WEP_SHOCKWAVE, + head.origin, + final_force + ); + + if(accuracy_isgooddamage(self.realowner, head)) + { + print("wtf\n"); + accuracy_add(self.realowner, WEP_SHOCKWAVE, 0, final_damage); + } + + #ifdef DEBUG_SHOCKWAVE + print(sprintf( + "SHOCKWAVE by %s: damage = %f, force = %f.\n", + self.netname, + final_damage, + vlen(final_force) + )); + #endif + + shockwave_hit[i-1] = world; + shockwave_hit_force[i-1] = '0 0 0'; + shockwave_hit_damage[i-1] = 0; + } +} + +float W_Shockwave(float req) +{ + switch(req) + { + case WR_AIM: + { + if(vlen(self.origin - self.enemy.origin) <= WEP_CVAR(shockwave, melee_range)) + { self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE); } + else + { self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE); } + + return TRUE; + } + case WR_THINK: + { + if(self.BUTTON_ATCK) + { + if(time >= self.shockwave_blasttime) // handle refire separately so the secondary can be fired straight after a primary + { + if(weapon_prepareattack(0, WEP_CVAR(shockwave, blast_animtime))) + { + W_Shockwave_Attack(); + self.shockwave_blasttime = time + WEP_CVAR(shockwave, blast_refire) * W_WeaponRateFactor(); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(shockwave, blast_animtime), w_ready); + } + } + } + else if(self.BUTTON_ATCK2) + { + //if(self.clip_load >= 0) // we are not currently reloading + if(!self.crouch) // no crouchmelee please + if(weapon_prepareattack(1, WEP_CVAR(shockwave, melee_refire))) + { + // attempt forcing playback of the anim by switching to another anim (that we never play) here... + weapon_thinkf(WFRAME_FIRE1, 0, W_Shockwave_Melee); + } + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/uziflash.md3"); + precache_model("models/weapons/g_shotgun.md3"); + precache_model("models/weapons/v_shotgun.md3"); + precache_model("models/weapons/h_shotgun.iqm"); + precache_sound("misc/itempickup.wav"); + precache_sound("weapons/lasergun_fire.wav"); + precache_sound("weapons/shotgun_melee.wav"); + SHOCKWAVE_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_CHECKAMMO1: + case WR_CHECKAMMO2: + { + // shockwave has infinite ammo + return TRUE; + } + case WR_CONFIG: + { + SHOCKWAVE_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + return WEAPON_THINKING_WITH_PORTALS; + } + case WR_KILLMESSAGE: + { + if(w_deathtype & HITTYPE_SECONDARY) + return WEAPON_SHOCKWAVE_MURDER_SLAP; + else + return WEAPON_SHOCKWAVE_MURDER; + } + } + return FALSE; +} +#endif +#ifdef CSQC +// WEAPONTODO: add client side settings for these +#define SW_MAXALPHA 0.5 +#define SW_FADETIME 0.4 +#define SW_DISTTOMIN 200 +void Draw_Shockwave() +{ + // fading/removal control + float a = bound(0, (SW_MAXALPHA - ((time - self.sw_time) / SW_FADETIME)), SW_MAXALPHA); + if(a < ALPHA_MIN_VISIBLE) { remove(self); } + + // WEAPONTODO: save this only once when creating the entity + vector sw_color = getcsqcplayercolor(self.sv_entnum); // GetTeamRGB(GetPlayerColor(self.sv_entnum)); + + // WEAPONTODO: trace to find what we actually hit + vector endpos = (self.sw_shotorg + (self.sw_shotdir * self.sw_distance)); + + vectorvectors(self.sw_shotdir); + vector right = v_right; // save this for when we do makevectors later + vector up = v_up; // save this for when we do makevectors later + + // WEAPONTODO: combine and simplify these calculations + vector min_end = ((self.sw_shotorg + (self.sw_shotdir * SW_DISTTOMIN)) + (up * self.sw_spread_min)); + vector max_end = (endpos + (up * self.sw_spread_max)); + float spread_to_min = vlen(normalize(min_end - self.sw_shotorg) - self.sw_shotdir); + float spread_to_max = vlen(normalize(max_end - min_end) - self.sw_shotdir); + + vector first_min_end = '0 0 0', prev_min_end = '0 0 0', new_min_end = '0 0 0'; + vector first_max_end = '0 0 0', prev_max_end = '0 0 0', new_max_end = '0 0 0'; + float new_max_dist, new_min_dist; + + vector deviation, angle = '0 0 0'; + float counter, divisions = 20; + for(counter = 0; counter < divisions; ++counter) + { + // perfect circle effect lines + makevectors('0 360 0' * (0.75 + (counter - 0.5) / divisions)); + angle_y = v_forward_x; + angle_z = v_forward_y; + + // first do the spread_to_min effect + deviation = angle * spread_to_min; + deviation = ((self.sw_shotdir + (right * deviation_y) + (up * deviation_z))); + new_min_dist = SW_DISTTOMIN; + new_min_end = (self.sw_shotorg + (deviation * new_min_dist)); + //te_lightning2(world, new_min_end, self.sw_shotorg); + + // then calculate spread_to_max effect + deviation = angle * spread_to_max; + deviation = ((self.sw_shotdir + (right * deviation_y) + (up * deviation_z))); + new_max_dist = vlen(new_min_end - endpos); + new_max_end = (new_min_end + (deviation * new_max_dist)); + //te_lightning2(world, new_end, prev_min_end); + + + if(counter == 0) + { + first_min_end = new_min_end; + first_max_end = new_max_end; + } + + if(counter >= 1) + { + // draw from shot origin to min spread radius + R_BeginPolygon("", DRAWFLAG_NORMAL); + R_PolygonVertex(prev_min_end, '0 0 0', sw_color, a); + R_PolygonVertex(new_min_end, '0 0 0', sw_color, a); + R_PolygonVertex(self.sw_shotorg, '0 0 0', sw_color, a); + R_EndPolygon(); + + // draw from min spread radius to max spread radius + R_BeginPolygon("", DRAWFLAG_NORMAL); + R_PolygonVertex(new_min_end, '0 0 0', sw_color, a); + R_PolygonVertex(prev_min_end, '0 0 0', sw_color, a); + R_PolygonVertex(prev_max_end, '0 0 0', sw_color, a); + R_PolygonVertex(new_max_end, '0 0 0', sw_color, a); + R_EndPolygon(); + } + + prev_min_end = new_min_end; + prev_max_end = new_max_end; + + // last division only + if((counter + 1) == divisions) + { + // draw from shot origin to min spread radius + R_BeginPolygon("", DRAWFLAG_NORMAL); + R_PolygonVertex(prev_min_end, '0 0 0', sw_color, a); + R_PolygonVertex(first_min_end, '0 0 0', sw_color, a); + R_PolygonVertex(self.sw_shotorg, '0 0 0', sw_color, a); + R_EndPolygon(); + + // draw from min spread radius to max spread radius + R_BeginPolygon("", DRAWFLAG_NORMAL); + R_PolygonVertex(first_min_end, '0 0 0', sw_color, a); + R_PolygonVertex(prev_min_end, '0 0 0', sw_color, a); + R_PolygonVertex(prev_max_end, '0 0 0', sw_color, a); + R_PolygonVertex(first_max_end, '0 0 0', sw_color, a); + R_EndPolygon(); + } + } +} + +void Net_ReadShockwaveParticle(void) +{ + entity shockwave; + shockwave = spawn(); + shockwave.draw = Draw_Shockwave; + + shockwave.sw_shotorg_x = ReadCoord(); shockwave.sw_shotorg_y = ReadCoord(); shockwave.sw_shotorg_z = ReadCoord(); + shockwave.sw_shotdir_x = ReadCoord(); shockwave.sw_shotdir_y = ReadCoord(); shockwave.sw_shotdir_z = ReadCoord(); + + shockwave.sw_distance = ReadShort(); + shockwave.sw_spread_max = ReadByte(); + shockwave.sw_spread_min = ReadByte(); + + shockwave.sv_entnum = ReadByte(); + + shockwave.sw_time = time; +} + +float W_Shockwave(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + // handled by Net_ReadShockwaveParticle + //vector org2; + //org2 = w_org + w_backoff * 2; + //pointparticles(particleeffectnum("laser_impact"), org2, w_backoff * 1000, 1); + return FALSE; + } + case WR_INIT: + { + //precache_sound("weapons/ric1.wav"); + //precache_sound("weapons/ric2.wav"); + //precache_sound("weapons/ric3.wav"); + return FALSE; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_shotgun.qc b/qcsrc/common/weapons/w_shotgun.qc new file mode 100644 index 0000000000..9227cdab4e --- /dev/null +++ b/qcsrc/common/weapons/w_shotgun.qc @@ -0,0 +1,391 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ SHOTGUN, +/* function */ W_Shotgun, +/* ammotype */ ammo_shells, +/* impulse */ 2, +/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN, +/* rating */ BOT_PICKUP_RATING_LOW, +/* color */ '0.5 0.25 0', +/* modelname */ "shotgun", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairshotgun 0.65", +/* wepimg */ "weaponshotgun", +/* refname */ "shotgun", +/* wepname */ _("Shotgun") +); + +#define SHOTGUN_SETTINGS(w_cvar,w_prop) SHOTGUN_SETTINGS_LIST(w_cvar, w_prop, SHOTGUN, shotgun) +#define SHOTGUN_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, PRI, ammo) \ + w_cvar(id, sn, BOTH, animtime) \ + w_cvar(id, sn, BOTH, refire) \ + w_cvar(id, sn, PRI, bullets) \ + w_cvar(id, sn, BOTH, damage) \ + w_cvar(id, sn, BOTH, force) \ + w_cvar(id, sn, PRI, solidpenetration) \ + w_cvar(id, sn, PRI, spread) \ + w_cvar(id, sn, NONE, secondary) \ + w_cvar(id, sn, SEC, melee_time) \ + w_cvar(id, sn, SEC, melee_no_doubleslap) \ + w_cvar(id, sn, SEC, melee_traces) \ + w_cvar(id, sn, SEC, melee_swing_up) \ + w_cvar(id, sn, SEC, melee_swing_side) \ + w_cvar(id, sn, SEC, melee_nonplayerdamage) \ + w_cvar(id, sn, SEC, melee_multihit) \ + w_cvar(id, sn, SEC, melee_delay) \ + w_cvar(id, sn, SEC, melee_range) \ + w_cvar(id, sn, SEC, alt_animtime) \ + w_cvar(id, sn, SEC, alt_refire) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +SHOTGUN_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_shotgun(void) { weapon_defaultspawnfunc(WEP_SHOTGUN); } + +void W_Shotgun_Attack(float isprimary) +{ + float sc; + entity flash; + + W_DecreaseAmmo(WEP_CVAR_PRI(shotgun, ammo)); + + W_SetupShot(self, TRUE, 5, "weapons/shotgun_fire.wav", ((isprimary) ? CH_WEAPON_A : CH_WEAPON_SINGLE), WEP_CVAR_PRI(shotgun, damage) * WEP_CVAR_PRI(shotgun, bullets)); + for(sc = 0;sc < WEP_CVAR_PRI(shotgun, bullets);sc = sc + 1) + fireBullet(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, 0); + + pointparticles(particleeffectnum("shotgun_muzzleflash"), w_shotorg, w_shotdir * 1000, WEP_CVAR_PRI(shotgun, ammo)); + + // casing code + if(autocvar_g_casings >= 1) + for(sc = 0;sc < WEP_CVAR_PRI(shotgun, ammo);sc = sc + 1) + SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 30) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 1, self); + + // muzzle flash for 1st person view + flash = spawn(); + setmodel(flash, "models/uziflash.md3"); // precision set below + flash.think = SUB_Remove; + flash.nextthink = time + 0.06; + flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; + W_AttachToShotorg(flash, '5 0 0'); +} + +.float swing_prev; +.entity swing_alreadyhit; +void W_Shotgun_Melee_Think(void) +{ + // declarations + float i, f, swing, swing_factor, swing_damage, meleetime, is_player; + entity target_victim; + vector targpos; + + if(!self.cnt) // set start time of melee + { + self.cnt = time; + W_PlayStrengthSound(self.realowner); + } + + makevectors(self.realowner.v_angle); // update values for v_* vectors + + // calculate swing percentage based on time + meleetime = WEP_CVAR_SEC(shotgun, melee_time) * W_WeaponRateFactor(); + swing = bound(0, (self.cnt + meleetime - time) / meleetime, 10); + f = ((1 - swing) * WEP_CVAR_SEC(shotgun, melee_traces)); + + // check to see if we can still continue, otherwise give up now + if((self.realowner.deadflag != DEAD_NO) && WEP_CVAR_SEC(shotgun, melee_no_doubleslap)) + { + remove(self); + return; + } + + // if okay, perform the traces needed for this frame + for(i=self.swing_prev; i < f; ++i) + { + swing_factor = ((1 - (i / WEP_CVAR_SEC(shotgun, melee_traces))) * 2 - 1); + + targpos = (self.realowner.origin + self.realowner.view_ofs + + (v_forward * WEP_CVAR_SEC(shotgun, melee_range)) + + (v_up * swing_factor * WEP_CVAR_SEC(shotgun, melee_swing_up)) + + (v_right * swing_factor * WEP_CVAR_SEC(shotgun, melee_swing_side))); + + WarpZone_traceline_antilag(self, self.realowner.origin + self.realowner.view_ofs, targpos, FALSE, self, ANTILAG_LATENCY(self.realowner)); + + // draw lightning beams for debugging + //te_lightning2(world, targpos, self.realowner.origin + self.realowner.view_ofs + v_forward * 5 - v_up * 5); + //te_customflash(targpos, 40, 2, '1 1 1'); + + is_player = (IS_PLAYER(trace_ent) || trace_ent.classname == "body" || (trace_ent.flags & FL_MONSTER)); + + if((trace_fraction < 1) // if trace is good, apply the damage and remove self + && (trace_ent.takedamage == DAMAGE_AIM) + && (trace_ent != self.swing_alreadyhit) + && (is_player || WEP_CVAR_SEC(shotgun, melee_nonplayerdamage))) + { + target_victim = trace_ent; // so it persists through other calls + + if(is_player) // this allows us to be able to nerf the non-player damage done in e.g. assault or onslaught. + swing_damage = (WEP_CVAR_SEC(shotgun, damage) * min(1, swing_factor + 1)); + else + swing_damage = (WEP_CVAR_SEC(shotgun, melee_nonplayerdamage) * min(1, swing_factor + 1)); + + //print(strcat(self.realowner.netname, " hitting ", target_victim.netname, " with ", strcat(ftos(swing_damage), " damage (factor: ", ftos(swing_factor), ") at "), ftos(time), " seconds.\n")); + + Damage(target_victim, self.realowner, self.realowner, + swing_damage, WEP_SHOTGUN | HITTYPE_SECONDARY, + self.realowner.origin + self.realowner.view_ofs, + v_forward * WEP_CVAR_SEC(shotgun, force)); + + if(accuracy_isgooddamage(self.realowner, target_victim)) { accuracy_add(self.realowner, WEP_SHOTGUN, 0, swing_damage); } + + // draw large red flash for debugging + //te_customflash(targpos, 200, 2, '15 0 0'); + + if(WEP_CVAR_SEC(shotgun, melee_multihit)) // allow multiple hits with one swing, but not against the same player twice. + { + self.swing_alreadyhit = target_victim; + continue; // move along to next trace + } + else + { + remove(self); + return; + } + } + } + + if(time >= self.cnt + meleetime) + { + // melee is finished + remove(self); + return; + } + else + { + // set up next frame + self.swing_prev = i; + self.nextthink = time; + } +} + +void W_Shotgun_Attack2(void) +{ + sound(self, CH_WEAPON_A, "weapons/shotgun_melee.wav", VOL_BASE, ATTEN_NORM); + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(shotgun, animtime), w_ready); + + entity meleetemp; + meleetemp = spawn(); + meleetemp.realowner = self; + meleetemp.think = W_Shotgun_Melee_Think; + meleetemp.nextthink = time + WEP_CVAR_SEC(shotgun, melee_delay) * W_WeaponRateFactor(); + W_SetupShot_Range(self, TRUE, 0, "", 0, WEP_CVAR_SEC(shotgun, damage), WEP_CVAR_SEC(shotgun, melee_range)); +} + +// alternate secondary weapon frames +void W_Shotgun_Attack3_Frame2() +{ + if (!WEP_ACTION(self.weapon, WR_CHECKAMMO2)) + if (!(self.items & IT_UNLIMITED_WEAPON_AMMO)) + { + W_SwitchWeapon_Force(self, w_getbestweapon(self)); + w_ready(); + return; + } + + sound(self, CH_WEAPON_SINGLE, "misc/null.wav", VOL_BASE, ATTN_NORM); // kill previous sound + W_Shotgun_Attack(TRUE); // actually is secondary, but we trick the last shot into playing full reload sound + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), w_ready); +} +void W_Shotgun_Attack3_Frame1() +{ + if (!WEP_ACTION(self.weapon, WR_CHECKAMMO2)) + if (!(self.items & IT_UNLIMITED_WEAPON_AMMO)) + { + W_SwitchWeapon_Force(self, w_getbestweapon(self)); + w_ready(); + return; + } + + W_Shotgun_Attack(FALSE); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame2); +} + +.float shotgun_primarytime; + +float W_Shotgun(float req) +{ + float ammo_amount; + switch(req) + { + case WR_AIM: + { + if(vlen(self.origin-self.enemy.origin) <= WEP_CVAR_SEC(shotgun, melee_range)) + self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE); + else + self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE); + + return TRUE; + } + case WR_THINK: + { + if(WEP_CVAR(shotgun, reload_ammo) && self.clip_load < WEP_CVAR_PRI(shotgun, ammo)) // forced reload + { + // don't force reload an empty shotgun if its melee attack is active + if(WEP_CVAR(shotgun, secondary) < 2) + WEP_ACTION(self.weapon, WR_RELOAD); + } + else + { + if(self.BUTTON_ATCK) + { + if(time >= self.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary + { + if(weapon_prepareattack(0, WEP_CVAR_PRI(shotgun, animtime))) + { + W_Shotgun_Attack(TRUE); + self.shotgun_primarytime = time + WEP_CVAR_PRI(shotgun, refire) * W_WeaponRateFactor(); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(shotgun, animtime), w_ready); + } + } + } + else if(self.BUTTON_ATCK2 && WEP_CVAR(shotgun, secondary) == 2) + { + if(time >= self.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary + { + if(weapon_prepareattack(0, WEP_CVAR_SEC(shotgun, alt_animtime))) + { + W_Shotgun_Attack(FALSE); + self.shotgun_primarytime = time + WEP_CVAR_SEC(shotgun, alt_refire) * W_WeaponRateFactor(); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame1); + } + } + } + } + if(self.clip_load >= 0) // we are not currently reloading + if(!self.crouch) // no crouchmelee please + if(WEP_CVAR(shotgun, secondary) == 1) + if((self.BUTTON_ATCK && self.WEP_AMMO(SHOTGUN) <= 0 && !(self.items & IT_UNLIMITED_WEAPON_AMMO)) || self.BUTTON_ATCK2) + if(weapon_prepareattack(1, WEP_CVAR_SEC(shotgun, refire))) + { + // attempt forcing playback of the anim by switching to another anim (that we never play) here... + weapon_thinkf(WFRAME_FIRE1, 0, W_Shotgun_Attack2); + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/uziflash.md3"); + precache_model("models/weapons/g_shotgun.md3"); + precache_model("models/weapons/v_shotgun.md3"); + precache_model("models/weapons/h_shotgun.iqm"); + precache_sound("misc/itempickup.wav"); + precache_sound("weapons/shotgun_fire.wav"); + precache_sound("weapons/shotgun_melee.wav"); + SHOTGUN_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_SETUP: + { + self.ammo_field = ammo_none; + return TRUE; + } + case WR_CHECKAMMO1: + { + ammo_amount = self.WEP_AMMO(SHOTGUN) >= WEP_CVAR_PRI(shotgun, ammo); + ammo_amount += self.(weapon_load[WEP_SHOTGUN]) >= WEP_CVAR_PRI(shotgun, ammo); + return ammo_amount; + } + case WR_CHECKAMMO2: + { + if(IS_BOT_CLIENT(self)) + if(vlen(self.origin-self.enemy.origin) > WEP_CVAR_SEC(shotgun, melee_range)) + return FALSE; // bots cannot use secondary out of range (fixes constant melee when out of ammo) + switch(WEP_CVAR(shotgun, secondary)) + { + case 1: return TRUE; // melee does not use ammo + case 2: // secondary triple shot + { + ammo_amount = self.WEP_AMMO(SHOTGUN) >= WEP_CVAR_PRI(shotgun, ammo); + ammo_amount += self.(weapon_load[WEP_SHOTGUN]) >= WEP_CVAR_PRI(shotgun, ammo); + return ammo_amount; + } + default: return FALSE; // secondary unavailable + } + } + case WR_CONFIG: + { + SHOTGUN_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_RELOAD: + { + W_Reload(WEP_CVAR_PRI(shotgun, ammo), "weapons/reload.wav"); // WEAPONTODO + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + return WEAPON_THINKING_WITH_PORTALS; + } + case WR_KILLMESSAGE: + { + if(w_deathtype & HITTYPE_SECONDARY) + return WEAPON_SHOTGUN_MURDER_SLAP; + else + return WEAPON_SHOTGUN_MURDER; + } + } + return FALSE; +} +#endif +#ifdef CSQC +.float prevric; +float W_Shotgun(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 2; + pointparticles(particleeffectnum("shotgun_impact"), org2, w_backoff * 1000, 1); + if(!w_issilent && time - self.prevric > 0.25) + { + if(w_random < 0.0165) + sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTEN_NORM); + else if(w_random < 0.033) + sound(self, CH_SHOTS, "weapons/ric2.wav", VOL_BASE, ATTEN_NORM); + else if(w_random < 0.05) + sound(self, CH_SHOTS, "weapons/ric3.wav", VOL_BASE, ATTEN_NORM); + self.prevric = time; + } + + return TRUE; + } + case WR_INIT: + { + precache_sound("weapons/ric1.wav"); + precache_sound("weapons/ric2.wav"); + precache_sound("weapons/ric3.wav"); + return TRUE; + } + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_tuba.qc b/qcsrc/common/weapons/w_tuba.qc new file mode 100644 index 0000000000..5b17893682 --- /dev/null +++ b/qcsrc/common/weapons/w_tuba.qc @@ -0,0 +1,516 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ TUBA, +/* function */ W_Tuba, +/* ammotype */ ammo_none, +/* impulse */ 1, +/* flags */ WEP_FLAG_HIDDEN | WEP_TYPE_SPLASH, +/* rating */ BOT_PICKUP_RATING_MID, +/* color */ '0 1 0', +/* modelname */ "tuba", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairtuba", +/* wepimg */ "weapontuba", +/* refname */ "tuba", +/* xgettext:no-c-format */ +/* wepname */ _("@!#%'n Tuba") +); + +#define TUBA_SETTINGS(w_cvar,w_prop) TUBA_SETTINGS_LIST(w_cvar, w_prop, TUBA, tuba) +#define TUBA_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, NONE, animtime) \ + w_cvar(id, sn, NONE, attenuation) \ + w_cvar(id, sn, NONE, damage) \ + w_cvar(id, sn, NONE, edgedamage) \ + w_cvar(id, sn, NONE, fadetime) \ + w_cvar(id, sn, NONE, force) \ + w_cvar(id, sn, NONE, pitchstep) \ + w_cvar(id, sn, NONE, radius) \ + w_cvar(id, sn, NONE, refire) \ + w_cvar(id, sn, NONE, volume) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +TUBA_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +float W_Tuba_MarkClientOnlyFieldsAsUsed() { + // These variables are only used by client/tuba.qc. TODO: move client/tuba.qc code here. + return WEP_CVAR(tuba, fadetime) + WEP_CVAR(tuba, pitchstep) + WEP_CVAR(tuba, volume); +} + +.entity tuba_note; +.float tuba_smoketime; +.float tuba_instrument; + +#define MAX_TUBANOTES 32 +.float tuba_lastnotes_last; +.float tuba_lastnotes_cnt; // over +.vector tuba_lastnotes[MAX_TUBANOTES]; +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_tuba(void) { weapon_defaultspawnfunc(WEP_TUBA); } + +float W_Tuba_HasPlayed(entity pl, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo) +{ + float i, j, mmin, mmax, nolength; + float n = tokenize_console(melody); + if(n > pl.tuba_lastnotes_cnt) + return FALSE; + float pitchshift = 0; + + if(instrument >= 0) + if(pl.tuba_instrument != instrument) + return FALSE; + + // verify notes... + nolength = FALSE; + for(i = 0; i < n; ++i) + { + vector v = pl.(tuba_lastnotes[mod(pl.tuba_lastnotes_last - i + MAX_TUBANOTES, MAX_TUBANOTES)]); + float ai = stof(argv(n - i - 1)); + float np = floor(ai); + if(ai == np) + nolength = TRUE; + // n counts the last played notes BACKWARDS + // _x is start + // _y is end + // _z is note pitch + if(ignorepitch && i == 0) + { + pitchshift = np - v_z; + } + else + { + if(v_z + pitchshift != np) + return FALSE; + } + } + + // now we know the right NOTES were played + if(!nolength) + { + // verify rhythm... + float ti = 0; + if(maxtempo > 0) + mmin = 240 / maxtempo; // 60 = "0.25 means 1 sec", at 120 0.5 means 1 sec, at 240 1 means 1 sec + else + mmin = 0; + if(mintempo > 0) + mmax = 240 / mintempo; // 60 = "0.25 means 1 sec", at 120 0.5 means 1 sec, at 240 1 means 1 sec + else + mmax = 240; // you won't try THAT hard... (tempo 1) + //printf("initial tempo rules: %f %f\n", mmin, mmax); + + for(i = 0; i < n; ++i) + { + vector vi = pl.(tuba_lastnotes[mod(pl.tuba_lastnotes_last - i + MAX_TUBANOTES, MAX_TUBANOTES)]); + float ai = stof(argv(n - i - 1)); + ti -= 1 / (ai - floor(ai)); + float tj = ti; + for(j = i+1; j < n; ++j) + { + vector vj = pl.(tuba_lastnotes[mod(pl.tuba_lastnotes_last - j + MAX_TUBANOTES, MAX_TUBANOTES)]); + float aj = stof(argv(n - j - 1)); + tj -= (aj - floor(aj)); + + // note i should be at m*ti+b + // note j should be at m*tj+b + // so: + // we have a LINE l, so that + // vi_x <= l(ti) <= vi_y + // vj_x <= l(tj) <= vj_y + // what is m? + + // vi_x <= vi_y <= vj_x <= vj_y + // ti <= tj + //printf("first note: %f to %f, should be %f\n", vi_x, vi_y, ti); + //printf("second note: %f to %f, should be %f\n", vj_x, vj_y, tj); + //printf("m1 = %f\n", (vi_x - vj_y) / (ti - tj)); + //printf("m2 = %f\n", (vi_y - vj_x) / (ti - tj)); + mmin = max(mmin, (vi_x - vj_y) / (ti - tj)); // lower bound + mmax = min(mmax, (vi_y - vj_x) / (ti - tj)); // upper bound + } + } + + if(mmin > mmax) // rhythm fail + return FALSE; + } + + pl.tuba_lastnotes_cnt = 0; + + return TRUE; +} + +void W_Tuba_NoteOff(void) +{ + // we have a note: + // on: self.spawnshieldtime + // off: time + // note: self.cnt + if(self.owner.tuba_note == self) + { + self.owner.tuba_lastnotes_last = mod(self.owner.tuba_lastnotes_last + 1, MAX_TUBANOTES); + self.owner.(tuba_lastnotes[self.owner.tuba_lastnotes_last]) = eX * self.spawnshieldtime + eY * time + eZ * self.cnt; + self.owner.tuba_note = world; + self.owner.tuba_lastnotes_cnt = bound(0, self.owner.tuba_lastnotes_cnt + 1, MAX_TUBANOTES); + + string s; + s = trigger_magicear_processmessage_forallears(self.owner, 0, world, string_null); + if(s != "") + { + // simulate a server message + switch(self.tuba_instrument) + { + default: + case 0: // Tuba + bprint(strcat("\{1}\{13}* ^3", self.owner.netname, "^3 played on the @!#%'n Tuba: ^7", s, "\n")); + break; + case 1: + bprint(strcat("\{1}\{13}* ^3", self.owner.netname, "^3 played on the @!#%'n Accordeon: ^7", s, "\n")); + break; + case 2: + bprint(strcat("\{1}\{13}* ^3", self.owner.netname, "^3 played on the @!#%'n Klein Bottle: ^7", s, "\n")); + break; + } + } + } + remove(self); +} + +float W_Tuba_GetNote(entity pl, float hittype) +{ + float note; + float movestate; + movestate = 5; + if(pl.movement_x < 0) movestate -= 3; + if(pl.movement_x > 0) movestate += 3; + if(pl.movement_y < 0) movestate -= 1; + if(pl.movement_y > 0) movestate += 1; +#ifdef GMQCC + note = 0; +#endif + switch(movestate) + { + // layout: originally I wanted + // eb e e#=f + // B c d + // Gb G G# + // but then you only use forward and right key. So to make things more + // interesting, I swapped B with e#. Har har har... + // eb e B + // f=e# c d + // Gb G G# + case 1: note = -6; break; // Gb + case 2: note = -5; break; // G + case 3: note = -4; break; // G# + case 4: note = +5; break; // e# + default: + case 5: note = 0; break; // c + case 6: note = +2; break; // d + case 7: note = +3; break; // eb + case 8: note = +4; break; // e + case 9: note = -1; break; // B + } + if(pl.BUTTON_CROUCH) + note -= 12; + if(pl.BUTTON_JUMP) + note += 12; + if(hittype & HITTYPE_SECONDARY) + note += 7; + + // we support two kinds of tubas, those tuned in Eb and those tuned in C + // kind of tuba currently is player slot number, or team number if in + // teamplay + // that way, holes in the range of notes are "plugged" + if(teamplay) + { + if(pl.team == NUM_TEAM_2 || pl.team == NUM_TEAM_4) + note += 3; + } + else + { + if(pl.clientcolors & 1) + note += 3; + } + + // total range of notes: + // 0 + // *** ** **** + // *** ** **** + // *** ** **** + // *** ** **** + // *** ********************* **** + // -18.........................+12 + // *** ********************* **** + // -18............................+15 + // with jump: ... +24 + // ... +27 + return note; +} + +float W_Tuba_NoteSendEntity(entity to, float sf) +{ + float f; + + msg_entity = to; + if(!sound_allowed(MSG_ONE, self.realowner)) + return FALSE; + + WriteByte(MSG_ENTITY, ENT_CLIENT_TUBANOTE); + WriteByte(MSG_ENTITY, sf); + if(sf & 1) + { + WriteChar(MSG_ENTITY, self.cnt); + f = 0; + if(self.realowner != to) + f |= 1; + f |= 2 * self.tuba_instrument; + WriteByte(MSG_ENTITY, f); + } + if(sf & 2) + { + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + } + return TRUE; +} + +void W_Tuba_NoteThink(void) +{ + float dist_mult; + float vol0, vol1; + vector dir0, dir1; + vector v; + entity e; + if(time > self.teleport_time) + { + W_Tuba_NoteOff(); + return; + } + self.nextthink = time; + dist_mult = WEP_CVAR(tuba, attenuation) / autocvar_snd_soundradius; + FOR_EACH_REALCLIENT(e) + if(e != self.realowner) + { + v = self.origin - (e.origin + e.view_ofs); + vol0 = max(0, 1 - vlen(v) * dist_mult); + dir0 = normalize(v); + v = self.realowner.origin - (e.origin + e.view_ofs); + vol1 = max(0, 1 - vlen(v) * dist_mult); + dir1 = normalize(v); + if(fabs(vol0 - vol1) > 0.005) // 0.5 percent change in volume + { + setorigin(self, self.realowner.origin); + self.SendFlags |= 2; + break; + } + if(dir0 * dir1 < 0.9994) // 2 degrees change in angle + { + setorigin(self, self.realowner.origin); + self.SendFlags |= 2; + break; + } + } +} + +void W_Tuba_NoteOn(float hittype) +{ + vector o; + float n; + + W_SetupShot(self, FALSE, 2, "", 0, WEP_CVAR(tuba, damage)); + + n = W_Tuba_GetNote(self, hittype); + + hittype = 0; + if(self.tuba_instrument & 1) + hittype |= HITTYPE_SECONDARY; + if(self.tuba_instrument & 2) + hittype |= HITTYPE_BOUNCE; + + if(self.tuba_note) + { + if(self.tuba_note.cnt != n || self.tuba_note.tuba_instrument != self.tuba_instrument) + { + entity oldself = self; + self = self.tuba_note; + W_Tuba_NoteOff(); + self = oldself; + } + } + + if(!self.tuba_note) + { + self.tuba_note = spawn(); + self.tuba_note.owner = self.tuba_note.realowner = self; + self.tuba_note.cnt = n; + self.tuba_note.tuba_instrument = self.tuba_instrument; + self.tuba_note.think = W_Tuba_NoteThink; + self.tuba_note.nextthink = time; + self.tuba_note.spawnshieldtime = time; + Net_LinkEntity(self.tuba_note, FALSE, 0, W_Tuba_NoteSendEntity); + } + + self.tuba_note.teleport_time = time + WEP_CVAR(tuba, refire) * 2 * W_WeaponRateFactor(); // so it can get prolonged safely + + //sound(self, c, TUBA_NOTE(n), bound(0, VOL_BASE * cvar("g_balance_tuba_volume"), 1), autocvar_g_balance_tuba_attenuation); + RadiusDamage(self, self, WEP_CVAR(tuba, damage), WEP_CVAR(tuba, edgedamage), WEP_CVAR(tuba, radius), world, world, WEP_CVAR(tuba, force), hittype | WEP_TUBA, world); + + o = gettaginfo(self.exteriorweaponentity, 0); + if(time > self.tuba_smoketime) + { + pointparticles(particleeffectnum("smoke_ring"), o + v_up * 45 + v_right * -6 + v_forward * 8, v_up * 100, 1); + self.tuba_smoketime = time + 0.25; + } +} + +float W_Tuba(float req) +{ + switch(req) + { + case WR_AIM: + { + // bots cannot play the Tuba well yet + // I think they should start with the recorder first + if(vlen(self.origin - self.enemy.origin) < WEP_CVAR(tuba, radius)) + { + if(random() > 0.5) + self.BUTTON_ATCK = 1; + else + self.BUTTON_ATCK2 = 1; + } + + return TRUE; + } + case WR_THINK: + { + if(self.BUTTON_ATCK) + if(weapon_prepareattack(0, WEP_CVAR(tuba, refire))) + { + W_Tuba_NoteOn(0); + //weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_tuba_animtime, w_ready); + weapon_thinkf(WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready); + } + if(self.BUTTON_ATCK2) + if(weapon_prepareattack(1, WEP_CVAR(tuba, refire))) + { + W_Tuba_NoteOn(HITTYPE_SECONDARY); + //weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_tuba_animtime, w_ready); + weapon_thinkf(WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready); + } + if(self.tuba_note) + { + if(!self.BUTTON_ATCK && !self.BUTTON_ATCK2) + { + entity oldself = self; + self = self.tuba_note; + W_Tuba_NoteOff(); + self = oldself; + } + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/weapons/g_tuba.md3"); + precache_model("models/weapons/v_tuba.md3"); + precache_model("models/weapons/h_tuba.iqm"); + precache_model("models/weapons/v_akordeon.md3"); + precache_model("models/weapons/h_akordeon.iqm"); + precache_model("models/weapons/v_kleinbottle.md3"); + precache_model("models/weapons/h_kleinbottle.iqm"); + TUBA_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_SETUP: + { + self.ammo_field = ammo_none; + self.tuba_instrument = 0; + return TRUE; + } + case WR_RELOAD: + { + // switch to alternate instruments :) + if(self.weaponentity.state == WS_READY) + { + switch(self.tuba_instrument) + { + case 0: + self.tuba_instrument = 1; + self.weaponname = "akordeon"; + break; + case 1: + self.tuba_instrument = 2; + self.weaponname = "kleinbottle"; + break; + case 2: + self.tuba_instrument = 0; + self.weaponname = "tuba"; + break; + } + W_SetupShot(self, FALSE, 0, "", 0, 0); + pointparticles(particleeffectnum("teleport"), w_shotorg, '0 0 0', 1); + self.weaponentity.state = WS_INUSE; + weapon_thinkf(WFRAME_RELOAD, 0.5, w_ready); + } + + return TRUE; + } + case WR_CHECKAMMO1: + case WR_CHECKAMMO2: + { + return TRUE; // tuba has infinite ammo + } + case WR_CONFIG: + { + TUBA_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + if(w_deathtype & HITTYPE_BOUNCE) + return WEAPON_KLEINBOTTLE_SUICIDE; + else if(w_deathtype & HITTYPE_SECONDARY) + return WEAPON_ACCORDEON_SUICIDE; + else + return WEAPON_TUBA_SUICIDE; + } + case WR_KILLMESSAGE: + { + if(w_deathtype & HITTYPE_BOUNCE) + return WEAPON_KLEINBOTTLE_MURDER; + else if(w_deathtype & HITTYPE_SECONDARY) + return WEAPON_ACCORDEON_MURDER; + else + return WEAPON_TUBA_MURDER; + } + } + return FALSE; +} +#endif +#ifdef CSQC +float W_Tuba(float req) +{ + // nothing to do here; particles of tuba are handled differently + // WEAPONTODO + + switch(req) + { + case WR_ZOOMRETICLE: + { + // no weapon specific image for this weapon + return FALSE; + } + } + + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_vaporizer.qc b/qcsrc/common/weapons/w_vaporizer.qc new file mode 100644 index 0000000000..90ea15d5c4 --- /dev/null +++ b/qcsrc/common/weapons/w_vaporizer.qc @@ -0,0 +1,302 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ VAPORIZER, +/* function */ W_Vaporizer, +/* ammotype */ ammo_cells, +/* impulse */ 7, +/* flags */ WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_FLAG_SUPERWEAPON | WEP_TYPE_HITSCAN, +/* rating */ BOT_PICKUP_RATING_HIGH, +/* color */ '0.5 1 1', +/* modelname */ "minstanex", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairminstanex 0.4", +/* wepimg */ "weaponminstanex", +/* refname */ "vaporizer", +/* wepname */ _("Vaporizer") +); + +#define VAPORIZER_SETTINGS(w_cvar,w_prop) VAPORIZER_SETTINGS_LIST(w_cvar, w_prop, VAPORIZER, vaporizer) +#define VAPORIZER_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, PRI, ammo) \ + w_cvar(id, sn, PRI, animtime) \ + w_cvar(id, sn, PRI, refire) \ + w_cvar(id, sn, SEC, ammo) \ + w_cvar(id, sn, SEC, animtime) \ + w_cvar(id, sn, SEC, damage) \ + w_cvar(id, sn, SEC, delay) \ + w_cvar(id, sn, SEC, edgedamage) \ + w_cvar(id, sn, SEC, force) \ + w_cvar(id, sn, SEC, lifetime) \ + w_cvar(id, sn, SEC, radius) \ + w_cvar(id, sn, SEC, refire) \ + w_cvar(id, sn, SEC, shotangle) \ + w_cvar(id, sn, SEC, speed) \ + w_cvar(id, sn, SEC, spread) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +VAPORIZER_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +.float vaporizer_lasthit; +.float jump_interval; +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_vaporizer(void) { weapon_defaultspawnfunc(WEP_VAPORIZER); } +void spawnfunc_weapon_minstanex(void) { spawnfunc_weapon_vaporizer(); } + +void W_Vaporizer_Attack(void) +{ + float flying; + flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last + + W_SetupShot(self, TRUE, 0, "", CH_WEAPON_A, 10000); + // handle sound separately so we can change the volume + // added bonus: no longer plays the strength sound (strength gives no bonus to instakill anyway) + sound (self, CH_WEAPON_A, "weapons/minstanexfire.wav", VOL_BASE * 0.8, ATTEN_NORM); + + yoda = 0; + damage_goodhits = 0; + FireRailgunBullet(w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, 10000, 800, 0, 0, 0, 0, WEP_VAPORIZER); + + if(yoda && flying) + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); + if(damage_goodhits && self.vaporizer_lasthit) + { + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_IMPRESSIVE); + damage_goodhits = 0; // only every second time + } + + self.vaporizer_lasthit = damage_goodhits; + + pointparticles(particleeffectnum("nex_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); + + // teamcolor / hit beam effect + vector v; + v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); + switch(self.team) + { + case NUM_TEAM_1: // Red + if(damage_goodhits) + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED_HIT"), w_shotorg, v); + else + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED"), w_shotorg, v); + break; + case NUM_TEAM_2: // Blue + if(damage_goodhits) + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE_HIT"), w_shotorg, v); + else + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE"), w_shotorg, v); + break; + case NUM_TEAM_3: // Yellow + if(damage_goodhits) + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW_HIT"), w_shotorg, v); + else + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW"), w_shotorg, v); + break; + case NUM_TEAM_4: // Pink + if(damage_goodhits) + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK_HIT"), w_shotorg, v); + else + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK"), w_shotorg, v); + break; + default: + if(damage_goodhits) + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3_HIT"), w_shotorg, v); + else + WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3"), w_shotorg, v); + break; + } + + W_DecreaseAmmo(((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo))); +} + +float W_Vaporizer(float req) +{ + float ammo_amount; + float vaporizer_ammo; + + // now multiple WR_s use this + vaporizer_ammo = ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)); + + switch(req) + { + case WR_AIM: + { + if(self.WEP_AMMO(VAPORIZER) > 0) + self.BUTTON_ATCK = bot_aim(1000000, 0, 1, FALSE); + else + self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_SEC(vaporizer, speed), 0, WEP_CVAR_SEC(vaporizer, lifetime), FALSE); // WEAPONTODO: replace with proper vaporizer cvars + + return TRUE; + } + case WR_THINK: + { + // if the laser uses load, we also consider its ammo for reloading + if(WEP_CVAR(vaporizer, reload_ammo) && WEP_CVAR_SEC(vaporizer, ammo) && self.clip_load < min(vaporizer_ammo, WEP_CVAR_SEC(vaporizer, ammo))) // forced reload + WEP_ACTION(self.weapon, WR_RELOAD); + else if(WEP_CVAR(vaporizer, reload_ammo) && self.clip_load < vaporizer_ammo) // forced reload + WEP_ACTION(self.weapon, WR_RELOAD); + else if(self.BUTTON_ATCK) + { + if(weapon_prepareattack(0, WEP_CVAR_PRI(vaporizer, refire))) + { + W_Vaporizer_Attack(); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(vaporizer, animtime), w_ready); + } + } + else if(self.BUTTON_ATCK2) + { + if(self.jump_interval <= time) + if(weapon_prepareattack(1, -1)) + { + // handle refire manually, so that primary and secondary can be fired without conflictions (important for instagib) + self.jump_interval = time + WEP_CVAR_SEC(vaporizer, refire) * W_WeaponRateFactor(); + + // decrease ammo for the laser? + if(WEP_CVAR_SEC(vaporizer, ammo)) + W_DecreaseAmmo(WEP_CVAR_SEC(vaporizer, ammo)); + + // ugly instagib hack to reuse the fire mode of the laser + W_Blaster_Attack( + WEP_VAPORIZER | 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) + ); + + // now do normal refire + weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(vaporizer, animtime), w_ready); + } + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/nexflash.md3"); + precache_model("models/weapons/g_minstanex.md3"); + precache_model("models/weapons/v_minstanex.md3"); + precache_model("models/weapons/h_minstanex.iqm"); + precache_sound("weapons/minstanexfire.wav"); + precache_sound("weapons/nexwhoosh1.wav"); + precache_sound("weapons/nexwhoosh2.wav"); + precache_sound("weapons/nexwhoosh3.wav"); + //W_Blaster(WR_INIT); // Samual: Is this really the proper thing to do? Didn't we already run this previously? + VAPORIZER_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_SETUP: + { + self.ammo_field = WEP_AMMO(VAPORIZER); + self.vaporizer_lasthit = 0; + return TRUE; + } + case WR_CHECKAMMO1: + { + ammo_amount = self.WEP_AMMO(VAPORIZER) >= vaporizer_ammo; + ammo_amount += self.(weapon_load[WEP_VAPORIZER]) >= vaporizer_ammo; + return ammo_amount; + } + case WR_CHECKAMMO2: + { + if(!WEP_CVAR_SEC(vaporizer, ammo)) + return TRUE; + ammo_amount = self.WEP_AMMO(VAPORIZER) >= WEP_CVAR_SEC(vaporizer, ammo); + ammo_amount += self.(weapon_load[WEP_VAPORIZER]) >= WEP_CVAR_SEC(vaporizer, ammo); + return ammo_amount; + } + case WR_CONFIG: + { + VAPORIZER_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_RESETPLAYER: + { + self.vaporizer_lasthit = 0; + return TRUE; + } + case WR_RELOAD: + { + float used_ammo; + if(WEP_CVAR_SEC(vaporizer, ammo)) + used_ammo = min(vaporizer_ammo, WEP_CVAR_SEC(vaporizer, ammo)); + else + used_ammo = vaporizer_ammo; + + W_Reload(used_ammo, "weapons/reload.wav"); + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + return WEAPON_THINKING_WITH_PORTALS; + } + case WR_KILLMESSAGE: + { + return WEAPON_VAPORIZER_MURDER; + } + } + return FALSE; +} +#endif +#ifdef CSQC +float W_Vaporizer(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 6; + if(w_deathtype & HITTYPE_SECONDARY) + { + pointparticles(particleeffectnum("laser_impact"), org2, w_backoff * 1000, 1); + if(!w_issilent) { sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM); } + } + else + { + pointparticles(particleeffectnum("nex_impact"), org2, '0 0 0', 1); + if(!w_issilent) { sound(self, CH_SHOTS, "weapons/neximpact.wav", VOL_BASE, ATTN_NORM); } + } + return TRUE; + } + case WR_INIT: + { + precache_sound("weapons/laserimpact.wav"); + precache_sound("weapons/neximpact.wav"); + if(autocvar_cl_reticle && autocvar_cl_reticle_weapon) + { + precache_pic("gfx/reticle_nex"); + } + return TRUE; + } + case WR_ZOOMRETICLE: + { + if(button_zoom || zoomscript_caught) + { + reticle_image = "gfx/reticle_nex"; + return TRUE; + } + else + { + // no weapon specific image for this weapon + return FALSE; + } + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/w_vortex.qc b/qcsrc/common/weapons/w_vortex.qc new file mode 100644 index 0000000000..6512d04308 --- /dev/null +++ b/qcsrc/common/weapons/w_vortex.qc @@ -0,0 +1,341 @@ +#ifdef REGISTER_WEAPON +REGISTER_WEAPON( +/* WEP_##id */ VORTEX, +/* function */ W_Vortex, +/* ammotype */ ammo_cells, +/* impulse */ 7, +/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN, +/* rating */ BOT_PICKUP_RATING_HIGH, +/* color */ '0.5 1 1', +/* modelname */ "nex", +/* simplemdl */ "foobar", +/* crosshair */ "gfx/crosshairnex 0.65", +/* wepimg */ "weaponnex", +/* refname */ "vortex", +/* wepname */ _("Vortex") +); + +#define VORTEX_SETTINGS(w_cvar,w_prop) VORTEX_SETTINGS_LIST(w_cvar, w_prop, VORTEX, vortex) +#define VORTEX_SETTINGS_LIST(w_cvar,w_prop,id,sn) \ + w_cvar(id, sn, BOTH, ammo) \ + w_cvar(id, sn, BOTH, animtime) \ + w_cvar(id, sn, BOTH, damage) \ + w_cvar(id, sn, BOTH, force) \ + w_cvar(id, sn, BOTH, damagefalloff_mindist) \ + w_cvar(id, sn, BOTH, damagefalloff_maxdist) \ + w_cvar(id, sn, BOTH, damagefalloff_halflife) \ + w_cvar(id, sn, BOTH, damagefalloff_forcehalflife) \ + w_cvar(id, sn, BOTH, refire) \ + w_cvar(id, sn, NONE, charge) \ + w_cvar(id, sn, NONE, charge_mindmg) \ + w_cvar(id, sn, NONE, charge_shot_multiplier) \ + w_cvar(id, sn, NONE, charge_animlimit) \ + w_cvar(id, sn, NONE, charge_limit) \ + w_cvar(id, sn, NONE, charge_rate) \ + w_cvar(id, sn, NONE, charge_rot_rate) \ + w_cvar(id, sn, NONE, charge_rot_pause) \ + w_cvar(id, sn, NONE, charge_start) \ + w_cvar(id, sn, NONE, charge_minspeed) \ + w_cvar(id, sn, NONE, charge_maxspeed) \ + w_cvar(id, sn, NONE, charge_velocity_rate) \ + w_cvar(id, sn, NONE, secondary) \ + w_cvar(id, sn, SEC, chargepool) \ + w_cvar(id, sn, SEC, chargepool_regen) \ + w_cvar(id, sn, SEC, chargepool_pause_regen) \ + w_prop(id, sn, float, reloading_ammo, reload_ammo) \ + w_prop(id, sn, float, reloading_time, reload_time) \ + w_prop(id, sn, float, switchdelay_raise, switchdelay_raise) \ + w_prop(id, sn, float, switchdelay_drop, switchdelay_drop) \ + w_prop(id, sn, string, weaponreplace, weaponreplace) \ + w_prop(id, sn, float, weaponstart, weaponstart) \ + w_prop(id, sn, float, weaponstartoverride, weaponstartoverride) \ + w_prop(id, sn, float, weaponthrowable, weaponthrowable) + +#ifdef SVQC +VORTEX_SETTINGS(WEP_ADD_CVAR, WEP_ADD_PROP) +#endif +#else +#ifdef SVQC +void spawnfunc_weapon_vortex(void) { weapon_defaultspawnfunc(WEP_VORTEX); } +void spawnfunc_weapon_nex(void) { spawnfunc_weapon_vortex(); } + +void SendCSQCVortexBeamParticle(float charge) { + vector v; + v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); + WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); + WriteByte(MSG_BROADCAST, TE_CSQC_VORTEXBEAMPARTICLE); + WriteCoord(MSG_BROADCAST, w_shotorg_x); + WriteCoord(MSG_BROADCAST, w_shotorg_y); + WriteCoord(MSG_BROADCAST, w_shotorg_z); + WriteCoord(MSG_BROADCAST, v_x); + WriteCoord(MSG_BROADCAST, v_y); + WriteCoord(MSG_BROADCAST, v_z); + WriteByte(MSG_BROADCAST, bound(0, 255 * charge, 255)); +} + +void W_Vortex_Attack(float issecondary) +{ + float mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, myammo, charge; + + mydmg = WEP_CVAR_BOTH(vortex, !issecondary, damage); + myforce = WEP_CVAR_BOTH(vortex, !issecondary, force); + mymindist = WEP_CVAR_BOTH(vortex, !issecondary, damagefalloff_mindist); + mymaxdist = WEP_CVAR_BOTH(vortex, !issecondary, damagefalloff_maxdist); + myhalflife = WEP_CVAR_BOTH(vortex, !issecondary, damagefalloff_halflife); + myforcehalflife = WEP_CVAR_BOTH(vortex, !issecondary, damagefalloff_forcehalflife); + myammo = WEP_CVAR_BOTH(vortex, !issecondary, ammo); + + float flying; + flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last + + if(WEP_CVAR(vortex, charge)) + { + charge = WEP_CVAR(vortex, charge_mindmg) / mydmg + (1 - WEP_CVAR(vortex, charge_mindmg) / mydmg) * self.vortex_charge; + self.vortex_charge *= WEP_CVAR(vortex, charge_shot_multiplier); // do this AFTER setting mydmg/myforce + // O RLY? -- divVerent + // YA RLY -- FruitieX + } + else + charge = 1; + mydmg *= charge; + myforce *= charge; + + W_SetupShot(self, TRUE, 5, "weapons/nexfire.wav", CH_WEAPON_A, mydmg); + if(charge > WEP_CVAR(vortex, charge_animlimit) && WEP_CVAR(vortex, charge_animlimit)) // if the Vortex is overcharged, we play an extra sound + { + sound(self, CH_WEAPON_B, "weapons/nexcharge.wav", VOL_BASE * (charge - 0.5 * WEP_CVAR(vortex, charge_animlimit)) / (1 - 0.5 * WEP_CVAR(vortex, charge_animlimit)), ATTN_NORM); + } + + yoda = 0; + FireRailgunBullet(w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, WEP_VORTEX); + + if(yoda && flying) + Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); + + //beam and muzzle flash done on client + SendCSQCVortexBeamParticle(charge); + + W_DecreaseAmmo(myammo); +} + +void spawnfunc_weapon_vortex(void); // defined in t_items.qc + +.float vortex_chargepool_pauseregen_finished; +float W_Vortex(float req) +{ + float dt; + float ammo_amount; + switch(req) + { + case WR_AIM: + { + if(bot_aim(1000000, 0, 1, FALSE)) + self.BUTTON_ATCK = TRUE; + else + { + if(WEP_CVAR(vortex, charge)) + self.BUTTON_ATCK2 = TRUE; + } + return TRUE; + } + case WR_THINK: + { + if(WEP_CVAR(vortex, charge) && self.vortex_charge < WEP_CVAR(vortex, charge_limit)) + self.vortex_charge = min(1, self.vortex_charge + WEP_CVAR(vortex, charge_rate) * frametime / W_TICSPERFRAME); + + if(WEP_CVAR_SEC(vortex, chargepool)) + if(self.vortex_chargepool_ammo < 1) + { + if(self.vortex_chargepool_pauseregen_finished < time) + self.vortex_chargepool_ammo = min(1, self.vortex_chargepool_ammo + WEP_CVAR_SEC(vortex, chargepool_regen) * frametime / W_TICSPERFRAME); + self.pauseregen_finished = max(self.pauseregen_finished, time + WEP_CVAR_SEC(vortex, chargepool_pause_regen)); + } + + if(autocvar_g_balance_vortex_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(vortex, ammo), WEP_CVAR_SEC(vortex, ammo))) // forced reload + WEP_ACTION(self.weapon, WR_RELOAD); + else + { + if(self.BUTTON_ATCK) + { + if(weapon_prepareattack(0, WEP_CVAR_PRI(vortex, refire))) + { + W_Vortex_Attack(0); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(vortex, animtime), w_ready); + } + } + if((WEP_CVAR(vortex, charge) && !WEP_CVAR(vortex, secondary)) ? (self.BUTTON_ZOOM | self.BUTTON_ZOOMSCRIPT) : self.BUTTON_ATCK2) + { + if(WEP_CVAR(vortex, charge)) + { + self.vortex_charge_rottime = time + WEP_CVAR(vortex, charge_rot_pause); + dt = frametime / W_TICSPERFRAME; + + if(self.vortex_charge < 1) + { + if(WEP_CVAR_SEC(vortex, chargepool)) + { + if(WEP_CVAR_SEC(vortex, ammo)) + { + // always deplete if secondary is held + self.vortex_chargepool_ammo = max(0, self.vortex_chargepool_ammo - WEP_CVAR_SEC(vortex, ammo) * dt); + + dt = min(dt, (1 - self.vortex_charge) / WEP_CVAR(vortex, charge_rate)); + self.vortex_chargepool_pauseregen_finished = time + WEP_CVAR_SEC(vortex, chargepool_pause_regen); + dt = min(dt, self.vortex_chargepool_ammo); + dt = max(0, dt); + + self.vortex_charge += dt * WEP_CVAR(vortex, charge_rate); + } + } + + else if(WEP_CVAR_SEC(vortex, ammo)) + { + if(self.BUTTON_ATCK2) // only eat ammo when the button is pressed + { + dt = min(dt, (1 - self.vortex_charge) / WEP_CVAR(vortex, charge_rate)); + if(!(self.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, (self.clip_load - WEP_CVAR_PRI(vortex, ammo)) / WEP_CVAR_SEC(vortex, ammo)); + dt = max(0, dt); + if(dt > 0) + { + self.clip_load = max(WEP_CVAR_SEC(vortex, ammo), self.clip_load - WEP_CVAR_SEC(vortex, ammo) * dt); + } + self.(weapon_load[WEP_VORTEX]) = self.clip_load; + } + else + { + dt = min(dt, (self.WEP_AMMO(VORTEX) - WEP_CVAR_PRI(vortex, ammo)) / WEP_CVAR_SEC(vortex, ammo)); + dt = max(0, dt); + if(dt > 0) + { + self.WEP_AMMO(VORTEX) = max(WEP_CVAR_SEC(vortex, ammo), self.WEP_AMMO(VORTEX) - WEP_CVAR_SEC(vortex, ammo) * dt); + } + } + } + self.vortex_charge += dt * WEP_CVAR(vortex, charge_rate); + } + } + + else + { + dt = min(dt, (1 - self.vortex_charge) / WEP_CVAR(vortex, charge_rate)); + self.vortex_charge += dt * WEP_CVAR(vortex, charge_rate); + } + } + } + else if(WEP_CVAR(vortex, secondary)) + { + if(weapon_prepareattack(0, WEP_CVAR_SEC(vortex, refire))) + { + W_Vortex_Attack(1); + weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_SEC(vortex, animtime), w_ready); + } + } + } + } + + return TRUE; + } + case WR_INIT: + { + precache_model("models/nexflash.md3"); + precache_model("models/weapons/g_nex.md3"); + precache_model("models/weapons/v_nex.md3"); + precache_model("models/weapons/h_nex.iqm"); + precache_sound("weapons/nexfire.wav"); + precache_sound("weapons/nexcharge.wav"); + precache_sound("weapons/nexwhoosh1.wav"); + precache_sound("weapons/nexwhoosh2.wav"); + precache_sound("weapons/nexwhoosh3.wav"); + VORTEX_SETTINGS(WEP_SKIP_CVAR, WEP_SET_PROP) + return TRUE; + } + case WR_CHECKAMMO1: + { + ammo_amount = self.WEP_AMMO(VORTEX) >= WEP_CVAR_PRI(vortex, ammo); + ammo_amount += (autocvar_g_balance_vortex_reload_ammo && self.(weapon_load[WEP_VORTEX]) >= WEP_CVAR_PRI(vortex, ammo)); + return ammo_amount; + } + case WR_CHECKAMMO2: + { + if(WEP_CVAR(vortex, secondary)) + { + // don't allow charging if we don't have enough ammo + ammo_amount = self.WEP_AMMO(VORTEX) >= WEP_CVAR_SEC(vortex, ammo); + ammo_amount += self.(weapon_load[WEP_VORTEX]) >= WEP_CVAR_SEC(vortex, ammo); + return ammo_amount; + } + else + { + return FALSE; // zoom is not a fire mode + } + } + case WR_CONFIG: + { + VORTEX_SETTINGS(WEP_CONFIG_WRITE_CVARS, WEP_CONFIG_WRITE_PROPS) + return TRUE; + } + case WR_RELOAD: + { + W_Reload(min(WEP_CVAR_PRI(vortex, ammo), WEP_CVAR_SEC(vortex, ammo)), "weapons/reload.wav"); + return TRUE; + } + case WR_SUICIDEMESSAGE: + { + return WEAPON_THINKING_WITH_PORTALS; + } + case WR_KILLMESSAGE: + { + return WEAPON_VORTEX_MURDER; + } + } + return FALSE; +} +#endif +#ifdef CSQC +var float autocvar_g_balance_vortex_secondary = 0; // WEAPONTODO +float W_Vortex(float req) +{ + switch(req) + { + case WR_IMPACTEFFECT: + { + vector org2; + org2 = w_org + w_backoff * 6; + pointparticles(particleeffectnum("nex_impact"), org2, '0 0 0', 1); + if(!w_issilent) + sound(self, CH_SHOTS, "weapons/neximpact.wav", VOL_BASE, ATTN_NORM); + + return TRUE; + } + case WR_INIT: + { + precache_sound("weapons/neximpact.wav"); + if(autocvar_cl_reticle && autocvar_cl_reticle_weapon) + { + precache_pic("gfx/reticle_nex"); + } + return TRUE; + } + case WR_ZOOMRETICLE: + { + if(button_zoom || zoomscript_caught || (!WEP_CVAR(vortex, secondary) && button_attack2)) + { + reticle_image = "gfx/reticle_nex"; + return TRUE; + } + else + { + // no weapon specific image for this weapon + return FALSE; + } + } + } + return FALSE; +} +#endif +#endif diff --git a/qcsrc/common/weapons/weapons.qc b/qcsrc/common/weapons/weapons.qc new file mode 100644 index 0000000000..e45a901c3c --- /dev/null +++ b/qcsrc/common/weapons/weapons.qc @@ -0,0 +1,321 @@ +#ifndef MENUQC +#include "calculations.qc" +#endif +#include "all.qh" + +// WEAPON PLUGIN SYSTEM +entity weapon_info[WEP_MAXCOUNT]; +entity dummy_weapon_info; + +#if WEP_MAXCOUNT > 72 +# error Kein Weltraum links auf dem Gerät +#endif + +WepSet WepSet_FromWeapon(float a) { + a -= WEP_FIRST; +#if WEP_MAXCOUNT > 24 + if(a >= 24) { + a -= 24; +#if WEP_MAXCOUNT > 48 + if(a >= 24) { + a -= 24; + return '0 0 1' * power2of(a); + } +#endif + return '0 1 0' * power2of(a); + } +#endif + return '1 0 0' * power2of(a); +} +#ifdef SVQC +void WepSet_AddStat() +{ + addstat(STAT_WEAPONS, AS_INT, weapons_x); +#if WEP_MAXCOUNT > 24 + addstat(STAT_WEAPONS2, AS_INT, weapons_y); +#if WEP_MAXCOUNT > 48 + addstat(STAT_WEAPONS3, AS_INT, weapons_z); +#endif +#endif +} +void WriteWepSet(float dst, WepSet w) +{ +#if WEP_MAXCOUNT > 48 + WriteInt72_t(dst, w); +#elif WEP_MAXCOUNT > 24 + WriteInt48_t(dst, w); +#else + WriteInt24_t(dst, w_x); +#endif +} +#endif +#ifdef CSQC +WepSet WepSet_GetFromStat() +{ + WepSet w = '0 0 0'; + w_x = getstati(STAT_WEAPONS); +#if WEP_MAXCOUNT > 24 + w_y = getstati(STAT_WEAPONS2); +#if WEP_MAXCOUNT > 48 + w_z = getstati(STAT_WEAPONS3); +#endif +#endif + return w; +} +WepSet ReadWepSet() +{ +#if WEP_MAXCOUNT > 48 + return ReadInt72_t(); +#elif WEP_MAXCOUNT > 24 + return ReadInt48_t(); +#else + return ReadInt24_t() * '1 0 0'; +#endif +} +#endif + +void register_weapon( + float id, + WepSet bit, + float(float) func, + .float ammotype, + float i, + float weapontype, + float pickupbasevalue, + vector clr, + string modelname, + string simplemdl, + string crosshair, + string wepimg, + string refname, + string wepname) +{ + entity e; + weapon_info[id - 1] = e = spawn(); + e.classname = "weapon_info"; + e.weapon = id; + e.weapons = bit; + e.weapon_func = func; + e.ammo_field = ammotype; + e.impulse = i; + e.spawnflags = weapontype; + e.bot_pickupbasevalue = pickupbasevalue; + e.wpcolor = clr; + e.wpmodel = strzone(strcat("wpn-", ftos(id))); + e.mdl = modelname; + e.model = strzone(strcat("models/weapons/g_", modelname, ".md3")); + e.w_simplemdl = strzone(simplemdl); // simpleitems weapon model/image + e.w_crosshair = strzone(car(crosshair)); + string s = cdr(crosshair); + e.w_crosshair_size = ((s != "") ? stof(s) : 1); // so that we can scale the crosshair from code (for compat) + e.model2 = strzone(wepimg); + e.netname = refname; + e.message = wepname; + + #ifndef MENUQC + func(WR_INIT); + #endif +} +float w_null(float dummy) +{ + return 0; +} +void register_weapons_done() +{ + dummy_weapon_info = spawn(); + dummy_weapon_info.classname = "weapon_info"; + dummy_weapon_info.weapon = 0; // you can recognize dummies by this + dummy_weapon_info.weapons = '0 0 0'; + dummy_weapon_info.netname = ""; + dummy_weapon_info.message = "AOL CD Thrower"; + dummy_weapon_info.weapon_func = w_null; + dummy_weapon_info.wpmodel = ""; + dummy_weapon_info.mdl = ""; + dummy_weapon_info.model = ""; + dummy_weapon_info.spawnflags = 0; + dummy_weapon_info.impulse = -1; + dummy_weapon_info.bot_pickupbasevalue = 0; + dummy_weapon_info.ammo_field = ammo_none; + + dummy_weapon_info.w_crosshair = "gfx/crosshair1"; + dummy_weapon_info.w_crosshair_size = 1; + dummy_weapon_info.model2 = ""; + + float i; + weaponorder_byid = ""; + for(i = WEP_MAXCOUNT; i >= 1; --i) + if(weapon_info[i-1]) + weaponorder_byid = strcat(weaponorder_byid, " ", ftos(i)); + weaponorder_byid = strzone(substring(weaponorder_byid, 1, strlen(weaponorder_byid) - 1)); +} +entity get_weaponinfo(float id) +{ + entity w; + if(id < WEP_FIRST || id > WEP_LAST) + return dummy_weapon_info; + w = weapon_info[id - 1]; + if(w) + return w; + return dummy_weapon_info; +} +string W_FixWeaponOrder(string order, float complete) +{ + return fixPriorityList(order, WEP_FIRST, WEP_LAST, 230 - WEP_FIRST, complete); +} +string W_NameWeaponOrder_MapFunc(string s) +{ + entity wi; + if(s == "0" || stof(s)) + { + wi = get_weaponinfo(stof(s)); + if(wi != dummy_weapon_info) + return wi.netname; + } + return s; +} + +string W_UndeprecateName(string s) +{ + switch ( s ) + { + case "nex" : return "vortex"; + case "rocketlauncher" : return "devastator"; + case "laser" : return "blaster"; + case "minstanex" : return "vaporizer"; + case "grenadelauncher": return "mortar"; + case "uzi" : return "machinegun"; + default : return s; + } +} +string W_NameWeaponOrder(string order) +{ + return mapPriorityList(order, W_NameWeaponOrder_MapFunc); +} +string W_NumberWeaponOrder_MapFunc(string s) +{ + float i; + if(s == "0" || stof(s)) + return s; + s = W_UndeprecateName(s); + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + if(s == get_weaponinfo(i).netname) + return ftos(i); + return s; +} +string W_NumberWeaponOrder(string order) +{ + return mapPriorityList(order, W_NumberWeaponOrder_MapFunc); +} + +float W_FixWeaponOrder_BuildImpulseList_buf[WEP_MAXCOUNT]; +string W_FixWeaponOrder_BuildImpulseList_order; +void W_FixWeaponOrder_BuildImpulseList_swap(float i, float j, entity pass) +{ + float h; + h = W_FixWeaponOrder_BuildImpulseList_buf[i]; + W_FixWeaponOrder_BuildImpulseList_buf[i] = W_FixWeaponOrder_BuildImpulseList_buf[j]; + W_FixWeaponOrder_BuildImpulseList_buf[j] = h; +} +float W_FixWeaponOrder_BuildImpulseList_cmp(float i, float j, entity pass) +{ + entity e1, e2; + float d; + e1 = get_weaponinfo(W_FixWeaponOrder_BuildImpulseList_buf[i]); + e2 = get_weaponinfo(W_FixWeaponOrder_BuildImpulseList_buf[j]); + d = mod(e1.impulse + 9, 10) - mod(e2.impulse + 9, 10); + if(d != 0) + return -d; // high impulse first! + return + strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "), sprintf(" %d ", W_FixWeaponOrder_BuildImpulseList_buf[i]), 0) + - + strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "), sprintf(" %d ", W_FixWeaponOrder_BuildImpulseList_buf[j]), 0) + ; // low char index first! +} +string W_FixWeaponOrder_BuildImpulseList(string o) +{ + float i; + W_FixWeaponOrder_BuildImpulseList_order = o; + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST] = i; + heapsort(WEP_LAST - WEP_FIRST + 1, W_FixWeaponOrder_BuildImpulseList_swap, W_FixWeaponOrder_BuildImpulseList_cmp, world); + o = ""; + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + o = strcat(o, " ", ftos(W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST])); + W_FixWeaponOrder_BuildImpulseList_order = string_null; + return substring(o, 1, -1); +} + +string W_FixWeaponOrder_AllowIncomplete(string order) +{ + return W_FixWeaponOrder(order, 0); +} + +string W_FixWeaponOrder_ForceComplete(string order) +{ + if(order == "") + order = W_NumberWeaponOrder(cvar_defstring("cl_weaponpriority")); + return W_FixWeaponOrder(order, 1); +} + +void W_RandomWeapons(entity e, float n) +{ + float i, j; + WepSet remaining; + WepSet result; + remaining = e.weapons; + result = '0 0 0'; + for(i = 0; i < n; ++i) + { + RandomSelection_Init(); + for(j = WEP_FIRST; j <= WEP_LAST; ++j) + if(remaining & WepSet_FromWeapon(j)) + RandomSelection_Add(world, j, string_null, 1, 1); + result |= WepSet_FromWeapon(RandomSelection_chosen_float); + remaining &= ~WepSet_FromWeapon(RandomSelection_chosen_float); + } + e.weapons = result; +} + +string GetAmmoPicture(.float ammotype) +{ + switch(ammotype) + { + case ammo_shells: return "ammo_shells"; + case ammo_nails: return "ammo_bullets"; + case ammo_rockets: return "ammo_rockets"; + case ammo_cells: return "ammo_cells"; + case ammo_plasma: return "ammo_cells"; + case ammo_fuel: return "ammo_fuel"; + default: return ""; // wtf, no ammo type? + } +} + +#ifdef CSQC +.float GetAmmoFieldFromNum(float i) +{ + switch(i) + { + case 0: return ammo_shells; + case 1: return ammo_nails; + case 2: return ammo_rockets; + case 3: return ammo_cells; + case 4: return ammo_plasma; + case 5: return ammo_fuel; + default: return ammo_none; + } +} + +float GetAmmoStat(.float ammotype) +{ + switch(ammotype) + { + case ammo_shells: return STAT_SHELLS; + case ammo_nails: return STAT_NAILS; + case ammo_rockets: return STAT_ROCKETS; + case ammo_cells: return STAT_CELLS; + case ammo_plasma: return STAT_PLASMA; + case ammo_fuel: return STAT_FUEL; + default: return -1; + } +} +#endif diff --git a/qcsrc/common/weapons/weapons.qh b/qcsrc/common/weapons/weapons.qh new file mode 100644 index 0000000000..dca226f420 --- /dev/null +++ b/qcsrc/common/weapons/weapons.qh @@ -0,0 +1,208 @@ +#ifndef MENUQC +#include "calculations.qh" +#endif + +const float MAX_SHOT_DISTANCE = 32768; + +// weapon pickup ratings for bot logic +const float BOT_PICKUP_RATING_LOW = 2500; +const float BOT_PICKUP_RATING_MID = 5000; +const float BOT_PICKUP_RATING_HIGH = 10000; + +// weapon flags +const float WEP_TYPE_OTHER = 0x00; // not for damaging people +const float WEP_TYPE_SPLASH = 0x01; // splash damage +const float WEP_TYPE_HITSCAN = 0x02; // hitscan +const float WEP_TYPEMASK = 0x0F; +const float WEP_FLAG_CANCLIMB = 0x10; // can be used for movement +const float WEP_FLAG_NORMAL = 0x20; // in "most weapons" set +const float WEP_FLAG_HIDDEN = 0x40; // hides from menu +const float WEP_FLAG_RELOADABLE = 0x80; // can has reload +const float WEP_FLAG_SUPERWEAPON = 0x100; // powerup timer +const float WEP_FLAG_MUTATORBLOCKED = 0x200; // hides from impulse 99 etc. (mutators are allowed to clear this flag) + +// weapon requests +const float WR_SETUP = 1; // (SERVER) setup weapon data +const float WR_THINK = 2; // (SERVER) logic to run every frame +const float WR_CHECKAMMO1 = 3; // (SERVER) checks ammo for weapon primary +const float WR_CHECKAMMO2 = 4; // (SERVER) checks ammo for weapon second +const float WR_AIM = 5; // (SERVER) runs bot aiming code for this weapon +const float WR_INIT = 6; // (BOTH) precaches models/sounds used by this weapon, also sets up weapon properties +const float WR_SUICIDEMESSAGE = 7; // (SERVER) notification number for suicide message (may inspect w_deathtype for details) +const float WR_KILLMESSAGE = 8; // (SERVER) notification number for kill message (may inspect w_deathtype for details) +const float WR_RELOAD = 9; // (SERVER) handles reloading for weapon +const float WR_RESETPLAYER = 10; // (SERVER) clears fields that the weapon may use +const float WR_IMPACTEFFECT = 11; // (CLIENT) impact effect for weapon explosion +const float WR_PLAYERDEATH = 12; // (SERVER) called whenever a player dies +const float WR_GONETHINK = 13; // (SERVER) logic to run when weapon is lost +const float WR_CONFIG = 14; // (ALL) dump weapon cvars to config in data directory (see: sv_cmd dumpweapons) +const float WR_ZOOMRETICLE = 15; // (CLIENT) weapon specific zoom reticle +const float WR_DROP = 16; // (SERVER) the weapon is dropped +const float WR_PICKUP = 17; // (SERVER) a weapon is picked up + +// variables: +string weaponorder_byid; + +// weapon sets +typedef vector WepSet; +WepSet WepSet_FromWeapon(float a); +#ifdef SVQC +void WepSet_AddStat(); +void WriteWepSet(float dest, WepSet w); +#endif +#ifdef CSQC +WepSet WepSet_GetFromStat(); +WepSet ReadWepSet(); +#endif + +// weapon name macros +#define WEP_FIRST 1 +#define WEP_MAXCOUNT 24 // Increase as needed. Can be up to three times as much. +float WEP_COUNT; +float WEP_LAST; +WepSet WEPSET_ALL; +WepSet WEPSET_SUPERWEAPONS; + +// functions: +entity get_weaponinfo(float id); +string W_FixWeaponOrder(string order, float complete); +string W_NameWeaponOrder(string order); +string W_NumberWeaponOrder(string order); +string W_FixWeaponOrder_BuildImpulseList(string o); +string W_FixWeaponOrder_AllowIncomplete(string order); +string W_FixWeaponOrder_ForceComplete(string order); +void W_RandomWeapons(entity e, float n); + +string GetAmmoPicture(.float ammotype); + +#ifdef CSQC +.float GetAmmoFieldFromNum(float i); +float GetAmmoStat(.float ammotype); +#endif + +// ammo types +.float ammo_shells; +.float ammo_nails; +.float ammo_rockets; +.float ammo_cells; +.float ammo_plasma; +.float ammo_fuel; +.float ammo_none; + +// other useful macros +#define WEP_ACTION(wpn,wrequest) (get_weaponinfo(wpn)).weapon_func(wrequest) +#define WEP_AMMO(wpn) ((get_weaponinfo(WEP_##wpn)).ammo_field) // only used inside weapon files/with direct name, don't duplicate prefix +#define WEP_NAME(wpn) ((get_weaponinfo(wpn)).message) + + +// ====================== +// Configuration Macros +// ====================== + +// create cvars for weapon settings +#define WEP_ADD_CVAR_NONE(wepname,name) [[last]] float autocvar_g_balance_##wepname##_##name; + +#define WEP_ADD_CVAR_PRI(wepname,name) WEP_ADD_CVAR_NONE(wepname, primary_##name) +#define WEP_ADD_CVAR_SEC(wepname,name) WEP_ADD_CVAR_NONE(wepname, secondary_##name) +#define WEP_ADD_CVAR_BOTH(wepname,name) \ + WEP_ADD_CVAR_PRI(wepname, name) \ + WEP_ADD_CVAR_SEC(wepname, name) + +#define WEP_ADD_CVAR(wepid,wepname,mode,name) WEP_ADD_CVAR_##mode(wepname, name) + +// create properties for weapon settings +#define WEP_ADD_PROP(wepid,wepname,type,prop,name) \ + .type prop; \ + [[last]] type autocvar_g_balance_##wepname##_##name; + +// read cvars from weapon settings +#define WEP_CVAR(wepname,name) autocvar_g_balance_##wepname##_##name +#define WEP_CVAR_PRI(wepname,name) WEP_CVAR(wepname, primary_##name) +#define WEP_CVAR_SEC(wepname,name) WEP_CVAR(wepname, secondary_##name) +#define WEP_CVAR_BOTH(wepname,isprimary,name) ((isprimary) ? WEP_CVAR_PRI(wepname, name) : WEP_CVAR_SEC(wepname, name)) + +// set initialization values for weapon settings +#define WEP_SKIP_CVAR(unuseda,unusedb,unusedc,unusedd) /* skip cvars */ +#define WEP_SET_PROP(wepid,wepname,type,prop,name) get_weaponinfo(WEP_##wepid).##prop = autocvar_g_balance_##wepname##_##name; + + +// ===================== +// Weapon Registration +// ===================== + +float w_null(float dummy); + +void register_weapon( + float id, + WepSet bit, + float(float) func, + .float ammotype, + float i, + float weapontype, + float pickupbasevalue, + vector clr, + string modelname, + string simplemdl, + string crosshair, + string wepimg, + string refname, + string wepname); + +void register_weapons_done(); + +// entity properties of weaponinfo: +// fields which are explicitly/manually set are marked with "M", fields set automatically are marked with "A" +.float weapon; // M: WEP_id // WEP_... +.WepSet weapons; // A: WEPSET_id // WEPSET_... +.float(float) weapon_func; // M: function // w_... +..float ammo_field; // M: ammotype // main ammo field +.float impulse; // M: impulse // weapon impulse +.float spawnflags; // M: flags // WEPSPAWNFLAG_... combined +.float bot_pickupbasevalue; // M: rating // bot weapon priority +.vector wpcolor; // M: color // waypointsprite color +.string wpmodel; // A: wpn-id // wpn- sprite name +.string mdl; // M: modelname // name of model (without g_ v_ or h_ prefixes) +.string model; // A: modelname // full path to g_ model +.string w_simplemdl; // M: simplemdl // simpleitems weapon model/image +.string w_crosshair; // M: crosshair // per-weapon crosshair: "CrosshairImage Size" +.float w_crosshair_size; // A: crosshair // per-weapon crosshair size (argument two of "crosshair" field) +.string model2; // M: wepimg // "weaponfoobar" side view image file of weapon // WEAPONTODO: Move out of skin files, move to common files +.string netname; // M: refname // reference name name +.string message; // M: wepname // human readable name + + +// note: the fabs call is just there to hide "if result is constant" warning +#define REGISTER_WEAPON_2(id,bit,function,ammotype,impulse,flags,rating,color,modelname,simplemdl,crosshair,wepimg,refname,wepname) \ + float id; \ + WepSet bit; \ + float function(float); \ + void RegisterWeapons_##id() \ + { \ + WEP_LAST = (id = WEP_FIRST + WEP_COUNT); \ + bit = WepSet_FromWeapon(id); \ + WEPSET_ALL |= bit; \ + if((flags) & WEP_FLAG_SUPERWEAPON) \ + WEPSET_SUPERWEAPONS |= bit; \ + ++WEP_COUNT; \ + register_weapon(id,bit,function,ammotype,impulse,flags,rating,color,modelname,simplemdl,crosshair,wepimg,refname,wepname); \ + } \ + ACCUMULATE_FUNCTION(RegisterWeapons, RegisterWeapons_##id) +#ifdef MENUQC +#define REGISTER_WEAPON(id,function,ammotype,impulse,flags,rating,color,modelname,simplemdl,crosshair,wepimg,refname,wepname) \ + REGISTER_WEAPON_2(WEP_##id,WEPSET_##id,w_null,ammotype,impulse,flags,rating,color,modelname,simplemdl,crosshair,wepimg,refname,wepname) +#else +#define REGISTER_WEAPON(id,function,ammotype,impulse,flags,rating,color,modelname,simplemdl,crosshair,wepimg,refname,wepname) \ + REGISTER_WEAPON_2(WEP_##id,WEPSET_##id,function,ammotype,impulse,flags,rating,color,modelname,simplemdl,crosshair,wepimg,refname,wepname) +#endif + +#include "all.qh" + +#undef WEP_ADD_CVAR_MO_PRI +#undef WEP_ADD_CVAR_MO_SEC +#undef WEP_ADD_CVAR_MO_BOTH +#undef WEP_ADD_CVAR_MO_NONE +#undef WEP_ADD_CVAR +#undef WEP_ADD_PROP +#undef REGISTER_WEAPON + +ACCUMULATE_FUNCTION(RegisterWeapons, register_weapons_done); diff --git a/qcsrc/menu/draw.qh b/qcsrc/menu/draw.qh index cfab348ed6..bd75795bbc 100644 --- a/qcsrc/menu/draw.qh +++ b/qcsrc/menu/draw.qh @@ -38,7 +38,5 @@ vector boxToGlobalSize(vector v, vector scale); vector globalToBox(vector v, vector shift, vector scale); vector globalToBoxSize(vector v, vector scale); -float draw_NeedResizeNotify; - float draw_TextWidth_WithColors(string s, vector size); float draw_TextWidth_WithoutColors(string s, vector size); diff --git a/qcsrc/menu/item/container.c b/qcsrc/menu/item/container.c index 091891a671..8bc925f1ca 100644 --- a/qcsrc/menu/item/container.c +++ b/qcsrc/menu/item/container.c @@ -317,8 +317,6 @@ void Container_addItem(entity me, entity other, vector theOrigin, vector theSize other.prevSibling = l; other.nextSibling = NULL; me.lastChild = other; - - draw_NeedResizeNotify = 1; } void Container_removeItem(entity me, entity other) diff --git a/qcsrc/menu/item/label.c b/qcsrc/menu/item/label.c index 0f6f966471..592c3a7ac5 100644 --- a/qcsrc/menu/item/label.c +++ b/qcsrc/menu/item/label.c @@ -79,7 +79,7 @@ void Label_recalcPositionWithText(entity me, string t) me.realOrigin_x = me.keepspaceLeft; if(!me.overrideCondenseFactor) me.condenseFactor = spaceAvail / spaceUsed; - dprintf(_("NOTE: label text %s too wide for label, condensed by factor %f\n"), t, me.condenseFactor); + dprintf("NOTE: label text %s too wide for label, condensed by factor %f\n", t, me.condenseFactor); } if(!me.overrideRealOrigin_y) diff --git a/qcsrc/menu/item/modalcontroller.c b/qcsrc/menu/item/modalcontroller.c index 6aab958e65..8a025cb330 100644 --- a/qcsrc/menu/item/modalcontroller.c +++ b/qcsrc/menu/item/modalcontroller.c @@ -2,7 +2,6 @@ CLASS(ModalController) EXTENDS(Container) METHOD(ModalController, resizeNotify, void(entity, vector, vector, vector, vector)) METHOD(ModalController, draw, void(entity)) - METHOD(ModalController, addItem, void(entity, entity, vector, vector, float)) METHOD(ModalController, showChild, void(entity, entity, vector, vector, float)) METHOD(ModalController, hideChild, void(entity, entity, float)) METHOD(ModalController, hideAll, void(entity, float)) diff --git a/qcsrc/menu/item/slider.c b/qcsrc/menu/item/slider.c index 8861357b77..b071a1af34 100644 --- a/qcsrc/menu/item/slider.c +++ b/qcsrc/menu/item/slider.c @@ -61,7 +61,7 @@ void Slider_setSliderValue(entity me, float val) } string Slider_toString(entity me) { - return sprintf(_("%d (%s)"), me.value, me.valueToText(me, me.value)); + return sprintf("%d (%s)", me.value, me.valueToText(me, me.value)); } void Slider_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize) { diff --git a/qcsrc/menu/menu.qc b/qcsrc/menu/menu.qc index d7fd581a32..b17e91f867 100644 --- a/qcsrc/menu/menu.qc +++ b/qcsrc/menu/menu.qc @@ -56,7 +56,7 @@ void m_init() check_unacceptable_compiler_bugs(); #ifdef WATERMARK - printf(_("^4MQC Build information: ^1%s\n"), WATERMARK); + dprintf("^4MQC Build information: ^1%s\n", WATERMARK); #endif // list all game dirs (TEST) diff --git a/qcsrc/menu/progs.src b/qcsrc/menu/progs.src index 86cb9621c7..9d2f693330 100644 --- a/qcsrc/menu/progs.src +++ b/qcsrc/menu/progs.src @@ -18,7 +18,7 @@ oo/base.h ../common/constants.qh ../common/mapinfo.qh ../common/campaign_common.qh -../common/items.qh +../common/weapons/weapons.qh // TODO ../common/counting.qh ../common/command/markup.qh ../common/command/rpn.qh @@ -51,7 +51,7 @@ xonotic/util.qc ../common/campaign_file.qc ../common/campaign_setup.qc ../common/mapinfo.qc -../common/items.qc +../common/weapons/weapons.qc // TODO ../common/urllib.qc ../common/monsters/monsters.qc diff --git a/qcsrc/menu/xonotic/campaign.c b/qcsrc/menu/xonotic/campaign.c index 90b559b01c..454b4ad710 100644 --- a/qcsrc/menu/xonotic/campaign.c +++ b/qcsrc/menu/xonotic/campaign.c @@ -283,7 +283,7 @@ void XonoticCampaignList_drawListBoxItem(entity me, float i, vector absSize, flo if(i <= me.campaignIndex) s = campaign_shortdesc[i]; // fteqcc sucks else - s = _("???"); + s = "???"; s = draw_TextShortenToWidth(sprintf(_("Level %d: %s"), i+1, s), me.columnNameSize, 0, me.realFontSize); draw_Text(me.realUpperMargin1 * eY + (me.columnNameOrigin + 0.00 * (me.columnNameSize - draw_TextWidth(s, 0, me.realFontSize))) * eX, s, me.realFontSize, theColor, theAlpha, 0); diff --git a/qcsrc/menu/xonotic/dialog_firstrun.c b/qcsrc/menu/xonotic/dialog_firstrun.c index 1b975da2b1..90988703e0 100644 --- a/qcsrc/menu/xonotic/dialog_firstrun.c +++ b/qcsrc/menu/xonotic/dialog_firstrun.c @@ -17,7 +17,7 @@ ENDCLASS(XonoticFirstRunDialog) #ifdef IMPLEMENTATION float CheckFirstRunButton(entity me) { - if(cvar_string("_cl_name") != "Player") + if(cvar_string("_cl_name") != cvar_defstring("_cl_name")) return 1; if(cvar_string("_menu_prvm_language") != prvm_language) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_centerprint.c b/qcsrc/menu/xonotic/dialog_hudpanel_centerprint.c index c4eea2eea6..dce687a0dd 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_centerprint.c +++ b/qcsrc/menu/xonotic/dialog_hudpanel_centerprint.c @@ -1,7 +1,7 @@ #ifdef INTERFACE CLASS(XonoticHUDCenterprintDialog) EXTENDS(XonoticRootDialog) METHOD(XonoticHUDCenterprintDialog, fill, void(entity)) - ATTRIB(XonoticHUDCenterprintDialog, title, string, _("Centerprint")) + ATTRIB(XonoticHUDCenterprintDialog, title, string, _("Centerprint Panel")) ATTRIB(XonoticHUDCenterprintDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT) ATTRIB(XonoticHUDCenterprintDialog, intendedWidth, float, 0.4) ATTRIB(XonoticHUDCenterprintDialog, rows, float, 15) @@ -26,7 +26,7 @@ void XonoticHUDCenterprintDialog_fill(entity me) me.TR(me); me.TDempty(me, 0.2); me.TD(me, 1, 1.2, e = makeXonoticTextLabel(0, _("Fade time:"))); - me.TD(me, 1, 2.6, e = makeXonoticSlider(0, 1, 0.05, "hud_panel_centerprint_fadetime")); + me.TD(me, 1, 2.6, e = makeXonoticSlider(0, 1, 0.05, "hud_panel_centerprint_fade_out")); me.TR(me); me.TDempty(me, 0.2); me.TD(me, 1, 3.8, e = makeXonoticCheckBox(0, "hud_panel_centerprint_flip", _("Flip messages order"))); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c index 3bac353053..18e167f5d2 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c @@ -78,7 +78,7 @@ string XonoticMutatorsDialog_toString(entity me) s = strcat(s, ", ", _("Invincible Projectiles")); if(cvar_string("g_weaponarena") != "0") s = strcat(s, ", ", WeaponArenaString()); - if(cvar("g_start_weapon_laser") == 0) + if(cvar("g_balance_blaster_weaponstart") == 0) s = strcat(s, ", ", _("No start weapons")); if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity"))) s = strcat(s, ", ", _("Low gravity")); @@ -146,7 +146,7 @@ float checkCompatibility_weaponarena_weapon(entity me) return 0; if(cvar_string("g_weaponarena") == "0") return 0; - if(cvar_string("g_start_weapon_laser") == "0") + if(cvar_string("g_balance_blaster_weaponstart") == "0") return 0; return 1; } @@ -263,9 +263,9 @@ void XonoticMutatorsDialog_fill(entity me) setDependent(e, "g_nix", 1, 1); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_start_weapon_laser", "0", _("No start weapons"))); + me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_balance_blaster_weaponstart", "0", _("No start weapons"))); e.cvarOffValue = "-1"; - makeMulti(e, "g_start_weapon_shotgun g_start_weapon_uzi g_start_weapon_grenadelauncher g_start_weapon_minelayer g_start_weapon_electro g_start_weapon_crylink g_start_weapon_nex g_start_weapon_hagar g_start_weapon_rocketlauncher g_start_weapon_porto g_start_weapon_minstanex g_start_weapon_hook g_start_weapon_hlac g_start_weapon_rifle g_start_weapon_fireball g_start_weapon_seeker g_start_weapon_tuba"); + makeMulti(e, "g_balance_shotgun_weaponstart g_balance_machinegun_weaponstart g_balance_devastator_weaponstart g_balance_minelayer_weaponstart g_balance_electro_weaponstart g_balance_crylink_weaponstart g_balance_hagar_weaponstart g_balance_porto_weaponstart g_balance_vaporizer_weaponstart g_balance_hook_weaponstart g_balance_rifle_weaponstart g_balance_fireball_weaponstart g_balance_seeker_weaponstart g_balance_tuba_weaponstart g_balance_arc_weaponstart g_balance_vortex_weaponstart g_balance_mortar_weaponstart"); me.gotoRC(me, me.rows - 1, 0); me.TD(me, 1, me.columns, e = makeXonoticButton(_("OK"), '0 0 0')); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.c b/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.c index 5ecb77dd79..fa5e30682f 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.c +++ b/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.c @@ -154,7 +154,7 @@ void XonoticServerInfoDialog_loadServerInfo(entity me, float i) s = gethostcachestring(SLIST_FIELD_MOD, i); if(s != "data") - modname = sprintf(_("%s (%s)"), modname, s); + modname = sprintf("%s (%s)", modname, s); j = MapInfo_Type_FromString(typestr); // try and get the real name of the game type if(j) { typestr = MapInfo_Type_ToText(j); } // only set it if we actually found it @@ -171,7 +171,7 @@ void XonoticServerInfoDialog_loadServerInfo(entity me, float i) numh = gethostcachenumber(SLIST_FIELD_NUMHUMANS, i); maxp = gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i); numb = gethostcachenumber(SLIST_FIELD_NUMBOTS, i); - me.currentServerNumPlayers = strzone(sprintf(_("%d/%d"), numh, maxp)); + me.currentServerNumPlayers = strzone(sprintf("%d/%d", numh, maxp)); me.numPlayersLabel.setText(me.numPlayersLabel, me.currentServerNumPlayers); s = ftos(numb); @@ -190,7 +190,7 @@ void XonoticServerInfoDialog_loadServerInfo(entity me, float i) me.currentServerVersion = strzone(versionstr); me.versionLabel.setText(me.versionLabel, me.currentServerVersion); - me.currentServerPure = ((pure < 0) ? "N/A" : (pure == 0) ? _("Official") : sprintf(_("%d modified"), pure)); + me.currentServerPure = ((pure < 0) ? _("N/A") : (pure == 0) ? _("Official") : sprintf(_("%d modified"), pure)); me.currentServerPure = strzone(me.currentServerPure); me.pureLabel.setText(me.pureLabel, me.currentServerPure); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_playersetup.c b/qcsrc/menu/xonotic/dialog_multiplayer_playersetup.c index f4bd359ece..fd4e5cd19b 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_playersetup.c +++ b/qcsrc/menu/xonotic/dialog_multiplayer_playersetup.c @@ -22,7 +22,7 @@ entity makeXonoticPlayerSettingsTab() } void XonoticPlayerSettingsTab_draw(entity me) { - if(cvar_string("_cl_name") == "Player") + if(cvar_string("_cl_name") == cvar_defstring("_cl_name")) me.playerNameLabel.alpha = ((mod(time * 2, 2) < 1) ? 1 : 0); else me.playerNameLabel.alpha = me.playerNameLabelAlpha; diff --git a/qcsrc/menu/xonotic/keybinder.c b/qcsrc/menu/xonotic/keybinder.c index 66c3486a3a..57f501f5c1 100644 --- a/qcsrc/menu/xonotic/keybinder.c +++ b/qcsrc/menu/xonotic/keybinder.c @@ -240,13 +240,13 @@ void KeyBinder_Bind_Clear(entity btn, entity me) } void XonoticKeyBinder_clickListBoxItem(entity me, float i, vector where) { - if(i == me.lastClickedServer) + if(i == me.lastClickedKey) if(time < me.lastClickedTime + 0.3) { // DOUBLE CLICK! KeyBinder_Bind_Change(NULL, me); } - me.lastClickedServer = i; + me.lastClickedKey = i; me.lastClickedTime = time; } void XonoticKeyBinder_setSelected(entity me, float i) diff --git a/qcsrc/menu/xonotic/mainwindow.c b/qcsrc/menu/xonotic/mainwindow.c index 5c58025fca..dcc4a8a817 100644 --- a/qcsrc/menu/xonotic/mainwindow.c +++ b/qcsrc/menu/xonotic/mainwindow.c @@ -256,7 +256,7 @@ void MainWindow_configureMainWindow(entity me) me.initializeDialog(me, n); - if(cvar_string("_cl_name") == "Player") + if(cvar_string("_cl_name") == cvar_defstring("_cl_name")) me.dialogToShow = me.firstRunDialog; } #endif diff --git a/qcsrc/menu/xonotic/slider_decibels.c b/qcsrc/menu/xonotic/slider_decibels.c index 3f1c468272..bf847e3126 100644 --- a/qcsrc/menu/xonotic/slider_decibels.c +++ b/qcsrc/menu/xonotic/slider_decibels.c @@ -78,11 +78,11 @@ string XonoticDecibelsSlider_valueToText(entity me, float v) else if(v <= me.valueMin) return CTX(_("VOL^OFF")); else if(autocvar_menu_snd_sliderscale == 3) // fake percent scale - return sprintf(_("%d %%"), (v - me.valueMin) / (me.valueMax - me.valueMin) * 100); + return sprintf("%d %%", (v - me.valueMin) / (me.valueMax - me.valueMin) * 100); else if(autocvar_menu_snd_sliderscale == 2) // 0..10 scale - return sprintf(_("%.1f"), (v - me.valueMin) / (me.valueMax - me.valueMin) * 10); + return sprintf("%.1f", (v - me.valueMin) / (me.valueMax - me.valueMin) * 10); else if(autocvar_menu_snd_sliderscale == 1) // real percent scale - return sprintf(_("%.2f %%"), fromDecibelOfSquare(v, me.valueMin) * 100); + return sprintf("%.2f %%", fromDecibelOfSquare(v, me.valueMin) * 100); else // decibel scale return sprintf(_("%s dB"), ftos_decimals(toDecibelOfSquare(fromDecibelOfSquare(v, me.valueMin), 0), me.valueDigits)); } diff --git a/qcsrc/menu/xonotic/util.qc b/qcsrc/menu/xonotic/util.qc index 74b60d30ae..7c46564f45 100644 --- a/qcsrc/menu/xonotic/util.qc +++ b/qcsrc/menu/xonotic/util.qc @@ -305,17 +305,17 @@ void UpdateNotification_URI_Get_Callback(float id, float status, string data) } if(status != 0) { - printf(_("error receiving update notification: status is %d\n"), status); + dprintf("error receiving update notification: status is %d\n", status); return; } if(substring(data, 0, 1) == "<") { - print(_("error: received HTML instead of an update notification\n")); + dprint("error: received HTML instead of an update notification\n"); return; } if(strstrofs(data, "\r", 0) != -1) { - print(_("error: received carriage returns from update notification server\n")); + dprint("error: received carriage returns from update notification server\n"); return; } @@ -343,7 +343,6 @@ void UpdateNotification_URI_Get_Callback(float id, float status, string data) switch(substring(argv(i), 0, 1)) { - #define APPEND_TO_STRING(list,sep,add) ((list) = (((list) != "") ? strcat(list, sep, add) : (add))) case "V": { un_version = s; diff --git a/qcsrc/server/accuracy.qc b/qcsrc/server/accuracy.qc deleted file mode 100644 index ca837e7d38..0000000000 --- a/qcsrc/server/accuracy.qc +++ /dev/null @@ -1,129 +0,0 @@ -.float accuracy_hit[WEP_MAXCOUNT]; -.float accuracy_fired[WEP_MAXCOUNT]; -.float accuracy_cnt_hit[WEP_MAXCOUNT]; -.float accuracy_cnt_fired[WEP_MAXCOUNT]; - -float accuracy_byte(float n, float d) -{ - //printf("accuracy: %d / %d\n", n, d); - if(n <= 0) - return 0; - if(n > d) - return 255; - return 1 + rint(n * 100.0 / d); -} - -float accuracy_send(entity to, float sf) -{ - float w, f; - entity a; - WriteByte(MSG_ENTITY, ENT_CLIENT_ACCURACY); - - a = self.owner; - if(IS_SPEC(a)) - a = a.enemy; - a = a.accuracy; - - if(to != a.owner) - if (!(self.owner.cvar_cl_accuracy_data_share && autocvar_sv_accuracy_data_share)) - sf = 0; - // note: zero sendflags can never be sent... so we can use that to say that we send no accuracy! - WriteInt24_t(MSG_ENTITY, sf); - if(sf == 0) - return TRUE; - // note: we know that client and server agree about SendFlags... - for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w) - { - if(sf & f) - WriteByte(MSG_ENTITY, accuracy_byte(self.(accuracy_hit[w]), self.(accuracy_fired[w]))); - if(f == 0x800000) - f = 1; - else - f *= 2; - } - return TRUE; -} - -// init/free -void accuracy_init(entity e) -{ - e.accuracy = spawn(); - e.accuracy.owner = e; - e.accuracy.classname = "accuracy"; - e.accuracy.drawonlytoclient = e; - Net_LinkEntity(e.accuracy, FALSE, 0, accuracy_send); -} - -void accuracy_free(entity e) -{ - remove(e.accuracy); -} - -// force a resend of a player's accuracy stats -void accuracy_resend(entity e) -{ - e.accuracy.SendFlags = 0xFFFFFF; -} - -// update accuracy stats -.float hit_time; -.float fired_time; - -void accuracy_add(entity e, float w, float fired, float hit) -{ - entity a; - float b; - if(IS_INDEPENDENT_PLAYER(e)) - return; - a = e.accuracy; - if(!a || !(hit || fired)) - return; - w -= WEP_FIRST; - b = accuracy_byte(a.(accuracy_hit[w]), a.(accuracy_fired[w])); - if(hit) - a.(accuracy_hit[w]) += hit; - if(fired) - a.(accuracy_fired[w]) += fired; - - if(hit && a.hit_time != time) // only run this once per frame - { - a.(accuracy_cnt_hit[w]) += 1; - a.hit_time = time; - } - - if(fired && a.fired_time != time) // only run this once per frame - { - a.(accuracy_cnt_fired[w]) += 1; - a.fired_time = time; - } - - if(b == accuracy_byte(a.(accuracy_hit[w]), a.(accuracy_fired[w]))) - return; - w = pow(2, mod(w, 24)); - a.SendFlags |= w; - FOR_EACH_CLIENT(a) - if(IS_SPEC(a)) - if(a.enemy == e) - a.SendFlags |= w; -} - -float accuracy_isgooddamage(entity attacker, entity targ) -{ - frag_attacker = attacker; - frag_target = targ; - float mutator_check = MUTATOR_CALLHOOK(AccuracyTargetValid); - - if(!warmup_stage) - if(targ.deadflag == DEAD_NO) - if(mutator_check == MUT_ACCADD_INVALID || (mutator_check == MUT_ACCADD_VALID && IS_CLIENT(targ))) - if(DIFF_TEAM(attacker, targ)) - return TRUE; - return FALSE; -} - -float accuracy_canbegooddamage(entity attacker) -{ - if(!warmup_stage) - return TRUE; - return FALSE; -} diff --git a/qcsrc/server/accuracy.qh b/qcsrc/server/accuracy.qh deleted file mode 100644 index 90dbb663de..0000000000 --- a/qcsrc/server/accuracy.qh +++ /dev/null @@ -1,15 +0,0 @@ -.float cvar_cl_accuracy_data_share; - -// init/free -void accuracy_init(entity e); -void accuracy_free(entity e); - -// force a resend of a player's accuracy stats -void accuracy_resend(entity e); - -// update accuracy stats -void accuracy_add(entity e, float w, float fired, float hit); - -// helper -float accuracy_isgooddamage(entity attacker, entity targ); -float accuracy_canbegooddamage(entity attacker); diff --git a/qcsrc/server/antilag.qh b/qcsrc/server/antilag.qh index cadb45a14d..b89f12bb15 100644 --- a/qcsrc/server/antilag.qh +++ b/qcsrc/server/antilag.qh @@ -5,5 +5,7 @@ void antilag_takeback(entity e, float t); void antilag_restore(entity e); void antilag_clear(entity e); +.float antilag_debug; + #define ANTILAG_LATENCY(e) min(0.4, e.ping * 0.001) // add one ticrate? diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index 4d207955ca..bb14c1315e 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -68,7 +68,7 @@ float autocvar_developer_fteqccbugs; float autocvar_ekg; #define autocvar_fraglimit cvar("fraglimit") #define autocvar_fraglimit_override cvar("fraglimit_override") -float autocvar_g_allow_oldnexbeam; +float autocvar_g_allow_oldvortexbeam; float autocvar_g_antilag; float autocvar_g_antilag_nudge; float autocvar_g_balance_armor_blockpercent; @@ -80,33 +80,6 @@ float autocvar_g_balance_armor_rot; float autocvar_g_balance_armor_rotlinear; float autocvar_g_balance_armor_rotstable; float autocvar_g_balance_armor_start; -float autocvar_g_balance_rifle_bursttime; -float autocvar_g_balance_rifle_primary_ammo; -float autocvar_g_balance_rifle_primary_animtime; -float autocvar_g_balance_rifle_primary_bullethail; -float autocvar_g_balance_rifle_primary_burstcost; -float autocvar_g_balance_rifle_primary_damage; -float autocvar_g_balance_rifle_primary_force; -float autocvar_g_balance_rifle_primary_refire; -float autocvar_g_balance_rifle_primary_shots; -float autocvar_g_balance_rifle_primary_solidpenetration; -float autocvar_g_balance_rifle_primary_spread; -float autocvar_g_balance_rifle_primary_tracer; -float autocvar_g_balance_rifle_secondary; -float autocvar_g_balance_rifle_secondary_ammo; -float autocvar_g_balance_rifle_secondary_animtime; -float autocvar_g_balance_rifle_secondary_bullethail; -float autocvar_g_balance_rifle_secondary_burstcost; -float autocvar_g_balance_rifle_secondary_damage; -float autocvar_g_balance_rifle_secondary_force; -float autocvar_g_balance_rifle_secondary_reload; -float autocvar_g_balance_rifle_secondary_refire; -float autocvar_g_balance_rifle_secondary_shots; -float autocvar_g_balance_rifle_secondary_solidpenetration; -float autocvar_g_balance_rifle_secondary_spread; -float autocvar_g_balance_rifle_secondary_tracer; -float autocvar_g_balance_rifle_reload_ammo; -float autocvar_g_balance_rifle_reload_time; float autocvar_g_balance_cloaked_alpha; float autocvar_g_balance_contents_damagerate; float autocvar_g_balance_contents_drowndelay; @@ -114,134 +87,11 @@ float autocvar_g_balance_contents_playerdamage_drowning; float autocvar_g_balance_contents_playerdamage_lava; float autocvar_g_balance_contents_playerdamage_slime; float autocvar_g_balance_contents_projectiledamage; -float autocvar_g_balance_crylink_primary_ammo; -float autocvar_g_balance_crylink_primary_animtime; -float autocvar_g_balance_crylink_primary_bouncedamagefactor; -float autocvar_g_balance_crylink_primary_bounces; -float autocvar_g_balance_crylink_primary_damage; -float autocvar_g_balance_crylink_primary_edgedamage; -float autocvar_g_balance_crylink_primary_force; -float autocvar_g_balance_crylink_primary_joindelay; -float autocvar_g_balance_crylink_primary_joinexplode; -float autocvar_g_balance_crylink_primary_joinexplode_damage; -float autocvar_g_balance_crylink_primary_joinexplode_edgedamage; -float autocvar_g_balance_crylink_primary_joinexplode_force; -float autocvar_g_balance_crylink_primary_joinexplode_radius; -float autocvar_g_balance_crylink_primary_joinspread; -float autocvar_g_balance_crylink_primary_linkexplode; -float autocvar_g_balance_crylink_primary_middle_fadetime; -float autocvar_g_balance_crylink_primary_middle_lifetime; -float autocvar_g_balance_crylink_primary_other_fadetime; -float autocvar_g_balance_crylink_primary_other_lifetime; -float autocvar_g_balance_crylink_primary_radius; -float autocvar_g_balance_crylink_primary_refire; -float autocvar_g_balance_crylink_primary_shots; -float autocvar_g_balance_crylink_primary_speed; -float autocvar_g_balance_crylink_primary_spread; -float autocvar_g_balance_crylink_secondary; -float autocvar_g_balance_crylink_secondary_ammo; -float autocvar_g_balance_crylink_secondary_animtime; -float autocvar_g_balance_crylink_secondary_bouncedamagefactor; -float autocvar_g_balance_crylink_secondary_bounces; -float autocvar_g_balance_crylink_secondary_damage; -float autocvar_g_balance_crylink_secondary_edgedamage; -float autocvar_g_balance_crylink_secondary_force; -float autocvar_g_balance_crylink_secondary_joindelay; -float autocvar_g_balance_crylink_secondary_joinexplode; -float autocvar_g_balance_crylink_secondary_joinexplode_damage; -float autocvar_g_balance_crylink_secondary_joinexplode_edgedamage; -float autocvar_g_balance_crylink_secondary_joinexplode_force; -float autocvar_g_balance_crylink_secondary_joinexplode_radius; -float autocvar_g_balance_crylink_secondary_joinspread; -float autocvar_g_balance_crylink_secondary_line_fadetime; -float autocvar_g_balance_crylink_secondary_line_lifetime; -float autocvar_g_balance_crylink_secondary_linkexplode; -float autocvar_g_balance_crylink_secondary_middle_fadetime; -float autocvar_g_balance_crylink_secondary_middle_lifetime; -float autocvar_g_balance_crylink_secondary_radius; -float autocvar_g_balance_crylink_secondary_refire; -float autocvar_g_balance_crylink_secondary_shots; -float autocvar_g_balance_crylink_secondary_speed; -float autocvar_g_balance_crylink_secondary_spread; -float autocvar_g_balance_crylink_secondary_spreadtype; -float autocvar_g_balance_crylink_reload_ammo; -float autocvar_g_balance_crylink_reload_time; float autocvar_g_balance_damagepush_speedfactor; -float autocvar_g_balance_electro_combo_comboradius; -float autocvar_g_balance_electro_combo_damage; -float autocvar_g_balance_electro_combo_edgedamage; -float autocvar_g_balance_electro_combo_force; -float autocvar_g_balance_electro_combo_radius; -float autocvar_g_balance_electro_combo_speed; -float autocvar_g_balance_electro_combo_safeammocheck; -float autocvar_g_balance_electro_lightning; -float autocvar_g_balance_electro_primary_ammo; -float autocvar_g_balance_electro_primary_animtime; -float autocvar_g_balance_electro_primary_comboradius; -float autocvar_g_balance_electro_primary_damage; -float autocvar_g_balance_electro_primary_edgedamage; -float autocvar_g_balance_electro_primary_falloff_halflifedist; -float autocvar_g_balance_electro_primary_falloff_maxdist; -float autocvar_g_balance_electro_primary_falloff_mindist; -float autocvar_g_balance_electro_primary_force; -float autocvar_g_balance_electro_primary_force_up; -float autocvar_g_balance_electro_primary_lifetime; -float autocvar_g_balance_electro_primary_radius; -float autocvar_g_balance_electro_primary_range; -float autocvar_g_balance_electro_primary_refire; -float autocvar_g_balance_electro_primary_speed; -float autocvar_g_balance_electro_secondary_ammo; -float autocvar_g_balance_electro_secondary_animtime; -float autocvar_g_balance_electro_secondary_bouncefactor; -float autocvar_g_balance_electro_secondary_bouncestop; -float autocvar_g_balance_electro_secondary_count; -float autocvar_g_balance_electro_secondary_damage; -float autocvar_g_balance_electro_secondary_damageforcescale; -float autocvar_g_balance_electro_secondary_damagedbycontents; -float autocvar_g_balance_electro_secondary_edgedamage; -float autocvar_g_balance_electro_secondary_force; -float autocvar_g_balance_electro_secondary_health; -float autocvar_g_balance_electro_secondary_lifetime; -float autocvar_g_balance_electro_secondary_radius; -float autocvar_g_balance_electro_secondary_refire; -float autocvar_g_balance_electro_secondary_refire2; -float autocvar_g_balance_electro_secondary_speed; -float autocvar_g_balance_electro_reload_ammo; -float autocvar_g_balance_electro_reload_time; float autocvar_g_balance_falldamage_deadminspeed; float autocvar_g_balance_falldamage_factor; float autocvar_g_balance_falldamage_maxdamage; float autocvar_g_balance_falldamage_minspeed; -float autocvar_g_balance_fireball_primary_animtime; -float autocvar_g_balance_fireball_primary_bfgdamage; -float autocvar_g_balance_fireball_primary_bfgforce; -float autocvar_g_balance_fireball_primary_bfgradius; -float autocvar_g_balance_fireball_primary_damage; -float autocvar_g_balance_fireball_primary_damageforcescale; -float autocvar_g_balance_fireball_primary_edgedamage; -float autocvar_g_balance_fireball_primary_force; -float autocvar_g_balance_fireball_primary_health; -float autocvar_g_balance_fireball_primary_laserburntime; -float autocvar_g_balance_fireball_primary_laserdamage; -float autocvar_g_balance_fireball_primary_laseredgedamage; -float autocvar_g_balance_fireball_primary_laserradius; -float autocvar_g_balance_fireball_primary_lifetime; -float autocvar_g_balance_fireball_primary_radius; -float autocvar_g_balance_fireball_primary_refire; -float autocvar_g_balance_fireball_primary_refire2; -float autocvar_g_balance_fireball_primary_speed; -float autocvar_g_balance_fireball_secondary_animtime; -float autocvar_g_balance_fireball_secondary_damage; -float autocvar_g_balance_fireball_secondary_damageforcescale; -float autocvar_g_balance_fireball_secondary_damagetime; -float autocvar_g_balance_fireball_secondary_laserburntime; -float autocvar_g_balance_fireball_secondary_laserdamage; -float autocvar_g_balance_fireball_secondary_laseredgedamage; -float autocvar_g_balance_fireball_secondary_laserradius; -float autocvar_g_balance_fireball_secondary_lifetime; -float autocvar_g_balance_fireball_secondary_refire; -float autocvar_g_balance_fireball_secondary_speed; -float autocvar_g_balance_fireball_secondary_speed_up; float autocvar_g_balance_firetransfer_damage; float autocvar_g_balance_firetransfer_time; float autocvar_g_balance_fuel_limit; @@ -261,75 +111,6 @@ float autocvar_g_balance_grapplehook_speed_pull; float autocvar_g_balance_grapplehook_stretch; float autocvar_g_balance_grapplehook_damagedbycontents; float autocvar_g_balance_grapplehook_refire; -float autocvar_g_balance_grenadelauncher_bouncefactor; -float autocvar_g_balance_grenadelauncher_bouncestop; -float autocvar_g_balance_grenadelauncher_primary_ammo; -float autocvar_g_balance_grenadelauncher_primary_animtime; -float autocvar_g_balance_grenadelauncher_primary_damage; -float autocvar_g_balance_grenadelauncher_primary_damageforcescale; -float autocvar_g_balance_grenadelauncher_primary_edgedamage; -float autocvar_g_balance_grenadelauncher_primary_force; -float autocvar_g_balance_grenadelauncher_primary_health; -float autocvar_g_balance_grenadelauncher_primary_lifetime; -float autocvar_g_balance_grenadelauncher_primary_lifetime_stick; -float autocvar_g_balance_grenadelauncher_primary_radius; -float autocvar_g_balance_grenadelauncher_primary_refire; -float autocvar_g_balance_grenadelauncher_primary_remote_minbouncecnt; -float autocvar_g_balance_grenadelauncher_primary_speed; -float autocvar_g_balance_grenadelauncher_primary_speed_up; -float autocvar_g_balance_grenadelauncher_primary_type; -float autocvar_g_balance_grenadelauncher_secondary_ammo; -float autocvar_g_balance_grenadelauncher_secondary_animtime; -float autocvar_g_balance_grenadelauncher_secondary_damage; -float autocvar_g_balance_grenadelauncher_secondary_damageforcescale; -float autocvar_g_balance_grenadelauncher_secondary_edgedamage; -float autocvar_g_balance_grenadelauncher_secondary_force; -float autocvar_g_balance_grenadelauncher_secondary_health; -float autocvar_g_balance_grenadelauncher_secondary_lifetime; -float autocvar_g_balance_grenadelauncher_secondary_lifetime_bounce; -float autocvar_g_balance_grenadelauncher_secondary_lifetime_stick; -float autocvar_g_balance_grenadelauncher_secondary_radius; -float autocvar_g_balance_grenadelauncher_secondary_refire; -float autocvar_g_balance_grenadelauncher_secondary_speed; -float autocvar_g_balance_grenadelauncher_secondary_speed_up; -float autocvar_g_balance_grenadelauncher_secondary_type; -float autocvar_g_balance_grenadelauncher_reload_ammo; -float autocvar_g_balance_grenadelauncher_reload_time; -float autocvar_g_balance_hagar_primary_ammo; -float autocvar_g_balance_hagar_primary_damage; -float autocvar_g_balance_hagar_primary_edgedamage; -float autocvar_g_balance_hagar_primary_force; -float autocvar_g_balance_hagar_primary_health; -float autocvar_g_balance_hagar_primary_damageforcescale; -float autocvar_g_balance_hagar_primary_lifetime; -float autocvar_g_balance_hagar_primary_radius; -float autocvar_g_balance_hagar_primary_refire; -float autocvar_g_balance_hagar_primary_speed; -float autocvar_g_balance_hagar_secondary; -float autocvar_g_balance_hagar_secondary_load; -float autocvar_g_balance_hagar_secondary_load_speed; -float autocvar_g_balance_hagar_secondary_load_spread; -float autocvar_g_balance_hagar_secondary_load_spread_bias; -float autocvar_g_balance_hagar_secondary_load_max; -float autocvar_g_balance_hagar_secondary_load_hold; -float autocvar_g_balance_hagar_secondary_load_releasedeath; -float autocvar_g_balance_hagar_secondary_load_abort; -float autocvar_g_balance_hagar_secondary_load_linkexplode; -float autocvar_g_balance_hagar_secondary_load_animtime; -float autocvar_g_balance_hagar_secondary_ammo; -float autocvar_g_balance_hagar_secondary_damage; -float autocvar_g_balance_hagar_secondary_edgedamage; -float autocvar_g_balance_hagar_secondary_force; -float autocvar_g_balance_hagar_secondary_health; -float autocvar_g_balance_hagar_secondary_damageforcescale; -float autocvar_g_balance_hagar_secondary_lifetime_min; -float autocvar_g_balance_hagar_secondary_lifetime_rand; -float autocvar_g_balance_hagar_secondary_radius; -float autocvar_g_balance_hagar_secondary_refire; -float autocvar_g_balance_hagar_secondary_speed; -float autocvar_g_balance_hagar_secondary_spread; -float autocvar_g_balance_hagar_reload_ammo; -float autocvar_g_balance_hagar_reload_time; float autocvar_g_balance_health_limit; float autocvar_g_balance_health_regen; float autocvar_g_balance_health_regenlinear; @@ -337,54 +118,6 @@ float autocvar_g_balance_health_regenstable; float autocvar_g_balance_health_rot; float autocvar_g_balance_health_rotlinear; float autocvar_g_balance_health_rotstable; -float autocvar_g_balance_hlac_primary_ammo; -float autocvar_g_balance_hlac_primary_animtime; -float autocvar_g_balance_hlac_primary_damage; -float autocvar_g_balance_hlac_primary_edgedamage; -float autocvar_g_balance_hlac_primary_force; -float autocvar_g_balance_hlac_primary_lifetime; -float autocvar_g_balance_hlac_primary_radius; -float autocvar_g_balance_hlac_primary_refire; -float autocvar_g_balance_hlac_primary_speed; -float autocvar_g_balance_hlac_primary_spread_add; -float autocvar_g_balance_hlac_primary_spread_crouchmod; -float autocvar_g_balance_hlac_primary_spread_max; -float autocvar_g_balance_hlac_primary_spread_min; -float autocvar_g_balance_hlac_secondary; -float autocvar_g_balance_hlac_secondary_ammo; -float autocvar_g_balance_hlac_secondary_animtime; -float autocvar_g_balance_hlac_secondary_damage; -float autocvar_g_balance_hlac_secondary_edgedamage; -float autocvar_g_balance_hlac_secondary_force; -float autocvar_g_balance_hlac_secondary_lifetime; -float autocvar_g_balance_hlac_secondary_radius; -float autocvar_g_balance_hlac_secondary_refire; -float autocvar_g_balance_hlac_secondary_shots; -float autocvar_g_balance_hlac_secondary_speed; -float autocvar_g_balance_hlac_secondary_spread; -float autocvar_g_balance_hlac_secondary_spread_crouchmod; -float autocvar_g_balance_hlac_reload_ammo; -float autocvar_g_balance_hlac_reload_time; -float autocvar_g_balance_hook_primary_animtime; -float autocvar_g_balance_hook_primary_fuel; -float autocvar_g_balance_hook_primary_hooked_fuel; -float autocvar_g_balance_hook_primary_hooked_time_free; -float autocvar_g_balance_hook_primary_hooked_time_max; -float autocvar_g_balance_hook_primary_refire; -float autocvar_g_balance_hook_secondary_ammo; -float autocvar_g_balance_hook_secondary_animtime; -float autocvar_g_balance_hook_secondary_damage; -float autocvar_g_balance_hook_secondary_duration; -float autocvar_g_balance_hook_secondary_edgedamage; -float autocvar_g_balance_hook_secondary_force; -float autocvar_g_balance_hook_secondary_gravity; -float autocvar_g_balance_hook_secondary_lifetime; -float autocvar_g_balance_hook_secondary_power; -float autocvar_g_balance_hook_secondary_radius; -float autocvar_g_balance_hook_secondary_refire; -float autocvar_g_balance_hook_secondary_speed; -float autocvar_g_balance_hook_secondary_health; -float autocvar_g_balance_hook_secondary_damageforcescale; float autocvar_g_balance_keyhunt_damageforcescale; float autocvar_g_balance_keyhunt_delay_collect; float autocvar_g_balance_keyhunt_delay_return; @@ -402,102 +135,6 @@ float autocvar_g_balance_keyhunt_score_push; float autocvar_g_balance_keyhunt_throwvelocity; float autocvar_g_balance_kill_delay; float autocvar_g_balance_kill_antispam; -float autocvar_g_balance_laser_primary_animtime; -float autocvar_g_balance_laser_primary_damage; -float autocvar_g_balance_laser_primary_delay; -float autocvar_g_balance_laser_primary_edgedamage; -float autocvar_g_balance_laser_primary_force; -float autocvar_g_balance_laser_primary_force_other_scale; -float autocvar_g_balance_laser_primary_force_velocitybias; -float autocvar_g_balance_laser_primary_force_zscale; -float autocvar_g_balance_laser_primary_lifetime; -float autocvar_g_balance_laser_primary_radius; -float autocvar_g_balance_laser_primary_refire; -float autocvar_g_balance_laser_primary_shotangle; -float autocvar_g_balance_laser_primary_speed; -float autocvar_g_balance_laser_secondary; -float autocvar_g_balance_laser_secondary_animtime; -float autocvar_g_balance_laser_secondary_damage; -float autocvar_g_balance_laser_secondary_edgedamage; -float autocvar_g_balance_laser_secondary_force; -float autocvar_g_balance_laser_secondary_force_other_scale; -float autocvar_g_balance_laser_secondary_force_velocitybias; -float autocvar_g_balance_laser_secondary_force_zscale; -float autocvar_g_balance_laser_secondary_lifetime; -float autocvar_g_balance_laser_secondary_radius; -float autocvar_g_balance_laser_secondary_speed; -float autocvar_g_balance_laser_reload_ammo; -float autocvar_g_balance_laser_reload_time; -float autocvar_g_balance_minelayer_ammo; -float autocvar_g_balance_minelayer_animtime; -float autocvar_g_balance_minelayer_damage; -float autocvar_g_balance_minelayer_damageforcescale; -float autocvar_g_balance_minelayer_detonatedelay; -float autocvar_g_balance_minelayer_edgedamage; -float autocvar_g_balance_minelayer_force; -float autocvar_g_balance_minelayer_health; -float autocvar_g_balance_minelayer_lifetime; -float autocvar_g_balance_minelayer_lifetime_countdown; -float autocvar_g_balance_minelayer_limit; -float autocvar_g_balance_minelayer_protection; -float autocvar_g_balance_minelayer_proximityradius; -float autocvar_g_balance_minelayer_radius; -float autocvar_g_balance_minelayer_refire; -float autocvar_g_balance_minelayer_remote_damage; -float autocvar_g_balance_minelayer_remote_edgedamage; -float autocvar_g_balance_minelayer_remote_force; -float autocvar_g_balance_minelayer_remote_radius; -float autocvar_g_balance_minelayer_speed; -float autocvar_g_balance_minelayer_time; -float autocvar_g_balance_minelayer_reload_ammo; -float autocvar_g_balance_minelayer_reload_time; -float autocvar_g_balance_minstanex_ammo; -float autocvar_g_balance_minstanex_laser_ammo; -float autocvar_g_balance_minstanex_laser_animtime; -float autocvar_g_balance_minstanex_laser_refire; -float autocvar_g_balance_minstanex_animtime; -float autocvar_g_balance_minstanex_refire; -float autocvar_g_balance_minstanex_reload_ammo; -float autocvar_g_balance_minstanex_reload_time; -float autocvar_g_balance_nex_charge; -float autocvar_g_balance_nex_charge_animlimit; -float autocvar_g_balance_nex_charge_limit; -float autocvar_g_balance_nex_charge_maxspeed; -float autocvar_g_balance_nex_charge_mindmg; -float autocvar_g_balance_nex_charge_minspeed; -float autocvar_g_balance_nex_charge_rate; -float autocvar_g_balance_nex_charge_rot_pause; -float autocvar_g_balance_nex_charge_rot_rate; -float autocvar_g_balance_nex_charge_shot_multiplier; -float autocvar_g_balance_nex_charge_start; -float autocvar_g_balance_nex_charge_velocity_rate; -float autocvar_g_balance_nex_primary_ammo; -float autocvar_g_balance_nex_primary_animtime; -float autocvar_g_balance_nex_primary_damage; -float autocvar_g_balance_nex_primary_damagefalloff_forcehalflife; -float autocvar_g_balance_nex_primary_damagefalloff_halflife; -float autocvar_g_balance_nex_primary_damagefalloff_maxdist; -float autocvar_g_balance_nex_primary_damagefalloff_mindist; -float autocvar_g_balance_nex_primary_force; -float autocvar_g_balance_nex_primary_refire; -float autocvar_g_balance_nex_secondary; -float autocvar_g_balance_nex_secondary_ammo; -float autocvar_g_balance_nex_secondary_animtime; -float autocvar_g_balance_nex_secondary_charge; -float autocvar_g_balance_nex_secondary_charge_rate; -float autocvar_g_balance_nex_secondary_chargepool; -float autocvar_g_balance_nex_secondary_chargepool_pause_health_regen; -float autocvar_g_balance_nex_secondary_chargepool_pause_regen; -float autocvar_g_balance_nex_secondary_chargepool_regen; -float autocvar_g_balance_nex_secondary_damage; -float autocvar_g_balance_nex_secondary_damagefalloff_forcehalflife; -float autocvar_g_balance_nex_secondary_damagefalloff_halflife; -float autocvar_g_balance_nex_secondary_damagefalloff_maxdist; -float autocvar_g_balance_nex_secondary_damagefalloff_mindist; -float autocvar_g_balance_nex_secondary_force; -float autocvar_g_balance_nex_secondary_refire; -float autocvar_g_balance_nex_reload_ammo; -float autocvar_g_balance_nex_reload_time; float autocvar_g_balance_nexball_primary_animtime; float autocvar_g_balance_nexball_primary_refire; float autocvar_g_balance_nexball_primary_speed; @@ -507,11 +144,13 @@ float autocvar_g_balance_nexball_secondary_lifetime; float autocvar_g_balance_nexball_secondary_refire; float autocvar_g_balance_nexball_secondary_speed; float autocvar_g_balance_nix_ammo_cells; +float autocvar_g_balance_nix_ammo_plasma; float autocvar_g_balance_nix_ammo_fuel; float autocvar_g_balance_nix_ammo_nails; float autocvar_g_balance_nix_ammo_rockets; float autocvar_g_balance_nix_ammo_shells; float autocvar_g_balance_nix_ammoincr_cells; +float autocvar_g_balance_nix_ammoincr_plasma; float autocvar_g_balance_nix_ammoincr_fuel; float autocvar_g_balance_nix_ammoincr_nails; float autocvar_g_balance_nix_ammoincr_rockets; @@ -529,15 +168,6 @@ float autocvar_g_balance_pause_health_rot; float autocvar_g_balance_pause_health_rot_spawn; float autocvar_g_balance_portal_health; float autocvar_g_balance_portal_lifetime; -float autocvar_g_balance_porto_primary_animtime; -float autocvar_g_balance_porto_primary_lifetime; -float autocvar_g_balance_porto_primary_refire; -float autocvar_g_balance_porto_primary_speed; -float autocvar_g_balance_porto_secondary; -float autocvar_g_balance_porto_secondary_animtime; -float autocvar_g_balance_porto_secondary_lifetime; -float autocvar_g_balance_porto_secondary_refire; -float autocvar_g_balance_porto_secondary_speed; float autocvar_g_balance_powerup_invincible_takedamage; float autocvar_g_balance_powerup_invincible_time; float autocvar_g_balance_powerup_strength_damage; @@ -546,133 +176,10 @@ float autocvar_g_balance_powerup_strength_selfdamage; float autocvar_g_balance_powerup_strength_selfforce; float autocvar_g_balance_powerup_strength_time; float autocvar_g_balance_superweapons_time; -float autocvar_g_balance_rocketlauncher_ammo; -float autocvar_g_balance_rocketlauncher_animtime; -float autocvar_g_balance_rocketlauncher_damage; -float autocvar_g_balance_rocketlauncher_damageforcescale; -float autocvar_g_balance_rocketlauncher_detonatedelay; -float autocvar_g_balance_rocketlauncher_edgedamage; -float autocvar_g_balance_rocketlauncher_force; -float autocvar_g_balance_rocketlauncher_guidedelay; -float autocvar_g_balance_rocketlauncher_guidegoal; -float autocvar_g_balance_rocketlauncher_guiderate; -float autocvar_g_balance_rocketlauncher_guideratedelay; -float autocvar_g_balance_rocketlauncher_guidestop; -float autocvar_g_balance_rocketlauncher_health; -float autocvar_g_balance_rocketlauncher_lifetime; -float autocvar_g_balance_rocketlauncher_radius; -float autocvar_g_balance_rocketlauncher_refire; -float autocvar_g_balance_rocketlauncher_remote_damage; -float autocvar_g_balance_rocketlauncher_remote_edgedamage; -float autocvar_g_balance_rocketlauncher_remote_force; -float autocvar_g_balance_rocketlauncher_remote_radius; -float autocvar_g_balance_rocketlauncher_speed; -float autocvar_g_balance_rocketlauncher_speedaccel; -float autocvar_g_balance_rocketlauncher_speedstart; -float autocvar_g_balance_rocketlauncher_reload_ammo; -float autocvar_g_balance_rocketlauncher_reload_time; -float autocvar_g_balance_seeker_type; -float autocvar_g_balance_seeker_flac_ammo; -float autocvar_g_balance_seeker_flac_animtime; -float autocvar_g_balance_seeker_flac_damage; -float autocvar_g_balance_seeker_flac_edgedamage; -float autocvar_g_balance_seeker_flac_force; -float autocvar_g_balance_seeker_flac_lifetime; -float autocvar_g_balance_seeker_flac_lifetime_rand; -float autocvar_g_balance_seeker_flac_radius; -float autocvar_g_balance_seeker_flac_refire; -float autocvar_g_balance_seeker_missile_accel; -float autocvar_g_balance_seeker_missile_ammo; -float autocvar_g_balance_seeker_missile_animtime; -float autocvar_g_balance_seeker_missile_count; -float autocvar_g_balance_seeker_missile_damage; -float autocvar_g_balance_seeker_missile_damageforcescale; -float autocvar_g_balance_seeker_missile_decel; -float autocvar_g_balance_seeker_missile_delay; -float autocvar_g_balance_seeker_missile_edgedamage; -float autocvar_g_balance_seeker_missile_force; -float autocvar_g_balance_seeker_missile_health; -float autocvar_g_balance_seeker_missile_lifetime; -float autocvar_g_balance_seeker_missile_proxy; -float autocvar_g_balance_seeker_missile_proxy_delay; -float autocvar_g_balance_seeker_missile_proxy_maxrange; -float autocvar_g_balance_seeker_missile_radius; -float autocvar_g_balance_seeker_missile_refire; -float autocvar_g_balance_seeker_missile_smart; -float autocvar_g_balance_seeker_missile_smart_mindist; -float autocvar_g_balance_seeker_missile_smart_trace_max; -float autocvar_g_balance_seeker_missile_smart_trace_min; -float autocvar_g_balance_seeker_missile_speed_max; -float autocvar_g_balance_seeker_missile_turnrate; -float autocvar_g_balance_seeker_tag_ammo; -float autocvar_g_balance_seeker_tag_animtime; -float autocvar_g_balance_seeker_tag_damageforcescale; -float autocvar_g_balance_seeker_tag_health; -float autocvar_g_balance_seeker_tag_lifetime; -float autocvar_g_balance_seeker_tag_refire; -float autocvar_g_balance_seeker_tag_speed; -float autocvar_g_balance_seeker_tag_tracker_lifetime; -float autocvar_g_balance_seeker_reload_ammo; -float autocvar_g_balance_seeker_reload_time; float autocvar_g_balance_selfdamagepercent; -float autocvar_g_balance_shotgun_primary_ammo; -float autocvar_g_balance_shotgun_primary_animtime; -float autocvar_g_balance_shotgun_primary_bullets; -float autocvar_g_balance_shotgun_primary_damage; -float autocvar_g_balance_shotgun_primary_force; -float autocvar_g_balance_shotgun_primary_refire; -float autocvar_g_balance_shotgun_primary_solidpenetration; -float autocvar_g_balance_shotgun_primary_spread; -float autocvar_g_balance_shotgun_secondary; -float autocvar_g_balance_shotgun_secondary_animtime; -float autocvar_g_balance_shotgun_secondary_damage; -float autocvar_g_balance_shotgun_secondary_force; -float autocvar_g_balance_shotgun_secondary_melee_delay; -float autocvar_g_balance_shotgun_secondary_melee_range; -float autocvar_g_balance_shotgun_secondary_melee_swing_side; -float autocvar_g_balance_shotgun_secondary_melee_swing_up; -float autocvar_g_balance_shotgun_secondary_melee_time; -float autocvar_g_balance_shotgun_secondary_melee_traces; -float autocvar_g_balance_shotgun_secondary_melee_no_doubleslap; -float autocvar_g_balance_shotgun_secondary_melee_nonplayerdamage; -float autocvar_g_balance_shotgun_secondary_melee_multihit; -float autocvar_g_balance_shotgun_secondary_refire; -float autocvar_g_balance_shotgun_reload_ammo; -float autocvar_g_balance_shotgun_reload_time; float autocvar_g_balance_teams; float autocvar_g_balance_teams_prevent_imbalance; float autocvar_g_balance_teams_scorefactor; -float autocvar_g_balance_tuba_animtime; -float autocvar_g_balance_tuba_attenuation; -float autocvar_g_balance_tuba_damage; -float autocvar_g_balance_tuba_edgedamage; -float autocvar_g_balance_tuba_force; -float autocvar_g_balance_tuba_radius; -float autocvar_g_balance_tuba_refire; -float autocvar_g_balance_uzi_burst; -float autocvar_g_balance_uzi_burst_ammo; -float autocvar_g_balance_uzi_burst_animtime; -float autocvar_g_balance_uzi_burst_refire; -float autocvar_g_balance_uzi_burst_refire2; -float autocvar_g_balance_uzi_burst_spread; -float autocvar_g_balance_uzi_first; -float autocvar_g_balance_uzi_first_ammo; -float autocvar_g_balance_uzi_first_damage; -float autocvar_g_balance_uzi_first_force; -float autocvar_g_balance_uzi_first_refire; -float autocvar_g_balance_uzi_first_spread; -float autocvar_g_balance_uzi_mode; -float autocvar_g_balance_uzi_solidpenetration; -float autocvar_g_balance_uzi_spread_add; -float autocvar_g_balance_uzi_spread_max; -float autocvar_g_balance_uzi_spread_min; -float autocvar_g_balance_uzi_sustained_ammo; -float autocvar_g_balance_uzi_sustained_damage; -float autocvar_g_balance_uzi_sustained_force; -float autocvar_g_balance_uzi_sustained_refire; -float autocvar_g_balance_uzi_sustained_spread; -float autocvar_g_balance_uzi_reload_ammo; -float autocvar_g_balance_uzi_reload_time; float autocvar_g_ballistics_density_corpse; float autocvar_g_ballistics_density_player; float autocvar_g_ballistics_mindistance; @@ -931,6 +438,7 @@ float autocvar_g_onslaught_cp_health; float autocvar_g_onslaught_cp_regen; float autocvar_g_onslaught_gen_health; float autocvar_g_pickup_cells_max; +float autocvar_g_pickup_plasma_max; float autocvar_g_pickup_fuel_max; float autocvar_g_pickup_items; float autocvar_g_pickup_nails_max; @@ -971,7 +479,6 @@ float autocvar_g_spawn_furthest; float autocvar_g_spawn_useallspawns; float autocvar_g_spawnpoints_auto_move_out_of_solid; #define autocvar_g_spawnshieldtime cvar("g_spawnshieldtime") -#define autocvar_g_start_weapon_laser cvar("g_start_weapon_laser") float autocvar_g_tdm_team_spawns; float autocvar_g_tdm_point_limit; float autocvar_g_tdm_point_leadlimit; @@ -1144,7 +651,7 @@ float autocvar_sv_maxairstrafespeed; float autocvar_sv_maxspeed; string autocvar_sv_motd; float autocvar_sv_precacheplayermodels; -float autocvar_sv_precacheweapons; +//float autocvar_sv_precacheweapons; // WEAPONTODO? float autocvar_sv_q3acompat_machineshotgunswap; float autocvar_sv_ready_restart; float autocvar_sv_ready_restart_after_countdown; diff --git a/qcsrc/server/bot/aim.qc b/qcsrc/server/bot/aim.qc index 61f4ab5e8f..0ae3312471 100644 --- a/qcsrc/server/bot/aim.qc +++ b/qcsrc/server/bot/aim.qc @@ -339,12 +339,12 @@ float bot_aim(float shotspeed, float shotspeedupward, float maxshottime, float a shotspeedupward *= g_weaponspeedfactor; if (!shotspeed) { - dprint("bot_aim: WARNING: weapon ", W_Name(self.weapon), " shotspeed is zero!\n"); + dprint("bot_aim: WARNING: weapon ", WEP_NAME(self.weapon), " shotspeed is zero!\n"); shotspeed = 1000000; } if (!maxshottime) { - dprint("bot_aim: WARNING: weapon ", W_Name(self.weapon), " maxshottime is zero!\n"); + dprint("bot_aim: WARNING: weapon ", WEP_NAME(self.weapon), " maxshottime is zero!\n"); maxshottime = 1; } makevectors(self.v_angle); diff --git a/qcsrc/server/bot/bot.qc b/qcsrc/server/bot/bot.qc index b93d87ad29..40b769ddd3 100644 --- a/qcsrc/server/bot/bot.qc +++ b/qcsrc/server/bot/bot.qc @@ -43,7 +43,7 @@ void bot_think() //self.bot_painintensity = self.bot_painintensity + self.bot_oldhealth - self.health; //self.bot_painintensity = bound(0, self.bot_painintensity, 100); - if (autocvar_g_campaign && !campaign_bots_may_start) + if (!IS_PLAYER(self) || (autocvar_g_campaign && !campaign_bots_may_start)) { self.bot_nextthink = time + 0.5; return; diff --git a/qcsrc/server/bot/havocbot/havocbot.qc b/qcsrc/server/bot/havocbot/havocbot.qc index 2e57eecb3b..2ce9706a2f 100644 --- a/qcsrc/server/bot/havocbot/havocbot.qc +++ b/qcsrc/server/bot/havocbot/havocbot.qc @@ -92,7 +92,7 @@ void havocbot_ai() if(self.weapons) { - weapon_action(self.weapon, WR_AIM); + WEP_ACTION(self.weapon, WR_AIM); if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(self)) { self.BUTTON_ATCK = FALSE; @@ -164,7 +164,7 @@ void havocbot_ai() for(i = WEP_FIRST; i <= WEP_LAST; ++i) { e = get_weaponinfo(i); - if ((self.weapons & WepSet_FromWeapon(i)) && (e.spawnflags & WEP_FLAG_RELOADABLE) && (self.weapon_load[i] < cvar(strcat("g_balance_", e.netname, "_reload_ammo")))) + if ((self.weapons & WepSet_FromWeapon(i)) && (e.spawnflags & WEP_FLAG_RELOADABLE) && (self.weapon_load[i] < e.reloading_ammo)) self.switchweapon = i; } } @@ -590,10 +590,10 @@ void havocbot_movetogoal() return; } - else if(self.health>autocvar_g_balance_rocketlauncher_damage*0.5) + else if(self.health>WEP_CVAR(devastator, damage)*0.5) { if(self.velocity_z < 0) - if(client_hasweapon(self, WEP_ROCKET_LAUNCHER, TRUE, FALSE)) + if(client_hasweapon(self, WEP_DEVASTATOR, TRUE, FALSE)) { self.movement_x = maxspeed; @@ -607,10 +607,10 @@ void havocbot_movetogoal() return; } - self.switchweapon = WEP_ROCKET_LAUNCHER; + self.switchweapon = WEP_DEVASTATOR; self.v_angle_x = 90; self.BUTTON_ATCK = TRUE; - self.rocketjumptime = time + autocvar_g_balance_rocketlauncher_detonatedelay; + self.rocketjumptime = time + WEP_CVAR(devastator, detonatedelay); return; } } @@ -956,7 +956,7 @@ float havocbot_chooseweapon_checkreload(float new_weapon) for(i = WEP_FIRST; i <= WEP_LAST; ++i) { // if we are out of ammo for all other weapons, it's an emergency to switch to anything else - if (weapon_action(i, WR_CHECKAMMO1) + weapon_action(i, WR_CHECKAMMO2)) + if (WEP_ACTION(i, WR_CHECKAMMO1) + WEP_ACTION(i, WR_CHECKAMMO2)) other_weapon_available = TRUE; } if(other_weapon_available) @@ -982,7 +982,7 @@ void havocbot_chooseweapon() { // If no weapon was chosen get the first available weapon if(self.weapon==0) - for(i=WEP_LASER + 1; i < WEP_COUNT ; ++i) + for(i=WEP_BLASTER + 1; i < WEP_COUNT ; ++i) // Samual: This seems strange compared to other weapon loops... { if(client_hasweapon(self, i, TRUE, FALSE)) { diff --git a/qcsrc/server/bot/havocbot/roles.qc b/qcsrc/server/bot/havocbot/roles.qc index 2104c3443a..7e3ddbb434 100644 --- a/qcsrc/server/bot/havocbot/roles.qc +++ b/qcsrc/server/bot/havocbot/roles.qc @@ -98,7 +98,10 @@ void havocbot_goalrating_items(float ratingscale, vector org, float sradius) if (head.ammo_rockets && player.ammo_rockets > self.ammo_rockets) continue; - if (head.ammo_cells && player.ammo_cells > self.ammo_cells ) + if (head.ammo_cells && player.ammo_cells > self.ammo_cells) + continue; + + if (head.ammo_plasma && player.ammo_plasma > self.ammo_plasma) continue; discard = FALSE; diff --git a/qcsrc/server/cheats.qc b/qcsrc/server/cheats.qc index c4b3fe67dd..22b793d396 100644 --- a/qcsrc/server/cheats.qc +++ b/qcsrc/server/cheats.qc @@ -140,6 +140,7 @@ float CheatImpulse(float i) self.personal.ammo_rockets = self.ammo_rockets; self.personal.ammo_nails = self.ammo_nails; self.personal.ammo_cells = self.ammo_cells; + self.personal.ammo_plasma = self.ammo_plasma; self.personal.ammo_shells = self.ammo_shells; self.personal.ammo_fuel = self.ammo_fuel; self.personal.health = self.health; @@ -197,6 +198,7 @@ float CheatImpulse(float i) self.ammo_rockets = self.personal.ammo_rockets; self.ammo_nails = self.personal.ammo_nails; self.ammo_cells = self.personal.ammo_cells; + self.ammo_plasma = self.personal.ammo_plasma; self.ammo_shells = self.personal.ammo_shells; self.ammo_fuel = self.personal.ammo_fuel; self.health = self.personal.health; @@ -264,7 +266,7 @@ float CheatImpulse(float i) e2 = spawn(); setorigin(e2, e.origin); - RadiusDamage(e2, self, 1000, 0, 128, world, 500, DEATH_CHEAT, e); + RadiusDamage(e2, self, 1000, 0, 128, world, world, 500, DEATH_CHEAT, e); remove(e2); print("404 Sportsmanship not found.\n"); diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc index f55c2658b7..143b2d18b8 100644 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@ -73,6 +73,8 @@ void ClientData_Touch(entity e) .string netname_previous; +void SetSpectator(entity player, entity spectatee); + /* ============= @@ -258,6 +260,7 @@ void PutObserverInServer (void) self.punchvector = '0 0 0'; self.oldvelocity = self.velocity; self.fire_endtime = -1; + self.event_damage = func_null; } .float model_randomizer; @@ -366,6 +369,8 @@ void PutClientInServer (void) WriteEntity(MSG_ONE, self); } + SetSpectator(self, world); + // reset player keys self.itemkeys = 0; @@ -419,11 +424,11 @@ void PutClientInServer (void) self.effects |= EF_TELEPORT_BIT | EF_RESTARTANIM_BIT; self.air_finished = time + 12; self.dmg = 2; - if(autocvar_g_balance_nex_charge) + if(WEP_CVAR(vortex, charge)) { - if(autocvar_g_balance_nex_secondary_chargepool) - self.nex_chargepool_ammo = 1; - self.nex_charge = autocvar_g_balance_nex_charge_start; + if(WEP_CVAR_SEC(vortex, chargepool)) + self.vortex_chargepool_ammo = 1; + self.vortex_charge = WEP_CVAR(vortex, charge_start); } if(warmup_stage) @@ -432,6 +437,7 @@ void PutClientInServer (void) self.ammo_nails = warmup_start_ammo_nails; self.ammo_rockets = warmup_start_ammo_rockets; self.ammo_cells = warmup_start_ammo_cells; + self.ammo_plasma = warmup_start_ammo_plasma; self.ammo_fuel = warmup_start_ammo_fuel; self.health = warmup_start_health; self.armorvalue = warmup_start_armorvalue; @@ -443,6 +449,7 @@ void PutClientInServer (void) self.ammo_nails = start_ammo_nails; self.ammo_rockets = start_ammo_rockets; self.ammo_cells = start_ammo_cells; + self.ammo_plasma = start_ammo_plasma; self.ammo_fuel = start_ammo_fuel; self.health = start_health; self.armorvalue = start_armorvalue; @@ -454,13 +461,13 @@ void PutClientInServer (void) else self.superweapons_finished = 0; - if(g_weaponarena_random) + if(g_weaponarena_random) // WEAPONTODO: more stuff that should be in a mutator. also: rename those cvars { - if(g_weaponarena_random_with_laser) - self.weapons &= ~WEPSET_LASER; + if(g_weaponarena_random_with_blaster) + self.weapons &= ~WEPSET_BLASTER; W_RandomWeapons(self, g_weaponarena_random); - if(g_weaponarena_random_with_laser) - self.weapons |= WEPSET_LASER; + if(g_weaponarena_random_with_blaster) + self.weapons |= WEPSET_BLASTER; } self.items = start_items; @@ -544,7 +551,6 @@ void PutClientInServer (void) self.spider_slowness = 0; - self.statdraintime = time + 5; self.BUTTON_ATCK = self.BUTTON_JUMP = self.BUTTON_ATCK2 = 0; if(self.killcount == -666) { @@ -567,13 +573,13 @@ void PutClientInServer (void) // reset fields the weapons may use for (j = WEP_FIRST; j <= WEP_LAST; ++j) { - weapon_action(j, WR_RESETPLAYER); + WEP_ACTION(j, WR_RESETPLAYER); // all weapons must be fully loaded when we spawn entity e; e = get_weaponinfo(j); if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars - self.(weapon_load[j]) = cvar(strcat("g_balance_", e.netname, "_reload_ammo")); + self.(weapon_load[j]) = e.reloading_ammo; } oldself = self; @@ -627,30 +633,27 @@ float ClientInit_SendEntity(entity to, float sf) WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[1])); WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[2])); WriteInt24_t(MSG_ENTITY, compressShotOrigin(hook_shotorigin[3])); - WriteInt24_t(MSG_ENTITY, compressShotOrigin(electro_shotorigin[0])); - WriteInt24_t(MSG_ENTITY, compressShotOrigin(electro_shotorigin[1])); - WriteInt24_t(MSG_ENTITY, compressShotOrigin(electro_shotorigin[2])); - WriteInt24_t(MSG_ENTITY, compressShotOrigin(electro_shotorigin[3])); - WriteInt24_t(MSG_ENTITY, compressShotOrigin(gauntlet_shotorigin[0])); - WriteInt24_t(MSG_ENTITY, compressShotOrigin(gauntlet_shotorigin[1])); - WriteInt24_t(MSG_ENTITY, compressShotOrigin(gauntlet_shotorigin[2])); - WriteInt24_t(MSG_ENTITY, compressShotOrigin(gauntlet_shotorigin[3])); + WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[0])); + WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[1])); + WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[2])); + WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[3])); + if(sv_foginterval && world.fog != "") WriteString(MSG_ENTITY, world.fog); else WriteString(MSG_ENTITY, ""); WriteByte(MSG_ENTITY, self.count * 255.0); // g_balance_armor_blockpercent - WriteCoord(MSG_ENTITY, self.bouncefactor); // g_balance_grenadelauncher_bouncefactor - WriteCoord(MSG_ENTITY, self.bouncestop); // g_balance_grenadelauncher_bouncestop - WriteCoord(MSG_ENTITY, self.ebouncefactor); // g_balance_grenadelauncher_bouncefactor - WriteCoord(MSG_ENTITY, self.ebouncestop); // g_balance_grenadelauncher_bouncestop - WriteByte(MSG_ENTITY, autocvar_g_balance_nex_secondary); // client has to know if it should zoom or not - WriteByte(MSG_ENTITY, autocvar_g_balance_rifle_secondary); // client has to know if it should zoom or not + WriteCoord(MSG_ENTITY, self.bouncefactor); // g_balance_mortar_bouncefactor // WEAPONTODO + WriteCoord(MSG_ENTITY, self.bouncestop); // g_balance_mortar_bouncestop + WriteCoord(MSG_ENTITY, self.ebouncefactor); // g_balance_mortar_bouncefactor + WriteCoord(MSG_ENTITY, self.ebouncestop); // g_balance_mortar_bouncestop + WriteByte(MSG_ENTITY, WEP_CVAR(vortex, secondary)); // client has to know if it should zoom or not // WEAPONTODO + WriteByte(MSG_ENTITY, WEP_CVAR(rifle, secondary)); // client has to know if it should zoom or not // WEAPONTODO WriteByte(MSG_ENTITY, serverflags); // client has to know if it should zoom or not - WriteByte(MSG_ENTITY, autocvar_g_balance_minelayer_limit); // minelayer max mines - WriteByte(MSG_ENTITY, autocvar_g_balance_hagar_secondary_load_max); // hagar max loadable rockets + WriteByte(MSG_ENTITY, WEP_CVAR(minelayer, limit)); // minelayer max mines // WEAPONTODO + WriteByte(MSG_ENTITY, WEP_CVAR_SEC(hagar, load_max)); // hagar max loadable rockets // WEAPONTODO WriteCoord(MSG_ENTITY, autocvar_g_trueaim_minrange); - WriteByte(MSG_ENTITY, autocvar_g_balance_porto_secondary); + WriteByte(MSG_ENTITY, WEP_CVAR(porto, secondary)); // WEAPONTODO return TRUE; } @@ -662,14 +665,14 @@ void ClientInit_CheckUpdate() self.count = autocvar_g_balance_armor_blockpercent; self.SendFlags |= 1; } - if(self.bouncefactor != autocvar_g_balance_grenadelauncher_bouncefactor) + if(self.bouncefactor != autocvar_g_balance_mortar_bouncefactor) // WEAPONTODO { - self.bouncefactor = autocvar_g_balance_grenadelauncher_bouncefactor; + self.bouncefactor = autocvar_g_balance_mortar_bouncefactor; self.SendFlags |= 1; } - if(self.bouncestop != autocvar_g_balance_grenadelauncher_bouncestop) + if(self.bouncestop != autocvar_g_balance_mortar_bouncestop) { - self.bouncestop = autocvar_g_balance_grenadelauncher_bouncestop; + self.bouncestop = autocvar_g_balance_mortar_bouncestop; self.SendFlags |= 1; } if(self.ebouncefactor != autocvar_g_balance_electro_secondary_bouncefactor) @@ -783,8 +786,8 @@ void ClientKill_Now() if(self.killindicator_teamchange) ClientKill_Now_TeamChange(); - // in any case: - Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0'); + if(IS_PLAYER(self)) + Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0'); // now I am sure the player IS dead } @@ -1196,13 +1199,7 @@ void ClientConnect (void) if(!sv_foginterval && world.fog != "") stuffcmd(self, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n")); - if(autocvar_g_hitplots || strstrofs(strcat(" ", autocvar_g_hitplots_individuals, " "), strcat(" ", self.netaddress, " "), 0) >= 0) - { - self.hitplotfh = fopen(strcat("hits-", matchid, "-", self.netaddress, "-", ftos(self.playerid), ".plot"), FILE_WRITE); - fputs(self.hitplotfh, strcat("#name ", self.netname, "\n")); - } - else - self.hitplotfh = -1; + W_HitPlotOpen(self); if(autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)) && !g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts send_CSQC_teamnagger(); @@ -1244,11 +1241,7 @@ void ClientDisconnect (void) CheatShutdownClient(); - if(self.hitplotfh >= 0) - { - fclose(self.hitplotfh); - self.hitplotfh = -1; - } + W_HitPlotClose(self); anticheat_report(); anticheat_shutdown(); @@ -1688,6 +1681,7 @@ void SpectateCopy(entity spectatee) { self.armortype = spectatee.armortype; self.armorvalue = spectatee.armorvalue; self.ammo_cells = spectatee.ammo_cells; + self.ammo_plasma = spectatee.ammo_plasma; self.ammo_shells = spectatee.ammo_shells; self.ammo_nails = spectatee.ammo_nails; self.ammo_rockets = spectatee.ammo_rockets; @@ -1708,9 +1702,10 @@ void SpectateCopy(entity spectatee) { self.switchweapon = spectatee.switchweapon; self.switchingweapon = spectatee.switchingweapon; self.weapon = spectatee.weapon; - self.nex_charge = spectatee.nex_charge; - self.nex_chargepool_ammo = spectatee.nex_chargepool_ammo; + self.vortex_charge = spectatee.vortex_charge; + self.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo; self.hagar_load = spectatee.hagar_load; + self.arc_heat_percent = spectatee.arc_heat_percent; self.minelayer_mines = spectatee.minelayer_mines; self.punchangle = spectatee.punchangle; self.view_ofs = spectatee.view_ofs; @@ -1756,15 +1751,16 @@ void SpectateCopy(entity spectatee) { } } -float SpectateUpdate() { +float SpectateUpdate() +{ if(!self.enemy) return 0; - if (self == self.enemy) - return 0; - - if (!IS_PLAYER(self.enemy)) + if(!IS_PLAYER(self.enemy) || self == self.enemy) + { + SetSpectator(self, world); return 0; + } SpectateCopy(self.enemy); @@ -1802,13 +1798,25 @@ float SpectateSet() return TRUE; } +void SetSpectator(entity player, entity spectatee) +{ + entity old_spectatee = player.enemy; + + player.enemy = spectatee; + + // WEAPONTODO + // these are required to fix the spectator bug with arc + if(old_spectatee && old_spectatee.arc_beam) { old_spectatee.arc_beam.SendFlags |= ARC_SF_SETTINGS; } + if(player.enemy && player.enemy.arc_beam) { player.enemy.arc_beam.SendFlags |= ARC_SF_SETTINGS; } +} + float Spectate(entity pl) { if(g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer) if(pl.team != self.team) return 0; - self.enemy = pl; + SetSpectator(self, pl); return SpectateSet(); } @@ -1848,8 +1856,7 @@ float SpectateNext() other = find(other, classname, "player"); } - if (other) - self.enemy = other; + if(other) { SetSpectator(self, other); } return SpectateSet(); } @@ -1888,7 +1895,7 @@ float SpectatePrev() else other = first; } - self.enemy = other; + SetSpectator(self, other); return SpectateSet(); } @@ -1983,7 +1990,7 @@ float nJoinAllowed(entity ignore) { float currentlyPlaying = 0; FOR_EACH_REALCLIENT(e) - if(IS_PLAYER(e) || e.caplayer == 1) + if(IS_PLAYER(e) || e.caplayer) currentlyPlaying += 1; if(currentlyPlaying < autocvar_g_maxplayers) @@ -1997,7 +2004,10 @@ float nJoinAllowed(entity ignore) { * g_maxplayers_spectator_blocktime seconds */ void checkSpectatorBlock() { - if(IS_SPEC(self) || IS_OBSERVER(self)) { + if(IS_SPEC(self) || IS_OBSERVER(self)) + if(!self.caplayer) + if(IS_REAL_CLIENT(self)) + { if( time > (self.spectatortime + autocvar_g_maxplayers_spectator_blocktime) ) { Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_QUIT_KICK_SPECTATING); dropclient(self); @@ -2162,7 +2172,7 @@ void PlayerPreThink (void) self.stat_game_starttime = game_starttime; self.stat_round_starttime = round_starttime; - self.stat_allow_oldnexbeam = autocvar_g_allow_oldnexbeam; + self.stat_allow_oldvortexbeam = autocvar_g_allow_oldvortexbeam; self.stat_leadlimit = autocvar_leadlimit; if(frametime) @@ -2290,17 +2300,17 @@ void PlayerPreThink (void) if(frametime) { - if(self.weapon == WEP_NEX && autocvar_g_balance_nex_charge) + if(self.weapon == WEP_VORTEX && WEP_CVAR(vortex, charge)) { - self.weaponentity_glowmod_x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit); - self.weaponentity_glowmod_y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit); - self.weaponentity_glowmod_z = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit); + self.weaponentity_glowmod_x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit)); + self.weaponentity_glowmod_y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit)); + self.weaponentity_glowmod_z = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_half * min(1, self.vortex_charge / WEP_CVAR(vortex, charge_animlimit)); - if(self.nex_charge > autocvar_g_balance_nex_charge_animlimit) + if(self.vortex_charge > WEP_CVAR(vortex, charge_animlimit)) { - self.weaponentity_glowmod_x = self.weaponentity_glowmod_x + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit); - self.weaponentity_glowmod_y = self.weaponentity_glowmod_y + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit); - self.weaponentity_glowmod_z = self.weaponentity_glowmod_z + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit); + self.weaponentity_glowmod_x = self.weaponentity_glowmod_x + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit)); + self.weaponentity_glowmod_y = self.weaponentity_glowmod_y + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit)); + self.weaponentity_glowmod_z = self.weaponentity_glowmod_z + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (self.vortex_charge - WEP_CVAR(vortex, charge_animlimit)) / (1 - WEP_CVAR(vortex, charge_animlimit)); } } else @@ -2383,7 +2393,10 @@ void PlayerPreThink (void) do_crouch = 0; if(self.frozen) do_crouch = 0; - if(self.weapon == WEP_SHOTGUN && self.weaponentity.wframe == WFRAME_FIRE2 && time < self.weapon_nextthink) + + // WEAPONTODO: THIS SHIT NEEDS TO GO EVENTUALLY + // It cannot be predicted by the engine! + if((self.weapon == WEP_SHOCKWAVE || self.weapon == WEP_SHOTGUN) && self.weaponentity.wframe == WFRAME_FIRE2 && time < self.weapon_nextthink) do_crouch = 0; if (do_crouch) @@ -2431,9 +2444,10 @@ void PlayerPreThink (void) player_regen(); - // rot nex charge to the charge limit - if(autocvar_g_balance_nex_charge_rot_rate && self.nex_charge > autocvar_g_balance_nex_charge_limit && self.nex_charge_rottime < time) - self.nex_charge = bound(autocvar_g_balance_nex_charge_limit, self.nex_charge - autocvar_g_balance_nex_charge_rot_rate * frametime / W_TICSPERFRAME, 1); + // WEAPONTODO: Add a weapon request for this + // rot vortex charge to the charge limit + if(WEP_CVAR(vortex, charge_rot_rate) && self.vortex_charge > WEP_CVAR(vortex, charge_limit) && self.vortex_charge_rottime < time) + self.vortex_charge = bound(WEP_CVAR(vortex, charge_limit), self.vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1); if(frametime) player_anim(); @@ -2457,8 +2471,9 @@ void PlayerPreThink (void) SpectatorThink(); } + // WEAPONTODO: Add weapon request for this if(!zoomstate_set) - SetZoomState(self.BUTTON_ZOOM || self.BUTTON_ZOOMSCRIPT || (self.BUTTON_ATCK2 && self.weapon == WEP_NEX) || (self.BUTTON_ATCK2 && self.weapon == WEP_RIFLE && autocvar_g_balance_rifle_secondary == 0)); + SetZoomState(self.BUTTON_ZOOM || self.BUTTON_ZOOMSCRIPT || (self.BUTTON_ATCK2 && self.weapon == WEP_VORTEX) || (self.BUTTON_ATCK2 && self.weapon == WEP_RIFLE && WEP_CVAR(rifle, secondary) == 0)); // WEAPONTODO float oldspectatee_status; oldspectatee_status = self.spectatee_status; @@ -2498,6 +2513,7 @@ void PlayerPreThink (void) target_voicescript_next(self); + // WEAPONTODO: Move into weaponsystem somehow // if a player goes unarmed after holding a loaded weapon, empty his clip size and remove the crosshair ammo ring if(!self.weapon) self.clip_load = self.clip_size = 0; diff --git a/qcsrc/server/cl_impulse.qc b/qcsrc/server/cl_impulse.qc index c9896bc745..76610f4c3e 100644 --- a/qcsrc/server/cl_impulse.qc +++ b/qcsrc/server/cl_impulse.qc @@ -68,8 +68,8 @@ void ImpulseCommands (void) // weapon switching impulses if(self.deadflag == DEAD_NO) W_NextWeaponOnImpulse(imp); - else - self.impulse = imp; // retry in next frame + //else + // self.impulse = imp; // retry in next frame } else if(imp >= 10 && imp <= 20) { @@ -78,37 +78,37 @@ void ImpulseCommands (void) switch(imp) { case 10: - W_NextWeapon (0); + W_NextWeapon(0); break; case 11: W_LastWeapon(); break; case 12: - W_PreviousWeapon (0); + W_PreviousWeapon(0); break; case 13: - W_SwitchWeapon (w_getbestweapon(self)); + W_SwitchWeapon(w_getbestweapon(self)); break; case 14: W_NextWeaponOnImpulse(0); break; case 15: - W_NextWeapon (2); + W_NextWeapon(2); break; case 16: - W_PreviousWeapon (2); + W_PreviousWeapon(2); break; case 17: W_ThrowWeapon(W_CalculateProjectileVelocity(self.velocity, v_forward * 750, FALSE), '0 0 0', TRUE); break; case 18: - W_NextWeapon (1); + W_NextWeapon(1); break; case 19: - W_PreviousWeapon (1); + W_PreviousWeapon(1); break; case 20: - W_TriggerReload (); + if(!forbidWeaponUse()) { WEP_ACTION(self.weapon, WR_RELOAD); } break; } } diff --git a/qcsrc/server/cl_physics.qc b/qcsrc/server/cl_physics.qc index 14747fa99a..913a5a037f 100644 --- a/qcsrc/server/cl_physics.qc +++ b/qcsrc/server/cl_physics.qc @@ -1091,12 +1091,11 @@ void SV_PlayerPhysics() else fz = bound(0, 1 + self.velocity_z / autocvar_g_jetpack_maxspeed_up, 1); - float fvel; - fvel = vlen(wishvel); wishvel_x *= fxy; wishvel_y *= fxy; wishvel_z = (wishvel_z - autocvar_sv_gravity) * fz + autocvar_sv_gravity; + float fvel; fvel = min(1, vlen(wishvel) / best); if(autocvar_g_jetpack_fuel && !(self.items & IT_UNLIMITED_WEAPON_AMMO)) f = min(1, self.ammo_fuel / (autocvar_g_jetpack_fuel * frametime * fvel)); @@ -1301,15 +1300,16 @@ void SV_PlayerPhysics() } } + // WEAPONTODO float xyspeed; xyspeed = vlen('1 0 0' * self.velocity_x + '0 1 0' * self.velocity_y); - if(self.weapon == WEP_NEX && autocvar_g_balance_nex_charge && autocvar_g_balance_nex_charge_velocity_rate && xyspeed > autocvar_g_balance_nex_charge_minspeed) + if(self.weapon == WEP_VORTEX && WEP_CVAR(vortex, charge) && WEP_CVAR(vortex, charge_velocity_rate) && xyspeed > WEP_CVAR(vortex, charge_minspeed)) { // add a maximum of charge_velocity_rate when going fast (f = 1), gradually increasing from minspeed (f = 0) to maxspeed - xyspeed = min(xyspeed, autocvar_g_balance_nex_charge_maxspeed); - f = (xyspeed - autocvar_g_balance_nex_charge_minspeed) / (autocvar_g_balance_nex_charge_maxspeed - autocvar_g_balance_nex_charge_minspeed); + xyspeed = min(xyspeed, WEP_CVAR(vortex, charge_maxspeed)); + f = (xyspeed - WEP_CVAR(vortex, charge_minspeed)) / (WEP_CVAR(vortex, charge_maxspeed) - WEP_CVAR(vortex, charge_minspeed)); // add the extra charge - self.nex_charge = min(1, self.nex_charge + autocvar_g_balance_nex_charge_velocity_rate * f * frametime); + self.vortex_charge = min(1, self.vortex_charge + WEP_CVAR(vortex, charge_velocity_rate) * f * frametime); } :end if(self.flags & FL_ONGROUND) diff --git a/qcsrc/server/cl_player.qc b/qcsrc/server/cl_player.qc index 5aab588320..9ae3f25d75 100644 --- a/qcsrc/server/cl_player.qc +++ b/qcsrc/server/cl_player.qc @@ -1,126 +1,3 @@ -.entity accuracy; -.float accuracy_frags[WEP_MAXCOUNT]; - -float weaponstats_buffer; - -void WeaponStats_Init() -{ - if(autocvar_sv_weaponstats_file != "") - weaponstats_buffer = buf_create(); - else - weaponstats_buffer = -1; -} - -#define WEAPONSTATS_GETINDEX(awep,abot,vwep,vbot) (((vwep) + (awep) * (WEP_LAST - WEP_FIRST + 1) - (WEP_FIRST + WEP_FIRST * (WEP_LAST - WEP_FIRST + 1))) * 4 + (abot) * 2 + (vbot)) - -void WeaponStats_ready(entity fh, entity pass, float status) -{ - float i, j, n, ibot, jbot, idx; - vector v; - string prefix, s; - switch(status) - { - case URL_READY_CANWRITE: - // we can write - prefix = strcat(autocvar_hostname, "\t", GetGametype(), "_", GetMapname(), "\t"); - url_fputs(fh, "#begin statsfile\n"); - url_fputs(fh, strcat("#date ", strftime(TRUE, "%a %b %e %H:%M:%S %Z %Y"), "\n")); -#ifdef WATERMARK - url_fputs(fh, strcat("#version ", WATERMARK, "\n")); -#endif - url_fputs(fh, strcat("#config ", ftos(crc16(FALSE, cvar_purechanges)), "\n")); - url_fputs(fh, strcat("#cvar_purechanges ", ftos(cvar_purechanges_count), "\n")); - n = tokenizebyseparator(cvar_purechanges, "\n"); - for(i = 0; i < n; ++i) - url_fputs(fh, strcat("#cvar_purechange ", argv(i), "\n")); - for(i = WEP_FIRST; i <= WEP_LAST; ++i) for(ibot = 0; ibot <= 1; ++ibot) - for(j = WEP_FIRST; j <= WEP_LAST; ++j) for(jbot = 0; jbot <= 1; ++jbot) - { - idx = WEAPONSTATS_GETINDEX(i, ibot, j, jbot); - v = stov(bufstr_get(weaponstats_buffer, idx)); - if(v != '0 0 0') - { - //vector is: kills hits damage - url_fputs(fh, sprintf("%s%d %d\t%d %d\t", prefix, i, ibot, j, jbot)); - url_fputs(fh, sprintf("%d %d %g\n", v_x, v_y, v_z)); - } - } - url_fputs(fh, "#end\n\n"); - url_fclose(fh); - break; - case URL_READY_CANREAD: - // url_fclose is processing, we got a response for writing the data - // this must come from HTTP - print("Got response from weapon stats server:\n"); - while((s = url_fgets(fh))) - print(" ", s, "\n"); - print("End of response.\n"); - url_fclose(fh); - break; - case URL_READY_CLOSED: - // url_fclose has finished - print("Weapon stats written\n"); - buf_del(weaponstats_buffer); - weaponstats_buffer = -1; - break; - case URL_READY_ERROR: - default: - print("Weapon stats writing failed: ", ftos(status), "\n"); - buf_del(weaponstats_buffer); - weaponstats_buffer = -1; - break; - } -} - -void WeaponStats_Shutdown() -{ - if(weaponstats_buffer < 0) - return; - if(autocvar_sv_weaponstats_file != "") - { - url_multi_fopen(autocvar_sv_weaponstats_file, FILE_APPEND, WeaponStats_ready, world); - } - else - { - buf_del(weaponstats_buffer); - weaponstats_buffer = -1; - } -} - -void WeaponStats_LogItem(float awep, float abot, float vwep, float vbot, vector item) -{ - float idx; - if(weaponstats_buffer < 0) - return; - if(awep < WEP_FIRST || vwep < WEP_FIRST) - return; - if(awep > WEP_LAST || vwep > WEP_LAST) - return; - idx = WEAPONSTATS_GETINDEX(awep,abot,vwep,vbot); - bufstr_set(weaponstats_buffer, idx, vtos(stov(bufstr_get(weaponstats_buffer, idx)) + item)); -} -void WeaponStats_LogDamage(float awep, float abot, float vwep, float vbot, float damage) -{ - if(damage < 0) - error("negative damage?"); - WeaponStats_LogItem(awep, abot, vwep, vbot, '0 0 1' * damage + '0 1 0'); -} -void WeaponStats_LogKill(float awep, float abot, float vwep, float vbot) -{ - WeaponStats_LogItem(awep, abot, vwep, vbot, '1 0 0'); -} - -// changes by LordHavoc on 03/29/04 and 03/30/04 at Vermeulen's request -// merged player_run and player_stand to player_anim -// added death animations to player_anim -// can now spawn thrown weapons from anywhere, not just from players -// thrown weapons now fade out after 20 seconds -// created PlayerGib function -// PlayerDie no longer uses hitloc or damage -// PlayerDie now supports dying animations as well as gibbing -// cleaned up PlayerDie a lot -// added CopyBody - .entity pusher; .float pushltime; .float istypefrag; @@ -268,13 +145,6 @@ void player_anim (void) } } -void SpawnThrownWeapon (vector org, float w) -{ - if(self.weapons & WepSet_FromWeapon(self.weapon)) - if(W_IsWeaponThrowable(self.weapon)) - W_ThrowNewWeapon(self, self.weapon, FALSE, org, randomvec() * 125 + '0 0 200'); -} - void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) { float take, save; @@ -343,6 +213,9 @@ void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float void calculate_player_respawn_time() { + if(g_ca) + return; + float gametype_setting_tmp; float sdelay_max = GAMETYPE_DEFAULTED_SETTING(respawn_delay_max); float sdelay_small = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small); @@ -561,7 +434,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht } if(sound_allowed(MSG_BROADCAST, attacker)) - if(!DEATH_ISWEAPON(deathtype, WEP_LASER) || attacker != self || self.health < 2 * autocvar_g_balance_laser_primary_damage * autocvar_g_balance_selfdamagepercent + 1) + if((self.health < 2 * WEP_CVAR_PRI(blaster, damage) * autocvar_g_balance_selfdamagepercent + 1) || !((get_weaponinfo(DEATH_WEAPONOF(deathtype))).spawnflags & WEP_FLAG_CANCLIMB) || attacker != self) // WEAPONTODO: create separate limit for pain notification with laser if(self.health > 1) // exclude pain sounds for laserjumps as long as you aren't REALLY low on health and would die of the next two { @@ -677,7 +550,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht frag_deathtype = deathtype; MUTATOR_CALLHOOK(PlayerDies); - weapon_action(self.weapon, WR_PLAYERDEATH); + WEP_ACTION(self.weapon, WR_PLAYERDEATH); RemoveGrapplingHook(self); @@ -721,6 +594,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht self.flags &= ~FL_ONGROUND; // dying animation self.deadflag = DEAD_DYING; + // when to allow respawn calculate_player_respawn_time(); @@ -749,7 +623,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht // reset fields the weapons may use just in case for (j = WEP_FIRST; j <= WEP_LAST; ++j) { - weapon_action(j, WR_RESETPLAYER); + WEP_ACTION(j, WR_RESETPLAYER); ATTACK_FINISHED_FOR(self, j) = 0; } } diff --git a/qcsrc/server/cl_weapons.qc b/qcsrc/server/cl_weapons.qc deleted file mode 100644 index 41cdd2b05e..0000000000 --- a/qcsrc/server/cl_weapons.qc +++ /dev/null @@ -1,537 +0,0 @@ -void W_TriggerReload() -{ - weapon_action(self.weapon, WR_RELOAD); -} - -// switch between weapons -void W_SwitchWeapon(float imp) -{ - if (self.switchweapon != imp) - { - if (client_hasweapon(self, imp, TRUE, TRUE)) - W_SwitchWeapon_Force(self, imp); - else - self.selectweapon = imp; // update selectweapon ANYWAY - } - else - { - W_TriggerReload(); - } -} - -.float weaponcomplainindex; -float W_GetCycleWeapon(entity pl, string weaponorder, float dir, float imp, float complain, float skipmissing) -{ - // We cannot tokenize in this function, as GiveItems calls this - // function. Thus we must use car/cdr. - float weaponwant, first_valid, prev_valid, switchtonext, switchtolast, c; - string rest; - switchtonext = switchtolast = 0; - first_valid = prev_valid = 0; - float weaponcur; - - if(skipmissing || pl.selectweapon == 0) - weaponcur = pl.switchweapon; - else - weaponcur = pl.selectweapon; - - if(dir == 0) - switchtonext = 1; - - c = 0; - - rest = weaponorder; - while(rest != "") - { - weaponwant = stof(car(rest)); rest = cdr(rest); - if(imp >= 0) - if((get_weaponinfo(weaponwant)).impulse != imp) - continue; - - ++c; - - if(!skipmissing || client_hasweapon(pl, weaponwant, TRUE, FALSE)) - { - if(switchtonext) - return weaponwant; - if(!first_valid) - first_valid = weaponwant; - if(weaponwant == weaponcur) - { - if(dir >= 0) - switchtonext = 1; - else if(prev_valid) - return prev_valid; - else - switchtolast = 1; - } - prev_valid = weaponwant; - } - } - if(first_valid) - { - if(switchtolast) - return prev_valid; - else - return first_valid; - } - // complain (but only for one weapon on the button that has been pressed) - if(complain) - { - self.weaponcomplainindex += 1; - c = mod(self.weaponcomplainindex, c) + 1; - rest = weaponorder; - while(rest != "") - { - weaponwant = stof(car(rest)); rest = cdr(rest); - if(imp >= 0) - if((get_weaponinfo(weaponwant)).impulse != imp) - continue; - - --c; - if(c == 0) - { - client_hasweapon(pl, weaponwant, TRUE, TRUE); - break; - } - } - } - return 0; -} - -void W_CycleWeapon(string weaponorder, float dir) -{ - float w; - w = W_GetCycleWeapon(self, weaponorder, dir, -1, 1, TRUE); - if(w > 0) - W_SwitchWeapon(w); -} - -void W_NextWeaponOnImpulse(float imp) -{ - float w; - w = W_GetCycleWeapon(self, self.cvar_cl_weaponpriority, +1, imp, 1, (self.cvar_cl_weaponimpulsemode == 0)); - if(w > 0) - W_SwitchWeapon(w); -} - -// next weapon -void W_NextWeapon(float list) -{ - if(list == 0) - W_CycleWeapon(weaponorder_byid, -1); - else if(list == 1) - W_CycleWeapon(self.weaponorder_byimpulse, -1); - else if(list == 2) - W_CycleWeapon(self.cvar_cl_weaponpriority, -1); -} - -// prev weapon -void W_PreviousWeapon(float list) -{ - if(list == 0) - W_CycleWeapon(weaponorder_byid, +1); - else if(list == 1) - W_CycleWeapon(self.weaponorder_byimpulse, +1); - else if(list == 2) - W_CycleWeapon(self.cvar_cl_weaponpriority, +1); -} - -// previously used if exists and has ammo, (second) best otherwise -void W_LastWeapon() -{ - if(client_hasweapon(self, self.cnt, TRUE, FALSE)) - W_SwitchWeapon(self.cnt); - else - W_SwitchToOtherWeapon(self); -} - -float w_getbestweapon(entity e) -{ - return W_GetCycleWeapon(e, e.cvar_cl_weaponpriority, 0, -1, FALSE, TRUE); -} - -// generic weapons table -// TODO should they be macros instead? -float weapon_action(float wpn, float wrequest) -{ - return (get_weaponinfo(wpn)).weapon_func(wrequest); -} - -.float savenextthink; -void thrown_wep_think() -{ - self.owner = world; - float timeleft = self.savenextthink - time; - if(timeleft > 1) - SUB_SetFade(self, self.savenextthink - 1, 1); - else if(timeleft > 0) - SUB_SetFade(self, time, timeleft); - else - SUB_VanishOrRemove(self); -} - -// returns amount of ammo used as string, or -1 for failure, or 0 for no ammo count -string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo) -{ - entity oldself, wep; - float wa, thisammo, i, j; - string s; - var .float ammofield; - - wep = spawn(); - - setorigin(wep, org); - wep.classname = "droppedweapon"; - wep.velocity = velo; - wep.owner = wep.enemy = own; - wep.flags |= FL_TOSSED; - wep.colormap = own.colormap; - - if(WepSet_FromWeapon(wpn) & WEPSET_SUPERWEAPONS) - { - if(own.items & IT_UNLIMITED_SUPERWEAPONS) - { - wep.superweapons_finished = time + autocvar_g_balance_superweapons_time; - } - else - { - float superweapons = 1; - for(i = WEP_FIRST; i <= WEP_LAST; ++i) - if(WepSet_FromWeapon(i) & WEPSET_SUPERWEAPONS) - if(own.weapons & WepSet_FromWeapon(i)) - ++superweapons; - if(superweapons <= 1) - { - wep.superweapons_finished = own.superweapons_finished; - own.superweapons_finished = 0; - } - else - { - float timeleft = own.superweapons_finished - time; - float weptimeleft = timeleft / superweapons; - wep.superweapons_finished = time + weptimeleft; - own.superweapons_finished -= weptimeleft; - } - } - } - - wa = W_AmmoItemCode(wpn); - if(wa == 0) - { - oldself = self; - self = wep; - weapon_defaultspawnfunc(wpn); - self = oldself; - if(startitem_failed) - return string_null; - wep.glowmod = own.weaponentity_glowmod; - wep.think = thrown_wep_think; - wep.savenextthink = wep.nextthink; - wep.nextthink = min(wep.nextthink, time + 0.5); - wep.pickup_anyway = TRUE; // these are ALWAYS pickable - return ""; - } - else - { - s = ""; - oldself = self; - self = wep; - weapon_defaultspawnfunc(wpn); - self = oldself; - if(startitem_failed) - return string_null; - if(doreduce && g_weapon_stay == 2) - { - for(i = 0, j = 1; i < 24; ++i, j *= 2) - { - if(wa & j) - { - ammofield = Item_CounterField(j); - - // if our weapon is loaded, give its load back to the player - if(self.(weapon_load[self.weapon]) > 0) - { - own.ammofield += self.(weapon_load[self.weapon]); - self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading - } - - wep.ammofield = 0; - } - } - } - else if(doreduce) - { - for(i = 0, j = 1; i < 24; ++i, j *= 2) - { - if(wa & j) - { - ammofield = Item_CounterField(j); - - // if our weapon is loaded, give its load back to the player - if(self.(weapon_load[self.weapon]) > 0) - { - own.ammofield += self.(weapon_load[self.weapon]); - self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading - } - - thisammo = min(own.ammofield, wep.ammofield); - wep.ammofield = thisammo; - own.ammofield -= thisammo; - s = strcat(s, " and ", ftos(thisammo), " ", Item_CounterFieldName(j)); - } - } - s = substring(s, 5, -1); - } - wep.glowmod = own.weaponentity_glowmod; - wep.think = thrown_wep_think; - wep.savenextthink = wep.nextthink; - wep.nextthink = min(wep.nextthink, time + 0.5); - wep.pickup_anyway = TRUE; // these are ALWAYS pickable - - return s; - } -} - -float W_IsWeaponThrowable(float w) -{ - float wa; - - if (!autocvar_g_pickup_items) - return 0; - if (g_weaponarena) - return 0; - if (g_nexball && w == WEP_GRENADE_LAUNCHER) - return 0; - if(w == 0) - return 0; - - wa = W_AmmoItemCode(w); - if(start_weapons & WepSet_FromWeapon(w)) - { - // start weapons that take no ammo can't be dropped (this prevents dropping the laser, as long as it continues to use no ammo) - if(start_items & IT_UNLIMITED_WEAPON_AMMO) - return 0; - if(wa == 0) - return 0; - } - - return 1; -} - -// toss current weapon -void W_ThrowWeapon(vector velo, vector delta, float doreduce) -{ - float w; - string a; - - w = self.weapon; - if (w == 0) - return; // just in case - if(self.frozen) - return; - if(MUTATOR_CALLHOOK(ForbidThrowCurrentWeapon)) - return; - if(!autocvar_g_weapon_throwable) - return; - if(self.weaponentity.state != WS_READY) - return; - if(!W_IsWeaponThrowable(w)) - return; - - if(!(self.weapons & WepSet_FromWeapon(w))) - return; - self.weapons &= ~WepSet_FromWeapon(w); - - W_SwitchWeapon_Force(self, w_getbestweapon(self)); - a = W_ThrowNewWeapon(self, w, doreduce, self.origin + delta, velo); - - if (!a) return; - Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_WEAPON_DROP, a, w); -} - -float forbidWeaponUse() -{ - if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown) - return 1; - if(round_handler_IsActive() && !round_handler_IsRoundStarted()) - return 1; - if(self.player_blocked) - return 1; - if(self.frozen) - return 1; - return 0; -} - -void W_WeaponFrame() -{ - vector fo, ri, up; - - if (frametime) - self.weapon_frametime = frametime; - - if (!self.weaponentity || self.health < 1) - return; // Dead player can't use weapons and injure impulse commands - - if(forbidWeaponUse()) - if(self.weaponentity.state != WS_CLEAR) - { - w_ready(); - return; - } - - if(!self.switchweapon) - { - self.weapon = 0; - self.switchingweapon = 0; - self.weaponentity.state = WS_CLEAR; - self.weaponname = ""; - self.items &= ~IT_AMMO; - return; - } - - makevectors(self.v_angle); - fo = v_forward; // save them in case the weapon think functions change it - ri = v_right; - up = v_up; - - // Change weapon - if (self.weapon != self.switchweapon) - { - if (self.weaponentity.state == WS_CLEAR) - { - // end switching! - self.switchingweapon = self.switchweapon; - - entity newwep = get_weaponinfo(self.switchweapon); - - //setanim(self, self.anim_draw, FALSE, TRUE, TRUE); - self.weaponentity.state = WS_RAISE; - weapon_action(self.switchweapon, WR_SETUP); - - // set our clip load to the load of the weapon we switched to, if it's reloadable - if(newwep.spawnflags & WEP_FLAG_RELOADABLE && cvar(strcat("g_balance_", newwep.netname, "_reload_ammo"))) // prevent accessing undefined cvars - { - self.clip_load = self.(weapon_load[self.switchweapon]); - self.clip_size = cvar(strcat("g_balance_", newwep.netname, "_reload_ammo")); - } - else - self.clip_load = self.clip_size = 0; - - // VorteX: add player model weapon select frame here - // setcustomframe(PlayerWeaponRaise); - weapon_thinkf(WFRAME_IDLE, cvar(sprintf("g_balance_%s_switchdelay_raise", newwep.netname)), w_ready); - //printf("W_WeaponFrame(): cvar: %s, value: %f\n", sprintf("g_balance_%s_switchdelay_raise", newwep.netname), cvar(sprintf("g_balance_%s_switchdelay_raise", newwep.netname))); - weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, '0 0 0'); - } - else if (self.weaponentity.state == WS_DROP) - { - // in dropping phase we can switch at any time - self.switchingweapon = self.switchweapon; - } - else if (self.weaponentity.state == WS_READY) - { - // start switching! - self.switchingweapon = self.switchweapon; - - entity oldwep = get_weaponinfo(self.weapon); - -#ifndef INDEPENDENT_ATTACK_FINISHED - if(ATTACK_FINISHED(self) <= time + self.weapon_frametime * 0.5) - { -#endif - sound (self, CH_WEAPON_SINGLE, "weapons/weapon_switch.wav", VOL_BASE, ATTEN_NORM); - self.weaponentity.state = WS_DROP; - // set up weapon switch think in the future, and start drop anim - weapon_thinkf(WFRAME_DONTCHANGE, cvar(sprintf("g_balance_%s_switchdelay_drop", oldwep.netname)), w_clear); - //printf("W_WeaponFrame(): cvar: %s, value: %f\n", sprintf("g_balance_%s_switchdelay_drop", oldwep.netname), cvar(sprintf("g_balance_%s_switchdelay_drop", oldwep.netname))); - weapon_boblayer1(PLAYER_WEAPONSELECTION_SPEED, PLAYER_WEAPONSELECTION_RANGE); -#ifndef INDEPENDENT_ATTACK_FINISHED - } -#endif - } - } - - // LordHavoc: network timing test code - //if (self.button0) - // print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(self)), " >= ", ftos(self.weapon_nextthink), "\n"); - - float w; - w = self.weapon; - - // call the think code which may fire the weapon - // and do so multiple times to resolve framerate dependency issues if the - // server framerate is very low and the weapon fire rate very high - float c; - c = 0; - while (c < W_TICSPERFRAME) - { - c = c + 1; - if(w && !(self.weapons & WepSet_FromWeapon(w))) - { - if(self.weapon == self.switchweapon) - W_SwitchWeapon_Force(self, w_getbestweapon(self)); - w = 0; - } - - v_forward = fo; - v_right = ri; - v_up = up; - - if(w) - weapon_action(self.weapon, WR_THINK); - else - weapon_action(self.weapon, WR_GONETHINK); - - if (time + self.weapon_frametime * 0.5 >= self.weapon_nextthink) - { - if(self.weapon_think) - { - v_forward = fo; - v_right = ri; - v_up = up; - self.weapon_think(); - } - else - bprint("\{1}^1ERROR: undefined weapon think function for ", self.netname, "\n"); - } - } - - // don't let attack_finished fall behind when not firing (must be after weapon_setup calls!) - //if (ATTACK_FINISHED(self) < time) - // ATTACK_FINISHED(self) = time; - - //if (self.weapon_nextthink < time) - // self.weapon_nextthink = time; - - // update currentammo incase it has changed -#if 0 - if (self.items & IT_CELLS) - self.currentammo = self.ammo_cells; - else if (self.items & IT_ROCKETS) - self.currentammo = self.ammo_rockets; - else if (self.items & IT_NAILS) - self.currentammo = self.ammo_nails; - else if (self.items & IT_SHELLS) - self.currentammo = self.ammo_shells; - else - self.currentammo = 1; -#endif -} - -string W_Apply_Weaponreplace(string in) -{ - float n = tokenize_console(in); - string out = ""; - float i; - for(i = 0; i < n; ++i) - { - string s = argv(i); - string r = cvar_string(strcat("g_weaponreplace_", s)); - if(r == "") - out = strcat(out, " ", s); - else if(r != "0") - out = strcat(out, " ", r); - } - return substring(out, 1, -1); -} diff --git a/qcsrc/server/cl_weaponsystem.qc b/qcsrc/server/cl_weaponsystem.qc deleted file mode 100644 index dac383633c..0000000000 --- a/qcsrc/server/cl_weaponsystem.qc +++ /dev/null @@ -1,1407 +0,0 @@ -/* -=========================================================================== - - CLIENT WEAPONSYSTEM CODE - Bring back W_Weaponframe - -=========================================================================== -*/ - -.float weapon_frametime; - -float W_WeaponRateFactor() -{ - float t; - t = 1.0 / g_weaponratefactor; - - weapon_rate = t; - MUTATOR_CALLHOOK(WeaponRateFactor); - t = weapon_rate; - - return t; -} - -void W_SwitchWeapon_Force(entity e, float w) -{ - e.cnt = e.switchweapon; - e.switchweapon = w; - e.selectweapon = w; -} - -.float antilag_debug; - -// VorteX: static frame globals -const float WFRAME_DONTCHANGE = -1; -const float WFRAME_FIRE1 = 0; -const float WFRAME_FIRE2 = 1; -const float WFRAME_IDLE = 2; -const float WFRAME_RELOAD = 3; -.float wframe; - -void(float fr, float t, void() func) weapon_thinkf; - -vector W_HitPlotUnnormalizedUntransform(vector screenforward, vector screenright, vector screenup, vector v) -{ - vector ret; - ret_x = screenright * v; - ret_y = screenup * v; - ret_z = screenforward * v; - return ret; -} - -vector W_HitPlotNormalizedUntransform(vector org, entity targ, vector screenforward, vector screenright, vector screenup, vector v) -{ - float i, j, k; - vector mi, ma, thisv, myv, ret; - - myv = W_HitPlotUnnormalizedUntransform(screenforward, screenright, screenup, org); - - // x = 0..1 relative to hitbox; y = 0..1 relative to hitbox; z = distance - - mi = ma = targ.origin + 0.5 * (targ.mins + targ.maxs); - for(i = 0; i < 2; ++i) for(j = 0; j < 2; ++j) for(k = 0; k < 2; ++k) - { - thisv = targ.origin; - if(i) thisv_x += targ.maxs_x; else thisv_x += targ.mins_x; - if(j) thisv_y += targ.maxs_y; else thisv_y += targ.mins_y; - if(k) thisv_z += targ.maxs_z; else thisv_z += targ.mins_z; - thisv = W_HitPlotUnnormalizedUntransform(screenforward, screenright, screenup, thisv); - if(i || j || k) - { - if(mi_x > thisv_x) mi_x = thisv_x; if(ma_x < thisv_x) ma_x = thisv_x; - if(mi_y > thisv_y) mi_y = thisv_y; if(ma_y < thisv_y) ma_y = thisv_y; - //if(mi_z > thisv_z) mi_z = thisv_z; if(ma_z < thisv_z) ma_y = thisv_z; - } - else - { - // first run - mi = ma = thisv; - } - } - - thisv = W_HitPlotUnnormalizedUntransform(screenforward, screenright, screenup, v); - ret_x = (thisv_x - mi_x) / (ma_x - mi_x); - ret_y = (thisv_y - mi_y) / (ma_y - mi_y); - ret_z = thisv_z - myv_z; - return ret; -} - -void W_HitPlotAnalysis(entity player, vector screenforward, vector screenright, vector screenup) -{ - vector hitplot; - vector org; - float lag; - - if(player.hitplotfh >= 0) - { - lag = ANTILAG_LATENCY(player); - if(lag < 0.001) - lag = 0; - if (!IS_REAL_CLIENT(player)) - lag = 0; // only antilag for clients - - org = player.origin + player.view_ofs; - traceline_antilag_force(player, org, org + screenforward * MAX_SHOT_DISTANCE, MOVE_NORMAL, player, lag); - if(IS_CLIENT(trace_ent) || (trace_ent.flags & FL_MONSTER)) - { - antilag_takeback(trace_ent, time - lag); - hitplot = W_HitPlotNormalizedUntransform(org, trace_ent, screenforward, screenright, screenup, trace_endpos); - antilag_restore(trace_ent); - fputs(player.hitplotfh, strcat(ftos(hitplot_x), " ", ftos(hitplot_y), " ", ftos(hitplot_z), " ", ftos(player.switchweapon), "\n")); - //print(strcat(ftos(hitplot_x), " ", ftos(hitplot_y), " ", ftos(hitplot_z), "\n")); - } - } -} - -vector w_shotorg; -vector w_shotdir; -vector w_shotend; - -.float prevstrengthsound; -.float prevstrengthsoundattempt; -void W_PlayStrengthSound(entity player) // void W_PlayStrengthSound -{ - if((player.items & IT_STRENGTH) - && ((time > player.prevstrengthsound + autocvar_sv_strengthsound_antispam_time) // prevent insane sound spam - || (time > player.prevstrengthsoundattempt + autocvar_sv_strengthsound_antispam_refire_threshold))) - { - sound(player, CH_TRIGGER, "weapons/strength_fire.wav", VOL_BASE, ATTEN_NORM); - player.prevstrengthsound = time; - } - player.prevstrengthsoundattempt = time; -} - -// this function calculates w_shotorg and w_shotdir based on the weapon model -// offset, trueaim and antilag, and won't put w_shotorg inside a wall. -// make sure you call makevectors first (FIXME?) -void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector mi, vector ma, float antilag, float recoil, string snd, float chan, float maxdamage, float range) -{ - float nudge = 1; // added to traceline target and subtracted from result - float oldsolid; - vector vecs, dv; - oldsolid = ent.dphitcontentsmask; - if(ent.weapon == WEP_RIFLE) - ent.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_CORPSE; - else - ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; - if(antilag) - WarpZone_traceline_antilag(world, ent.origin + ent.view_ofs, ent.origin + ent.view_ofs + s_forward * range, MOVE_NORMAL, ent, ANTILAG_LATENCY(ent)); - // passing world, because we do NOT want it to touch dphitcontentsmask - else - WarpZone_TraceLine(ent.origin + ent.view_ofs, ent.origin + ent.view_ofs + s_forward * range, MOVE_NOMONSTERS, ent); - ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; - - vector vf, vr, vu; - vf = v_forward; - vr = v_right; - vu = v_up; - w_shotend = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); // warpzone support - v_forward = vf; - v_right = vr; - v_up = vu; - - // un-adjust trueaim if shotend is too close - if(vlen(w_shotend - (ent.origin + ent.view_ofs)) < autocvar_g_trueaim_minrange) - w_shotend = ent.origin + ent.view_ofs + s_forward * autocvar_g_trueaim_minrange; - - // track max damage - if(accuracy_canbegooddamage(ent)) - accuracy_add(ent, ent.weapon, maxdamage, 0); - - W_HitPlotAnalysis(ent, v_forward, v_right, v_up); - - if(ent.weaponentity.movedir_x > 0) - vecs = ent.weaponentity.movedir; - else - vecs = '0 0 0'; - - dv = v_right * -vecs_y + v_up * vecs_z; - w_shotorg = ent.origin + ent.view_ofs + dv; - - // now move the shotorg forward as much as requested if possible - if(antilag) - { - if(ent.antilag_debug) - tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, ent, ent.antilag_debug); - else - tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, ent, ANTILAG_LATENCY(ent)); - } - else - tracebox(w_shotorg, mi, ma, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, ent); - w_shotorg = trace_endpos - v_forward * nudge; - // calculate the shotdir from the chosen shotorg - w_shotdir = normalize(w_shotend - w_shotorg); - - if (antilag) - if (!ent.cvar_cl_noantilag) - { - if (autocvar_g_antilag == 1) // switch to "ghost" if not hitting original - { - traceline(w_shotorg, w_shotorg + w_shotdir * range, MOVE_NORMAL, ent); - if (!trace_ent.takedamage) - { - traceline_antilag_force (ent, w_shotorg, w_shotorg + w_shotdir * range, MOVE_NORMAL, ent, ANTILAG_LATENCY(ent)); - if (trace_ent.takedamage && IS_PLAYER(trace_ent)) - { - entity e; - e = trace_ent; - traceline(w_shotorg, e.origin, MOVE_NORMAL, ent); - if(trace_ent == e) - w_shotdir = normalize(trace_ent.origin - w_shotorg); - } - } - } - else if(autocvar_g_antilag == 3) // client side hitscan - { - // this part MUST use prydon cursor - if (ent.cursor_trace_ent) // client was aiming at someone - if (ent.cursor_trace_ent != ent) // just to make sure - if (ent.cursor_trace_ent.takedamage) // and that person is killable - if (IS_PLAYER(ent.cursor_trace_ent)) // and actually a player - { - // verify that the shot would miss without antilag - // (avoids an issue where guns would always shoot at their origin) - traceline(w_shotorg, w_shotorg + w_shotdir * range, MOVE_NORMAL, ent); - if (!trace_ent.takedamage) - { - // verify that the shot would hit if altered - traceline(w_shotorg, ent.cursor_trace_ent.origin, MOVE_NORMAL, ent); - if (trace_ent == ent.cursor_trace_ent) - w_shotdir = normalize(ent.cursor_trace_ent.origin - w_shotorg); - else - print("antilag fail\n"); - } - } - } - } - - ent.dphitcontentsmask = oldsolid; // restore solid type (generally SOLID_SLIDEBOX) - - if (!autocvar_g_norecoil) - ent.punchangle_x = recoil * -1; - - if (snd != "") - { - sound (ent, chan, snd, VOL_BASE, ATTEN_NORM); - W_PlayStrengthSound(ent); - } - - // nudge w_shotend so a trace to w_shotend hits - w_shotend = w_shotend + normalize(w_shotend - w_shotorg) * nudge; -} - -#define W_SetupShot_Dir_ProjectileSize(ent,s_forward,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize_Range(ent, s_forward, mi, ma, antilag, recoil, snd, chan, maxdamage, MAX_SHOT_DISTANCE) -#define W_SetupShot_ProjectileSize(ent,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, v_forward, mi, ma, antilag, recoil, snd, chan, maxdamage) -#define W_SetupShot_Dir(ent,s_forward,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, s_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage) -#define W_SetupShot(ent,antilag,recoil,snd,chan,maxdamage) W_SetupShot_ProjectileSize(ent, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage) -#define W_SetupShot_Range(ent,antilag,recoil,snd,chan,maxdamage,range) W_SetupShot_Dir_ProjectileSize_Range(ent, v_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage, range) - -float CL_Weaponentity_CustomizeEntityForClient() -{ - self.viewmodelforclient = self.owner; - if(IS_SPEC(other)) - if(other.enemy == self.owner) - self.viewmodelforclient = other; - return TRUE; -} - -/* - * supported formats: - * - * 1. simple animated model, muzzle flash handling on h_ model: - * h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation - * tags: - * shot = muzzle end (shot origin, also used for muzzle flashes) - * shell = casings ejection point (must be on the right hand side of the gun) - * weapon = attachment for v_tuba.md3 - * v_tuba.md3 - first and third person model - * g_tuba.md3 - pickup model - * - * 2. simple animated model, muzzle flash handling on v_ model: - * h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation - * tags: - * weapon = attachment for v_tuba.md3 - * v_tuba.md3 - first and third person model - * tags: - * shot = muzzle end (shot origin, also used for muzzle flashes) - * shell = casings ejection point (must be on the right hand side of the gun) - * g_tuba.md3 - pickup model - * - * 3. fully animated model, muzzle flash handling on h_ model: - * h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model - * tags: - * shot = muzzle end (shot origin, also used for muzzle flashes) - * shell = casings ejection point (must be on the right hand side of the gun) - * handle = corresponding to the origin of v_tuba.md3 (used for muzzle flashes) - * v_tuba.md3 - third person model - * g_tuba.md3 - pickup model - * - * 4. fully animated model, muzzle flash handling on v_ model: - * h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model - * tags: - * shot = muzzle end (shot origin) - * shell = casings ejection point (must be on the right hand side of the gun) - * v_tuba.md3 - third person model - * tags: - * shot = muzzle end (for muzzle flashes) - * g_tuba.md3 - pickup model - */ - -// writes: -// self.origin, self.angles -// self.weaponentity -// self.movedir, self.view_ofs -// attachment stuff -// anim stuff -// to free: -// call again with "" -// remove the ent -void CL_WeaponEntity_SetModel(string name) -{ - float v_shot_idx; - if (name != "") - { - // if there is a child entity, hide it until we're sure we use it - if (self.weaponentity) - self.weaponentity.model = ""; - setmodel(self, strcat("models/weapons/v_", name, ".md3")); // precision set below - v_shot_idx = gettagindex(self, "shot"); // used later - if(!v_shot_idx) - v_shot_idx = gettagindex(self, "tag_shot"); - - setmodel(self, strcat("models/weapons/h_", name, ".iqm")); // precision set below - // preset some defaults that work great for renamed zym files (which don't need an animinfo) - self.anim_fire1 = animfixfps(self, '0 1 0.01', '0 0 0'); - self.anim_fire2 = animfixfps(self, '1 1 0.01', '0 0 0'); - self.anim_idle = animfixfps(self, '2 1 0.01', '0 0 0'); - self.anim_reload = animfixfps(self, '3 1 0.01', '0 0 0'); - - // if we have a "weapon" tag, let's attach the v_ model to it ("invisible hand" style model) - // if we don't, this is a "real" animated model - if(gettagindex(self, "weapon")) - { - if (!self.weaponentity) - self.weaponentity = spawn(); - setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter - setattachment(self.weaponentity, self, "weapon"); - } - else if(gettagindex(self, "tag_weapon")) - { - if (!self.weaponentity) - self.weaponentity = spawn(); - setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter - setattachment(self.weaponentity, self, "tag_weapon"); - } - else - { - if(self.weaponentity) - remove(self.weaponentity); - self.weaponentity = world; - } - - setorigin(self,'0 0 0'); - self.angles = '0 0 0'; - self.frame = 0; - self.viewmodelforclient = world; - - float idx; - - if(v_shot_idx) // v_ model attached to invisible h_ model - { - self.movedir = gettaginfo(self.weaponentity, v_shot_idx); - } - else - { - idx = gettagindex(self, "shot"); - if(!idx) - idx = gettagindex(self, "tag_shot"); - if(idx) - self.movedir = gettaginfo(self, idx); - else - { - print("WARNING: weapon model ", self.model, " does not support the 'shot' tag, will display shots TOTALLY wrong\n"); - self.movedir = '0 0 0'; - } - } - - if(self.weaponentity) // v_ model attached to invisible h_ model - { - idx = gettagindex(self.weaponentity, "shell"); - if(!idx) - idx = gettagindex(self.weaponentity, "tag_shell"); - if(idx) - self.spawnorigin = gettaginfo(self.weaponentity, idx); - } - else - idx = 0; - if(!idx) - { - idx = gettagindex(self, "shell"); - if(!idx) - idx = gettagindex(self, "tag_shell"); - if(idx) - self.spawnorigin = gettaginfo(self, idx); - else - { - print("WARNING: weapon model ", self.model, " does not support the 'shell' tag, will display casings wrong\n"); - self.spawnorigin = self.movedir; - } - } - - if(v_shot_idx) - { - self.oldorigin = '0 0 0'; // use regular attachment - } - else - { - if(self.weaponentity) - { - idx = gettagindex(self, "weapon"); - if(!idx) - idx = gettagindex(self, "tag_weapon"); - } - else - { - idx = gettagindex(self, "handle"); - if(!idx) - idx = gettagindex(self, "tag_handle"); - } - if(idx) - { - self.oldorigin = self.movedir - gettaginfo(self, idx); - } - else - { - print("WARNING: weapon model ", self.model, " does not support the 'handle' tag and neither does the v_ model support the 'shot' tag, will display muzzle flashes TOTALLY wrong\n"); - self.oldorigin = '0 0 0'; // there is no way to recover from this - } - } - - self.viewmodelforclient = self.owner; - } - else - { - self.model = ""; - if(self.weaponentity) - remove(self.weaponentity); - self.weaponentity = world; - self.movedir = '0 0 0'; - self.spawnorigin = '0 0 0'; - self.oldorigin = '0 0 0'; - self.anim_fire1 = '0 1 0.01'; - self.anim_fire2 = '0 1 0.01'; - self.anim_idle = '0 1 0.01'; - self.anim_reload = '0 1 0.01'; - } - - self.view_ofs = '0 0 0'; - - if(self.movedir_x >= 0) - { - vector v0; - v0 = self.movedir; - self.movedir = shotorg_adjust(v0, FALSE, FALSE); - self.view_ofs = shotorg_adjust(v0, FALSE, TRUE) - v0; - } - self.owner.stat_shotorg = compressShotOrigin(self.movedir); - self.movedir = decompressShotOrigin(self.owner.stat_shotorg); // make them match perfectly - - self.spawnorigin += self.view_ofs; // offset the casings origin by the same amount - - // check if an instant weapon switch occurred - setorigin(self, self.view_ofs); - // reset animstate now - self.wframe = WFRAME_IDLE; - setanim(self, self.anim_idle, TRUE, FALSE, TRUE); -} - -vector CL_Weapon_GetShotOrg(float wpn) -{ - entity wi, oldself; - vector ret; - wi = get_weaponinfo(wpn); - oldself = self; - self = spawn(); - CL_WeaponEntity_SetModel(wi.mdl); - ret = self.movedir; - CL_WeaponEntity_SetModel(""); - remove(self); - self = oldself; - return ret; -} - -void CL_Weaponentity_Think() -{ - float tb; - self.nextthink = time; - if (intermission_running) - self.frame = self.anim_idle_x; - if (self.owner.weaponentity != self) - { - if (self.weaponentity) - remove(self.weaponentity); - remove(self); - return; - } - if (self.owner.deadflag != DEAD_NO) - { - self.model = ""; - if (self.weaponentity) - self.weaponentity.model = ""; - return; - } - if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag) - { - self.weaponname = self.owner.weaponname; - self.dmg = self.owner.modelindex; - self.deadflag = self.owner.deadflag; - - CL_WeaponEntity_SetModel(self.owner.weaponname); - } - - tb = (self.effects & (EF_TELEPORT_BIT | EF_RESTARTANIM_BIT)); - self.effects = self.owner.effects & EFMASK_CHEAP; - self.effects &= ~EF_LOWPRECISION; - self.effects &= ~EF_FULLBRIGHT; // can mask team color, so get rid of it - self.effects &= ~EF_TELEPORT_BIT; - self.effects &= ~EF_RESTARTANIM_BIT; - self.effects |= tb; - - if(self.owner.alpha == default_player_alpha) - self.alpha = default_weapon_alpha; - else if(self.owner.alpha != 0) - self.alpha = self.owner.alpha; - else - self.alpha = 1; - - self.glowmod = self.owner.weaponentity_glowmod; - self.colormap = self.owner.colormap; - if (self.weaponentity) - { - self.weaponentity.effects = self.effects; - self.weaponentity.alpha = self.alpha; - self.weaponentity.colormap = self.colormap; - self.weaponentity.glowmod = self.glowmod; - } - - self.angles = '0 0 0'; - - float f = (self.owner.weapon_nextthink - time); - if (self.state == WS_RAISE && !intermission_running) - { - entity newwep = get_weaponinfo(self.owner.switchweapon); - f = f * g_weaponratefactor / max(f, cvar(sprintf("g_balance_%s_switchdelay_raise", newwep.netname))); - //printf("CL_Weaponentity_Think(): cvar: %s, value: %f, nextthink: %f\n", sprintf("g_balance_%s_switchdelay_raise", newwep.netname), cvar(sprintf("g_balance_%s_switchdelay_raise", newwep.netname)), (self.owner.weapon_nextthink - time)); - self.angles_x = -90 * f * f; - } - else if (self.state == WS_DROP && !intermission_running) - { - entity oldwep = get_weaponinfo(self.owner.weapon); - f = 1 - f * g_weaponratefactor / max(f, cvar(sprintf("g_balance_%s_switchdelay_drop", oldwep.netname))); - //printf("CL_Weaponentity_Think(): cvar: %s, value: %f, nextthink: %f\n", sprintf("g_balance_%s_switchdelay_drop", oldwep.netname), cvar(sprintf("g_balance_%s_switchdelay_drop", oldwep.netname)), (self.owner.weapon_nextthink - time)); - self.angles_x = -90 * f * f; - } - else if (self.state == WS_CLEAR) - { - f = 1; - self.angles_x = -90 * f * f; - } -} - -void CL_ExteriorWeaponentity_Think() -{ - float tag_found; - self.nextthink = time; - if (self.owner.exteriorweaponentity != self) - { - remove(self); - return; - } - if (self.owner.deadflag != DEAD_NO) - { - self.model = ""; - return; - } - if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag) - { - self.weaponname = self.owner.weaponname; - self.dmg = self.owner.modelindex; - self.deadflag = self.owner.deadflag; - if (self.owner.weaponname != "") - setmodel(self, strcat("models/weapons/v_", self.owner.weaponname, ".md3")); // precision set below - else - self.model = ""; - - if((tag_found = gettagindex(self.owner, "tag_weapon"))) - { - self.tag_index = tag_found; - self.tag_entity = self.owner; - } - else - setattachment(self, self.owner, "bip01 r hand"); - } - self.effects = self.owner.effects; - self.effects |= EF_LOWPRECISION; - self.effects = self.effects & EFMASK_CHEAP; // eat performance - if(self.owner.alpha == default_player_alpha) - self.alpha = default_weapon_alpha; - else if(self.owner.alpha != 0) - self.alpha = self.owner.alpha; - else - self.alpha = 1; - - self.glowmod = self.owner.weaponentity_glowmod; - self.colormap = self.owner.colormap; - - CSQCMODEL_AUTOUPDATE(); -} - -// spawning weaponentity for client -void CL_SpawnWeaponentity() -{ - self.weaponentity = spawn(); - self.weaponentity.classname = "weaponentity"; - self.weaponentity.solid = SOLID_NOT; - self.weaponentity.owner = self; - setmodel(self.weaponentity, ""); // precision set when changed - setorigin(self.weaponentity, '0 0 0'); - self.weaponentity.angles = '0 0 0'; - self.weaponentity.viewmodelforclient = self; - self.weaponentity.flags = 0; - self.weaponentity.think = CL_Weaponentity_Think; - self.weaponentity.customizeentityforclient = CL_Weaponentity_CustomizeEntityForClient; - self.weaponentity.nextthink = time; - - self.exteriorweaponentity = spawn(); - self.exteriorweaponentity.classname = "exteriorweaponentity"; - self.exteriorweaponentity.solid = SOLID_NOT; - self.exteriorweaponentity.exteriorweaponentity = self.exteriorweaponentity; - self.exteriorweaponentity.owner = self; - setorigin(self.exteriorweaponentity, '0 0 0'); - self.exteriorweaponentity.angles = '0 0 0'; - self.exteriorweaponentity.think = CL_ExteriorWeaponentity_Think; - self.exteriorweaponentity.nextthink = time; - - { - entity oldself = self; - self = self.exteriorweaponentity; - CSQCMODEL_AUTOINIT(); - self = oldself; - } -} - -void Send_WeaponComplain (entity e, float wpn, string wpnname, float type) -{ - msg_entity = e; - WriteByte(MSG_ONE, SVC_TEMPENTITY); - WriteByte(MSG_ONE, TE_CSQC_WEAPONCOMPLAIN); - WriteByte(MSG_ONE, wpn); - WriteString(MSG_ONE, wpnname); - WriteByte(MSG_ONE, type); -} - -.float hasweapon_complain_spam; - -float client_hasweapon(entity cl, float wpn, float andammo, float complain) -{ - float f; - entity oldself; - - if(time < self.hasweapon_complain_spam) - complain = 0; - if(complain) - self.hasweapon_complain_spam = time + 0.2; - - if(wpn == WEP_HOOK && !g_grappling_hook && autocvar_g_nades && !((cl.weapons | weaponsInMap) & WepSet_FromWeapon(wpn))) - complain = 0; - - if (wpn < WEP_FIRST || wpn > WEP_LAST) - { - if (complain) - sprint(self, "Invalid weapon\n"); - return FALSE; - } - if (cl.weapons & WepSet_FromWeapon(wpn)) - { - if (andammo) - { - if(cl.items & IT_UNLIMITED_WEAPON_AMMO) - { - f = 1; - } - else - { - oldself = self; - self = cl; - f = weapon_action(wpn, WR_CHECKAMMO1); - f = f + weapon_action(wpn, WR_CHECKAMMO2); - - // always allow selecting the Mine Layer if we placed mines, so that we can detonate them - entity mine; - if(wpn == WEP_MINE_LAYER) - for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self) - f = 1; - - self = oldself; - } - if (!f) - { - if (complain) - if(IS_REAL_CLIENT(cl)) - { - play2(cl, "weapons/unavailable.wav"); - Send_WeaponComplain (cl, wpn, W_Name(wpn), 0); - } - return FALSE; - } - } - return TRUE; - } - if (complain) - { - // DRESK - 3/16/07 - // Report Proper Weapon Status / Modified Weapon Ownership Message - if (weaponsInMap & WepSet_FromWeapon(wpn)) - { - Send_WeaponComplain(cl, wpn, W_Name(wpn), 1); - - if(autocvar_g_showweaponspawns) - { - entity e; - string s; - - e = get_weaponinfo(wpn); - s = e.model2; - - for(e = world; (e = findfloat(e, weapon, wpn)); ) - { - if(e.classname == "droppedweapon") - continue; - if (!(e.flags & FL_ITEM)) - continue; - WaypointSprite_Spawn( - s, - 1, 0, - world, e.origin, - self, 0, - world, enemy, - 0, - RADARICON_NONE, '0 0 0' - ); - } - } - } - else - { - Send_WeaponComplain (cl, wpn, W_Name(wpn), 2); - } - - play2(cl, "weapons/unavailable.wav"); - } - return FALSE; -} - -// Weapon subs -void w_clear() -{ - if (self.weapon != -1) - { - self.weapon = 0; - self.switchingweapon = 0; - } - if (self.weaponentity) - { - self.weaponentity.state = WS_CLEAR; - self.weaponentity.effects = 0; - } -} - -void w_ready() -{ - if (self.weaponentity) - self.weaponentity.state = WS_READY; - weapon_thinkf(WFRAME_IDLE, 1000000, w_ready); -} - -// Setup weapon for client (after this raise frame will be launched) -void weapon_setup(float windex) -{ - entity e; - e = get_weaponinfo(windex); - self.items &= ~IT_AMMO; - self.items = self.items | (e.items & IT_AMMO); - - // the two weapon entities will notice this has changed and update their models - self.weapon = windex; - self.switchingweapon = windex; // to make sure - self.weaponname = e.mdl; - self.bulletcounter = 0; -} - -// perform weapon to attack (weaponstate and attack_finished check is here) -void W_SwitchToOtherWeapon(entity pl) -{ - // hack to ensure it switches to an OTHER weapon (in case the other fire mode still has ammo, we want that anyway) - float w, ww; - w = pl.weapon; - if(pl.weapons & WepSet_FromWeapon(w)) - { - pl.weapons &= ~WepSet_FromWeapon(w); - ww = w_getbestweapon(pl); - pl.weapons |= WepSet_FromWeapon(w); - } - else - ww = w_getbestweapon(pl); - if(ww) - W_SwitchWeapon_Force(pl, ww); -} - -.float prevdryfire; -.float prevwarntime; -float weapon_prepareattack_checkammo(float secondary) -{ - if (!(self.items & IT_UNLIMITED_WEAPON_AMMO)) - if (!weapon_action(self.weapon, WR_CHECKAMMO1 + secondary)) - { - // always keep the Mine Layer if we placed mines, so that we can detonate them - entity mine; - if(self.weapon == WEP_MINE_LAYER) - for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self) - return FALSE; - - if(self.weapon == self.switchweapon && time - self.prevdryfire > 1) // only play once BEFORE starting to switch weapons - { - sound (self, CH_WEAPON_A, "weapons/dryfire.wav", VOL_BASE, ATTEN_NORM); - self.prevdryfire = time; - } - - if(weapon_action(self.weapon, WR_CHECKAMMO2 - secondary)) // check if the other firing mode has enough ammo - { - if(time - self.prevwarntime > 1) - { - Send_Notification( - NOTIF_ONE, - self, - MSG_MULTI, - ITEM_WEAPON_PRIMORSEC, - self.weapon, - secondary, - (1 - secondary) - ); - } - self.prevwarntime = time; - } - else // this weapon is totally unable to fire, switch to another one - { - W_SwitchToOtherWeapon(self); - } - - return FALSE; - } - return TRUE; -} -.float race_penalty; -float weapon_prepareattack_check(float secondary, float attacktime) -{ - if(!weapon_prepareattack_checkammo(secondary)) - return FALSE; - - //if sv_ready_restart_after_countdown is set, don't allow the player to shoot - //if all players readied up and the countdown is running - if(time < game_starttime || time < self.race_penalty) { - return FALSE; - } - - if (timeout_status == TIMEOUT_ACTIVE) //don't allow the player to shoot while game is paused - return FALSE; - - // do not even think about shooting if switching - if(self.switchweapon != self.weapon) - return FALSE; - - if(attacktime >= 0) - { - // don't fire if previous attack is not finished - if (ATTACK_FINISHED(self) > time + self.weapon_frametime * 0.5) - return FALSE; - // don't fire while changing weapon - if (self.weaponentity.state != WS_READY) - return FALSE; - } - - return TRUE; -} -float weapon_prepareattack_do(float secondary, float attacktime) -{ - self.weaponentity.state = WS_INUSE; - - self.spawnshieldtime = min(self.spawnshieldtime, time); // kill spawn shield when you fire - - // if the weapon hasn't been firing continuously, reset the timer - if(attacktime >= 0) - { - if (ATTACK_FINISHED(self) < time - self.weapon_frametime * 1.5) - { - ATTACK_FINISHED(self) = time; - //dprint("resetting attack finished to ", ftos(time), "\n"); - } - ATTACK_FINISHED(self) = ATTACK_FINISHED(self) + attacktime * W_WeaponRateFactor(); - } - self.bulletcounter += 1; - //dprint("attack finished ", ftos(ATTACK_FINISHED(self)), "\n"); - return TRUE; -} -float weapon_prepareattack(float secondary, float attacktime) -{ - if(weapon_prepareattack_check(secondary, attacktime)) - { - weapon_prepareattack_do(secondary, attacktime); - return TRUE; - } - else - return FALSE; -} - -void weapon_thinkf(float fr, float t, void() func) -{ - vector a; - vector of, or, ou; - float restartanim; - - if(fr == WFRAME_DONTCHANGE) - { - fr = self.weaponentity.wframe; - restartanim = FALSE; - } - else if (fr == WFRAME_IDLE) - restartanim = FALSE; - else - restartanim = TRUE; - - of = v_forward; - or = v_right; - ou = v_up; - - if (self.weaponentity) - { - self.weaponentity.wframe = fr; - a = '0 0 0'; - if (fr == WFRAME_IDLE) - a = self.weaponentity.anim_idle; - else if (fr == WFRAME_FIRE1) - a = self.weaponentity.anim_fire1; - else if (fr == WFRAME_FIRE2) - a = self.weaponentity.anim_fire2; - else // if (fr == WFRAME_RELOAD) - a = self.weaponentity.anim_reload; - a_z *= g_weaponratefactor; - setanim(self.weaponentity, a, restartanim == FALSE, restartanim, restartanim); - } - - v_forward = of; - v_right = or; - v_up = ou; - - if(self.weapon_think == w_ready && func != w_ready && self.weaponentity.state == WS_RAISE) - { - backtrace("Tried to override initial weapon think function - should this really happen?"); - } - - t *= W_WeaponRateFactor(); - - // VorteX: haste can be added here - if (self.weapon_think == w_ready) - { - self.weapon_nextthink = time; - //dprint("started firing at ", ftos(time), "\n"); - } - if (self.weapon_nextthink < time - self.weapon_frametime * 1.5 || self.weapon_nextthink > time + self.weapon_frametime * 1.5) - { - self.weapon_nextthink = time; - //dprint("reset weapon animation timer at ", ftos(time), "\n"); - } - self.weapon_nextthink = self.weapon_nextthink + t; - self.weapon_think = func; - //dprint("next ", ftos(self.weapon_nextthink), "\n"); - - if((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t) - { - if(self.weapon == WEP_SHOTGUN && fr == WFRAME_FIRE2) - animdecide_setaction(self, ANIMACTION_MELEE, restartanim); - else - animdecide_setaction(self, ANIMACTION_SHOOT, restartanim); - } - else - { - if(self.anim_upper_action == ANIMACTION_SHOOT || self.anim_upper_action == ANIMACTION_MELEE) - self.anim_upper_action = 0; - } -} - -void weapon_boblayer1(float spd, vector org) -{ - // VorteX: haste can be added here -} - -vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity, float forceAbsolute) -{ - vector mdirection; - float mspeed; - vector outvelocity; - - mvelocity = mvelocity * g_weaponspeedfactor; - - mdirection = normalize(mvelocity); - mspeed = vlen(mvelocity); - - outvelocity = get_shotvelocity(pvelocity, mdirection, mspeed, (forceAbsolute ? 0 : autocvar_g_projectiles_newton_style), autocvar_g_projectiles_newton_style_2_minfactor, autocvar_g_projectiles_newton_style_2_maxfactor); - - return outvelocity; -} - -void W_AttachToShotorg(entity flash, vector offset) -{ - entity xflash; - flash.owner = self; - flash.angles_z = random() * 360; - - if(gettagindex(self.weaponentity, "shot")) - setattachment(flash, self.weaponentity, "shot"); - else - setattachment(flash, self.weaponentity, "tag_shot"); - setorigin(flash, offset); - - xflash = spawn(); - copyentity(flash, xflash); - - flash.viewmodelforclient = self; - - if(self.weaponentity.oldorigin_x > 0) - { - setattachment(xflash, self.exteriorweaponentity, ""); - setorigin(xflash, self.weaponentity.oldorigin + offset); - } - else - { - if(gettagindex(self.exteriorweaponentity, "shot")) - setattachment(xflash, self.exteriorweaponentity, "shot"); - else - setattachment(xflash, self.exteriorweaponentity, "tag_shot"); - setorigin(xflash, offset); - } -} - -vector cliptoplane(vector v, vector p) -{ - return v - (v * p) * p; -} - -vector solve_cubic_pq(float p, float q) -{ - float D, u, v, a; - D = q*q/4.0 + p*p*p/27.0; - if(D < 0) - { - // irreducibilis - a = 1.0/3.0 * acos(-q/2.0 * sqrt(-27.0/(p*p*p))); - u = sqrt(-4.0/3.0 * p); - // a in range 0..pi/3 - // cos(a) - // cos(a + 2pi/3) - // cos(a + 4pi/3) - return - u * - ( - '1 0 0' * cos(a + 2.0/3.0*M_PI) - + - '0 1 0' * cos(a + 4.0/3.0*M_PI) - + - '0 0 1' * cos(a) - ); - } - else if(D == 0) - { - // simple - if(p == 0) - return '0 0 0'; - u = 3*q/p; - v = -u/2; - if(u >= v) - return '1 1 0' * v + '0 0 1' * u; - else - return '0 1 1' * v + '1 0 0' * u; - } - else - { - // cardano - u = cbrt(-q/2.0 + sqrt(D)); - v = cbrt(-q/2.0 - sqrt(D)); - return '1 1 1' * (u + v); - } -} -vector solve_cubic_abcd(float a, float b, float c, float d) -{ - // y = 3*a*x + b - // x = (y - b) / 3a - float p, q; - vector v; - p = (9*a*c - 3*b*b); - q = (27*a*a*d - 9*a*b*c + 2*b*b*b); - v = solve_cubic_pq(p, q); - v = (v - b * '1 1 1') * (1.0 / (3.0 * a)); - if(a < 0) - v += '1 0 -1' * (v_z - v_x); // swap x, z - return v; -} - -vector findperpendicular(vector v) -{ - vector p; - p_x = v_z; - p_y = -v_x; - p_z = v_y; - return normalize(cliptoplane(p, v)); -} - -vector W_CalculateProjectileSpread(vector forward, float spread) -{ - float sigma; - vector v1 = '0 0 0', v2; - float dx, dy, r; - float sstyle; - spread *= g_weaponspreadfactor; - if(spread <= 0) - return forward; - sstyle = autocvar_g_projectiles_spread_style; - - if(sstyle == 0) - { - // this is the baseline for the spread value! - // standard deviation: sqrt(2/5) - // density function: sqrt(1-r^2) - return forward + randomvec() * spread; - } - else if(sstyle == 1) - { - // same thing, basically - return normalize(forward + cliptoplane(randomvec() * spread, forward)); - } - else if(sstyle == 2) - { - // circle spread... has at sigma=1 a standard deviation of sqrt(1/2) - sigma = spread * 0.89442719099991587855; // match baseline stddev - v1 = findperpendicular(forward); - v2 = cross(forward, v1); - // random point on unit circle - dx = random() * 2 * M_PI; - dy = sin(dx); - dx = cos(dx); - // radius in our dist function - r = random(); - r = sqrt(r); - return normalize(forward + (v1 * dx + v2 * dy) * r * sigma); - } - else if(sstyle == 3) // gauss 3d - { - sigma = spread * 0.44721359549996; // match baseline stddev - // note: 2D gaussian has sqrt(2) times the stddev of 1D, so this factor is right - v1 = forward; - v1_x += gsl_ran_gaussian(sigma); - v1_y += gsl_ran_gaussian(sigma); - v1_z += gsl_ran_gaussian(sigma); - return v1; - } - else if(sstyle == 4) // gauss 2d - { - sigma = spread * 0.44721359549996; // match baseline stddev - // note: 2D gaussian has sqrt(2) times the stddev of 1D, so this factor is right - v1_x = gsl_ran_gaussian(sigma); - v1_y = gsl_ran_gaussian(sigma); - v1_z = gsl_ran_gaussian(sigma); - return normalize(forward + cliptoplane(v1, forward)); - } - else if(sstyle == 5) // 1-r - { - sigma = spread * 1.154700538379252; // match baseline stddev - v1 = findperpendicular(forward); - v2 = cross(forward, v1); - // random point on unit circle - dx = random() * 2 * M_PI; - dy = sin(dx); - dx = cos(dx); - // radius in our dist function - r = random(); - r = solve_cubic_abcd(-2, 3, 0, -r) * '0 1 0'; - return normalize(forward + (v1 * dx + v2 * dy) * r * sigma); - } - else if(sstyle == 6) // 1-r^2 - { - sigma = spread * 1.095445115010332; // match baseline stddev - v1 = findperpendicular(forward); - v2 = cross(forward, v1); - // random point on unit circle - dx = random() * 2 * M_PI; - dy = sin(dx); - dx = cos(dx); - // radius in our dist function - r = random(); - r = sqrt(1 - r); - r = sqrt(1 - r); - return normalize(forward + (v1 * dx + v2 * dy) * r * sigma); - } - else if(sstyle == 7) // (1-r) (2-r) - { - sigma = spread * 1.224744871391589; // match baseline stddev - v1 = findperpendicular(forward); - v2 = cross(forward, v1); - // random point on unit circle - dx = random() * 2 * M_PI; - dy = sin(dx); - dx = cos(dx); - // radius in our dist function - r = random(); - r = 1 - sqrt(r); - r = 1 - sqrt(r); - return normalize(forward + (v1 * dx + v2 * dy) * r * sigma); - } - else - error("g_projectiles_spread_style must be 0 (sphere), 1 (flattened sphere), 2 (circle), 3 (gauss 3D), 4 (gauss plane), 5 (linear falloff), 6 (quadratic falloff), 7 (stronger falloff)!"); - return '0 0 0'; - /* - * how to derive falloff functions: - * rho(r) := (2-r) * (1-r); - * a : 0; - * b : 1; - * rhor(r) := r * rho(r); - * cr(t) := integrate(rhor(r), r, a, t); - * scr(t) := integrate(rhor(r) * r^2, r, a, t); - * variance : scr(b) / cr(b); - * solve(cr(r) = rand * cr(b), r), programmmode:false; - * sqrt(0.4 / variance), numer; - */ -} - -#if 0 -float mspercallsum; -float mspercallsstyle; -float mspercallcount; -#endif -void W_SetupProjectileVelocityEx(entity missile, vector dir, vector upDir, float pSpeed, float pUpSpeed, float pZSpeed, float spread, float forceAbsolute) -{ - if(missile.owner == world) - error("Unowned missile"); - - dir = dir + upDir * (pUpSpeed / pSpeed); - dir_z += pZSpeed / pSpeed; - pSpeed *= vlen(dir); - dir = normalize(dir); - -#if 0 - if(autocvar_g_projectiles_spread_style != mspercallsstyle) - { - mspercallsum = mspercallcount = 0; - mspercallsstyle = autocvar_g_projectiles_spread_style; - } - mspercallsum -= gettime(GETTIME_HIRES); -#endif - dir = W_CalculateProjectileSpread(dir, spread); -#if 0 - mspercallsum += gettime(GETTIME_HIRES); - mspercallcount += 1; - print("avg: ", ftos(mspercallcount / mspercallsum), " per sec\n"); -#endif - - missile.velocity = W_CalculateProjectileVelocity(missile.owner.velocity, pSpeed * dir, forceAbsolute); -} - -void W_SetupProjectileVelocity(entity missile, float pSpeed, float spread) -{ - W_SetupProjectileVelocityEx(missile, w_shotdir, v_up, pSpeed, 0, 0, spread, FALSE); -} - -#define W_SETUPPROJECTILEVELOCITY_UP(m,s) W_SetupProjectileVelocityEx(m, w_shotdir, v_up, cvar(#s "_speed"), cvar(#s "_speed_up"), cvar(#s "_speed_z"), cvar(#s "_spread"), FALSE) -#define W_SETUPPROJECTILEVELOCITY(m,s) W_SetupProjectileVelocityEx(m, w_shotdir, v_up, cvar(#s "_speed"), 0, 0, cvar(#s "_spread"), FALSE) - -void W_DecreaseAmmo(.float ammo_type, float ammo_use, float ammo_reload) -{ - if((self.items & IT_UNLIMITED_WEAPON_AMMO) && !ammo_reload) - return; - - // if this weapon is reloadable, decrease its load. Else decrease the player's ammo - if(ammo_reload) - { - self.clip_load -= ammo_use; - self.(weapon_load[self.weapon]) = self.clip_load; - } - else - self.(self.current_ammo) -= ammo_use; -} - -// weapon reloading code - -.float reload_ammo_amount, reload_ammo_min, reload_time; -.float reload_complain; -.string reload_sound; - -void W_ReloadedAndReady() -{ - // finish the reloading process, and do the ammo transfer - - self.clip_load = self.old_clip_load; // restore the ammo counter, in case we still had ammo in the weapon before reloading - - // if the gun uses no ammo, max out weapon load, else decrease ammo as we increase weapon load - if(!self.reload_ammo_min || self.items & IT_UNLIMITED_WEAPON_AMMO) - self.clip_load = self.reload_ammo_amount; - else - { - while(self.clip_load < self.reload_ammo_amount && self.(self.current_ammo)) // make sure we don't add more ammo than we have - { - self.clip_load += 1; - self.(self.current_ammo) -= 1; - } - } - self.(weapon_load[self.weapon]) = self.clip_load; - - // do not set ATTACK_FINISHED in reload code any more. This causes annoying delays if eg: You start reloading a weapon, - // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there, - // so your weapon is disabled for a few seconds without reason - - //ATTACK_FINISHED(self) -= self.reload_time - 1; - - w_ready(); -} - -void W_Reload(float sent_ammo_min, float sent_ammo_amount, float sent_time, string sent_sound) -{ - // set global values to work with - - self.reload_ammo_min = sent_ammo_min; - self.reload_ammo_amount = sent_ammo_amount; - self.reload_time = sent_time; - self.reload_sound = sent_sound; - - // check if we meet the necessary conditions to reload - - entity e; - e = get_weaponinfo(self.weapon); - - // don't reload weapons that don't have the RELOADABLE flag - if (!(e.spawnflags & WEP_FLAG_RELOADABLE)) - { - dprint("Warning: Attempted to reload a weapon that does not have the WEP_FLAG_RELOADABLE flag. Fix your code!\n"); - return; - } - - // return if reloading is disabled for this weapon - if(!self.reload_ammo_amount) - return; - - // our weapon is fully loaded, no need to reload - if (self.clip_load >= self.reload_ammo_amount) - return; - - // no ammo, so nothing to load - if(!self.(self.current_ammo) && self.reload_ammo_min) - if (!(self.items & IT_UNLIMITED_WEAPON_AMMO)) - { - if(IS_REAL_CLIENT(self) && self.reload_complain < time) - { - play2(self, "weapons/unavailable.wav"); - sprint(self, strcat("You don't have enough ammo to reload the ^2", W_Name(self.weapon), "\n")); - self.reload_complain = time + 1; - } - // switch away if the amount of ammo is not enough to keep using this weapon - if (!(weapon_action(self.weapon, WR_CHECKAMMO1) + weapon_action(self.weapon, WR_CHECKAMMO2))) - { - self.clip_load = -1; // reload later - W_SwitchToOtherWeapon(self); - } - return; - } - - if (self.weaponentity) - { - if (self.weaponentity.wframe == WFRAME_RELOAD) - return; - - // allow switching away while reloading, but this will cause a new reload! - self.weaponentity.state = WS_READY; - } - - // now begin the reloading process - - sound (self, CH_WEAPON_SINGLE, self.reload_sound, VOL_BASE, ATTEN_NORM); - - // do not set ATTACK_FINISHED in reload code any more. This causes annoying delays if eg: You start reloading a weapon, - // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there, - // so your weapon is disabled for a few seconds without reason - - //ATTACK_FINISHED(self) = max(time, ATTACK_FINISHED(self)) + self.reload_time + 1; - - weapon_thinkf(WFRAME_RELOAD, self.reload_time, W_ReloadedAndReady); - - if(self.clip_load < 0) - self.clip_load = 0; - self.old_clip_load = self.clip_load; - self.clip_load = self.(weapon_load[self.weapon]) = -1; -} diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index 51a04b5913..7d67c4f30d 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -152,6 +152,8 @@ void ClientCommand_join(float request) { if(!IS_PLAYER(self) && !lockteams) { + if(self.caplayer) + return; if(nJoinAllowed(self)) { if(autocvar_g_campaign) { campaign_bots_may_start = 1; } @@ -580,15 +582,11 @@ void ClientCommand_spectate(float request) } } - if(IS_PLAYER(self) && autocvar_sv_spectate == 1) - ClientKill_TeamChange(-2); // observe - - // in CA, allow a dead player to move to spectators (without that, caplayer!=0 will be moved back to the player list) - // note: if arena game mode is ever done properly, this needs to be removed. - if(self.caplayer && (IS_SPEC(self) || IS_OBSERVER(self))) + if((IS_PLAYER(self) || self.caplayer) && autocvar_sv_spectate == 1) { - sprint(self, "WARNING: you will spectate in the next round.\n"); - self.caplayer = 0; + if(self.caplayer && (IS_SPEC(self) || IS_OBSERVER(self))) + Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_CA_LEAVE); + ClientKill_TeamChange(-2); // observe } } return; // never fall through to usage diff --git a/qcsrc/server/command/sv_cmd.qc b/qcsrc/server/command/sv_cmd.qc index c0da029c51..edeb571c0d 100644 --- a/qcsrc/server/command/sv_cmd.qc +++ b/qcsrc/server/command/sv_cmd.qc @@ -213,6 +213,8 @@ void GameCommand_allspec(float request, float argc) FOR_EACH_REALPLAYER(client) { self = client; + if(self.caplayer) + self.caplayer = 0; PutObserverInServer(); ++i; } @@ -1045,6 +1047,8 @@ void GameCommand_moveplayer(float request, float argc) if(!IS_SPEC(client) && !IS_OBSERVER(client)) { self = client; + if(self.caplayer) + self.caplayer = 0; PutObserverInServer(); successful = strcat(successful, (successful ? ", " : ""), client.netname); @@ -1150,9 +1154,10 @@ void GameCommand_nospectators(float request) { blockSpectators = 1; entity plr; - FOR_EACH_CLIENT(plr) //give every spectator seconds time to become a player + FOR_EACH_REALCLIENT(plr) //give every spectator seconds time to become a player { if(IS_SPEC(plr) || IS_OBSERVER(plr)) + if(!plr.caplayer) { plr.spectatortime = time; Send_Notification(NOTIF_ONE_ONLY, plr, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime); diff --git a/qcsrc/server/csqceffects.qc b/qcsrc/server/csqceffects.qc index 4b626546d6..ea13aedd55 100644 --- a/qcsrc/server/csqceffects.qc +++ b/qcsrc/server/csqceffects.qc @@ -1,7 +1,7 @@ void te_csqc_lightningarc(vector from,vector to) { WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); - WriteByte(MSG_BROADCAST, TE_CSQC_LIGHTNINGARC); + WriteByte(MSG_BROADCAST, TE_CSQC_ARC); WriteCoord(MSG_BROADCAST, from_x); WriteCoord(MSG_BROADCAST, from_y); diff --git a/qcsrc/server/csqcprojectile.qc b/qcsrc/server/csqcprojectile.qc deleted file mode 100644 index 3c747bdbb2..0000000000 --- a/qcsrc/server/csqcprojectile.qc +++ /dev/null @@ -1,118 +0,0 @@ -.float csqcprojectile_type; - -float CSQCProjectile_SendEntity(entity to, float sf) -{ - float ft, fr; - - // note: flag 0x08 = no trail please (teleport bit) - sf = sf & 0x0F; - - if(self.csqcprojectile_clientanimate) - sf |= 0x80; // client animated, not interpolated - - if(self.flags & FL_ONGROUND) - sf |= 0x40; - - ft = fr = 0; - if(self.fade_time != 0 || self.fade_rate != 0) - { - ft = (self.fade_time - time) / sys_frametime; - fr = (1 / self.fade_rate) / sys_frametime; - if(ft <= 255 && fr <= 255 && fr >= 1) - sf |= 0x20; - } - - if(self.gravity != 0) - sf |= 0x10; - - WriteByte(MSG_ENTITY, ENT_CLIENT_PROJECTILE); - WriteByte(MSG_ENTITY, sf); - - if(sf & 1) - { - WriteCoord(MSG_ENTITY, self.origin_x); - WriteCoord(MSG_ENTITY, self.origin_y); - WriteCoord(MSG_ENTITY, self.origin_z); - - if(sf & 0x80) - { - WriteCoord(MSG_ENTITY, self.velocity_x); - WriteCoord(MSG_ENTITY, self.velocity_y); - WriteCoord(MSG_ENTITY, self.velocity_z); - if(sf & 0x10) - WriteCoord(MSG_ENTITY, self.gravity); - } - - if(sf & 0x20) - { - WriteByte(MSG_ENTITY, ft); - WriteByte(MSG_ENTITY, fr); - } - - WriteByte(MSG_ENTITY, self.realowner.team); - } - - if(sf & 2) - WriteByte(MSG_ENTITY, self.csqcprojectile_type); // TODO maybe put this into sf? - - return 1; -} - -.vector csqcprojectile_oldorigin; -void CSQCProjectile_Check(entity e) -{ - if(e.csqcprojectile_clientanimate) - if(e.flags & FL_ONGROUND) - if(e.origin != e.csqcprojectile_oldorigin) - UpdateCSQCProjectile(e); - e.csqcprojectile_oldorigin = e.origin; -} - -void CSQCProjectile(entity e, float clientanimate, float type, float docull) -{ - Net_LinkEntity(e, docull, 0, CSQCProjectile_SendEntity); - - e.csqcprojectile_clientanimate = clientanimate; - - if(e.movetype == MOVETYPE_TOSS || e.movetype == MOVETYPE_BOUNCE) - { - if(e.gravity == 0) - e.gravity = 1; - } - else - e.gravity = 0; - - if(!sound_allowed(MSG_BROADCAST, e)) - type |= 0x80; - e.csqcprojectile_type = type; -} - -// FIXME HACK -float ItemSend(entity to, float sf); -void ItemUpdate(entity item); -// END HACK -void UpdateCSQCProjectile(entity e) -{ - if(e.SendEntity == CSQCProjectile_SendEntity) - { - // send new origin data - e.SendFlags |= 0x01; - } -// FIXME HACK - else if(e.SendEntity == ItemSend) - { - ItemUpdate(e); - } -// END HACK -} - -void UpdateCSQCProjectileAfterTeleport(entity e) -{ - if(e.SendEntity == CSQCProjectile_SendEntity) - { - // send new origin data - e.SendFlags |= 0x01; - // mark as teleported - e.SendFlags |= 0x08; - } -} diff --git a/qcsrc/server/csqcprojectile.qh b/qcsrc/server/csqcprojectile.qh deleted file mode 100644 index e00c0988b9..0000000000 --- a/qcsrc/server/csqcprojectile.qh +++ /dev/null @@ -1,6 +0,0 @@ -.float csqcprojectile_clientanimate; - -void CSQCProjectile(entity e, float clientanimate, float type, float docull); -void UpdateCSQCProjectile(entity e); -void UpdateCSQCProjectileAfterTeleport(entity e); -void CSQCProjectile_Check(entity e); diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index b892e24f2b..09fe2a9705 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -82,7 +82,6 @@ float server_is_dedicated; .float pain_finished; //Added by Supajoe .float pain_frame; //" -.float statdraintime; // record the one-second intervals between draining health and armour when they're over 100 .float crouch; // Crouching or not? .float strength_finished; @@ -172,7 +171,7 @@ void setanim(entity e, vector anim, float looping, float override, float restart .string item_pickupsound; // definitions for weaponsystem - +// more WEAPONTODO: move these to their proper files .entity weaponentity; .entity exteriorweaponentity; .vector weaponentity_glowmod; @@ -182,8 +181,9 @@ void setanim(entity e, vector anim, float looping, float override, float restart .float switchingweapon; // weapon currently being switched to (is copied from switchweapon once switch is possible) .string weaponname; // name of .weapon +// WEAPONTODO .float autoswitch; -float weapon_action(float wpn, float wrequest); +//float WEP_ACTION(float wpn, float wrequest); float client_hasweapon(entity cl, float wpn, float andammo, float complain); void w_clear(); void w_ready(); @@ -191,9 +191,6 @@ void w_ready(); .float weapon_nextthink; .void() weapon_think; -//float PLAYER_WEAPONSELECTION_DELAY = ); -const float PLAYER_WEAPONSELECTION_SPEED = 18; -const vector PLAYER_WEAPONSELECTION_RANGE = '0 20 -40'; // weapon states (self.weaponentity.state) const float WS_CLEAR = 0; // no weapon selected @@ -288,16 +285,12 @@ string gamemode_name; float startitem_failed; -typedef .float floatfield; -floatfield Item_CounterField(float it); - -float W_AmmoItemCode(float wpn); -string W_Name(float weaponid); string W_Apply_Weaponreplace(string in); void FixIntermissionClient(entity e); void FixClientCvars(entity e); +// WEAPONTODO: remove this WepSet weaponsInMap; .float respawn_countdown; // next number to count @@ -355,7 +348,6 @@ float next_pingtime; _VOICEMSG(death) \ _VOICEMSG(drown) \ _VOICEMSG(fall) \ - _VOICEMSG(fall) \ _VOICEMSG(falling) \ _VOICEMSG(gasp) \ _VOICEMSG(jump) \ @@ -459,7 +451,7 @@ void W_Porto_Remove (entity p); .string message2; -.float stat_allow_oldnexbeam; +.float stat_allow_oldvortexbeam; // reset to 0 on weapon switch // may be useful to all weapons @@ -494,8 +486,6 @@ void ClientData_Touch(entity e); float servertime, serverprevtime, serverframetime; -.entity soundentity; - .float ammo_fuel; .vector prevorigin; @@ -512,7 +502,6 @@ float servertime, serverprevtime, serverframetime; .float stat_shotorg; // networked stat for trueaim HUD string matchid; -.float hitplotfh; .float last_pickup; @@ -530,8 +519,6 @@ float client_cefc_accumulator; float client_cefc_accumulatortime; #endif -..float current_ammo; - .float weapon_load[WEP_MAXCOUNT]; .float ammo_none; // used by the reloading system, must always be 0 .float clip_load; @@ -540,9 +527,9 @@ float client_cefc_accumulatortime; .entity lastrocket; .float minelayer_mines; -.float nex_charge; -.float nex_charge_rottime; -.float nex_chargepool_ammo; +.float vortex_charge; +.float vortex_charge_rottime; +.float vortex_chargepool_ammo; .float hagar_load; .float grab; // 0 = can't grab, 1 = owner can grab, 2 = owner and team mates can grab, 3 = anyone can grab @@ -562,9 +549,6 @@ string deathmessage; .float just_joined; -.float cvar_cl_accuracy_data_share; -.float cvar_cl_accuracy_data_receive; - .float cvar_cl_weaponimpulsemode; .float selectweapon; // last selected weapon of the player diff --git a/qcsrc/server/func_breakable.qc b/qcsrc/server/func_breakable.qc index d67e79cd4d..0bf71059e6 100644 --- a/qcsrc/server/func_breakable.qc +++ b/qcsrc/server/func_breakable.qc @@ -173,7 +173,7 @@ void func_breakable_destroy() { sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM); if(self.dmg) - RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, self.dmg_force, DEATH_HURTTRIGGER, world); + RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, world, self.dmg_force, DEATH_HURTTRIGGER, world); if(self.cnt) pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count); diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index 02488b8b11..0e8bef06b7 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -115,7 +115,7 @@ void GiveFrags (entity attacker, entity targ, float f, float deathtype) else if(!(attacker.weapons & WepSet_FromWeapon(culprit))) culprit = attacker.weapon; - if(g_weaponarena_random_with_laser && culprit == WEP_LASER) + if(g_weaponarena_random_with_blaster && culprit == WEP_BLASTER) // WEAPONTODO: Shouldn't this be in a mutator? { // no exchange } @@ -287,7 +287,7 @@ float Obituary_WeaponDeath( if(death_weapon) { w_deathtype = deathtype; - float death_message = weapon_action(death_weapon, ((murder) ? WR_KILLMESSAGE : WR_SUICIDEMESSAGE)); + float death_message = WEP_ACTION(death_weapon, ((murder) ? WR_KILLMESSAGE : WR_SUICIDEMESSAGE)); w_deathtype = FALSE; if(death_message) @@ -948,11 +948,10 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float } float RadiusDamage_running; -float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity ignore, float forceintensity, float deathtype, entity directhitentity) +float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float inflictorselfdamage, float forceintensity, float deathtype, entity directhitentity) // Returns total damage applies to creatures { entity targ; - vector blastorigin; vector force; float total_damage_to_creatures; entity next; @@ -972,202 +971,161 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e tfloordmg = autocvar_g_throughfloor_damage; tfloorforce = autocvar_g_throughfloor_force; - blastorigin = (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5); total_damage_to_creatures = 0; if(deathtype != (WEP_HOOK | HITTYPE_SECONDARY | HITTYPE_BOUNCE)) // only send gravity bomb damage once if(DEATH_WEAPONOF(deathtype) != WEP_TUBA) // do not send tuba damage (bandwidth hog) { - force = inflictor.velocity; + force = inflictorvelocity; if(vlen(force) == 0) force = '0 0 -1'; else force = normalize(force); if(forceintensity >= 0) - Damage_DamageInfo(blastorigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker); + Damage_DamageInfo(inflictororigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker); else - Damage_DamageInfo(blastorigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker); + Damage_DamageInfo(inflictororigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker); } stat_damagedone = 0; - targ = WarpZone_FindRadius (blastorigin, rad + MAX_DAMAGEEXTRARADIUS, FALSE); + targ = WarpZone_FindRadius (inflictororigin, rad + MAX_DAMAGEEXTRARADIUS, FALSE); while (targ) { next = targ.chain; - if (targ != inflictor) - if (ignore != targ) if(targ.takedamage) + if ((targ != inflictor) || inflictorselfdamage) + if (((cantbe != targ) && !mustbe) || (mustbe == targ)) + if (targ.takedamage) + { + vector nearest; + vector diff; + float power; + + // LordHavoc: measure distance to nearest point on target (not origin) + // (this guarentees 100% damage on a touch impact) + nearest = targ.WarpZone_findradius_nearest; + diff = targ.WarpZone_findradius_dist; + // round up a little on the damage to ensure full damage on impacts + // and turn the distance into a fraction of the radius + power = 1 - ((vlen (diff) - bound(MIN_DAMAGEEXTRARADIUS, targ.damageextraradius, MAX_DAMAGEEXTRARADIUS)) / rad); + //bprint(" "); + //bprint(ftos(power)); + //if (targ == attacker) + // print(ftos(power), "\n"); + if (power > 0) { - vector nearest; - vector diff; - float power; - - // LordHavoc: measure distance to nearest point on target (not origin) - // (this guarentees 100% damage on a touch impact) - nearest = targ.WarpZone_findradius_nearest; - diff = targ.WarpZone_findradius_dist; - // round up a little on the damage to ensure full damage on impacts - // and turn the distance into a fraction of the radius - power = 1 - ((vlen (diff) - bound(MIN_DAMAGEEXTRARADIUS, targ.damageextraradius, MAX_DAMAGEEXTRARADIUS)) / rad); - //bprint(" "); - //bprint(ftos(power)); - //if (targ == attacker) - // print(ftos(power), "\n"); - if (power > 0) + float finaldmg; + if (power > 1) + power = 1; + finaldmg = coredamage * power + edgedamage * (1 - power); + if (finaldmg > 0) { - float finaldmg; - if (power > 1) - power = 1; - finaldmg = coredamage * power + edgedamage * (1 - power); - if (finaldmg > 0) - { - float a; - float c; - vector hitloc; - vector myblastorigin; - vector center; + float a; + float c; + vector hitloc; + vector myblastorigin; + vector center; - myblastorigin = WarpZone_TransformOrigin(targ, blastorigin); + myblastorigin = WarpZone_TransformOrigin(targ, inflictororigin); - // if it's a player, use the view origin as reference - center = CENTER_OR_VIEWOFS(targ); + // if it's a player, use the view origin as reference + center = CENTER_OR_VIEWOFS(targ); - force = normalize(center - myblastorigin); - force = force * (finaldmg / coredamage) * forceintensity; - hitloc = nearest; + force = normalize(center - myblastorigin); + force = force * (finaldmg / coredamage) * forceintensity; + hitloc = nearest; - if(targ != directhitentity) - { - float hits; - float total; - float hitratio; - float mininv_f, mininv_d; + if(deathtype & WEP_BLASTER) + force *= WEP_CVAR_BOTH(blaster, !(deathtype & HITTYPE_SECONDARY), force_zscale); - // test line of sight to multiple positions on box, - // and do damage if any of them hit - hits = 0; + if(targ != directhitentity) + { + float hits; + float total; + float hitratio; + float mininv_f, mininv_d; - // we know: max stddev of hitratio = 1 / (2 * sqrt(n)) - // so for a given max stddev: - // n = (1 / (2 * max stddev of hitratio))^2 + // test line of sight to multiple positions on box, + // and do damage if any of them hit + hits = 0; - mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev; - mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev; + // we know: max stddev of hitratio = 1 / (2 * sqrt(n)) + // so for a given max stddev: + // n = (1 / (2 * max stddev of hitratio))^2 - if(autocvar_g_throughfloor_debug) - printf("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f); + mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev; + mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev; - total = 0.25 * pow(max(mininv_f, mininv_d), 2); + if(autocvar_g_throughfloor_debug) + printf("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f); - if(autocvar_g_throughfloor_debug) - printf(" steps=%f", total); - if (IS_PLAYER(targ)) - total = ceil(bound(autocvar_g_throughfloor_min_steps_player, total, autocvar_g_throughfloor_max_steps_player)); - else - total = ceil(bound(autocvar_g_throughfloor_min_steps_other, total, autocvar_g_throughfloor_max_steps_other)); + total = 0.25 * pow(max(mininv_f, mininv_d), 2); - if(autocvar_g_throughfloor_debug) - printf(" steps=%f dD=%f dF=%f", total, finaldmg * (1-tfloordmg) / (2 * sqrt(total)), vlen(force) * (1-tfloorforce) / (2 * sqrt(total))); + if(autocvar_g_throughfloor_debug) + printf(" steps=%f", total); - for(c = 0; c < total; ++c) - { - //traceline(targ.WarpZone_findradius_findorigin, nearest, MOVE_NOMONSTERS, inflictor); - WarpZone_TraceLine(blastorigin, WarpZone_UnTransformOrigin(targ, nearest), MOVE_NOMONSTERS, inflictor); - if (trace_fraction == 1 || trace_ent == targ) - { - ++hits; - if (hits > 1) - hitloc = hitloc + nearest; - else - hitloc = nearest; - } - nearest_x = targ.origin_x + targ.mins_x + random() * targ.size_x; - nearest_y = targ.origin_y + targ.mins_y + random() * targ.size_y; - nearest_z = targ.origin_z + targ.mins_z + random() * targ.size_z; - } - nearest = hitloc * (1 / max(1, hits)); - hitratio = (hits / total); - a = bound(0, tfloordmg + (1-tfloordmg) * hitratio, 1); - finaldmg = finaldmg * a; - a = bound(0, tfloorforce + (1-tfloorforce) * hitratio, 1); - force = force * a; + if (IS_PLAYER(targ)) + total = ceil(bound(autocvar_g_throughfloor_min_steps_player, total, autocvar_g_throughfloor_max_steps_player)); + else + total = ceil(bound(autocvar_g_throughfloor_min_steps_other, total, autocvar_g_throughfloor_max_steps_other)); - if(autocvar_g_throughfloor_debug) - printf(" D=%f F=%f\n", finaldmg, vlen(force)); - } + if(autocvar_g_throughfloor_debug) + printf(" steps=%f dD=%f dF=%f", total, finaldmg * (1-tfloordmg) / (2 * sqrt(total)), vlen(force) * (1-tfloorforce) / (2 * sqrt(total))); - // laser force adjustments :P - if(DEATH_WEAPONOF(deathtype) == WEP_LASER) + for(c = 0; c < total; ++c) { - if (targ == attacker) + //traceline(targ.WarpZone_findradius_findorigin, nearest, MOVE_NOMONSTERS, inflictor); + WarpZone_TraceLine(inflictororigin, WarpZone_UnTransformOrigin(targ, nearest), MOVE_NOMONSTERS, inflictor); + if (trace_fraction == 1 || trace_ent == targ) { - vector vel; - - float force_zscale; - float force_velocitybiasramp; - float force_velocitybias; - - force_velocitybiasramp = autocvar_sv_maxspeed; - if(deathtype & HITTYPE_SECONDARY) - { - force_zscale = autocvar_g_balance_laser_secondary_force_zscale; - force_velocitybias = autocvar_g_balance_laser_secondary_force_velocitybias; - } + ++hits; + if (hits > 1) + hitloc = hitloc + nearest; else - { - force_zscale = autocvar_g_balance_laser_primary_force_zscale; - force_velocitybias = autocvar_g_balance_laser_primary_force_velocitybias; - } - - vel = targ.velocity; - vel_z = 0; - vel = normalize(vel) * bound(0, vlen(vel) / force_velocitybiasramp, 1) * force_velocitybias; - force = - vlen(force) - * - normalize(normalize(force) + vel); - - force_z *= force_zscale; - } - else - { - if(deathtype & HITTYPE_SECONDARY) - { - force *= autocvar_g_balance_laser_secondary_force_other_scale; - } - else - { - force *= autocvar_g_balance_laser_primary_force_other_scale; - } + hitloc = nearest; } + nearest_x = targ.origin_x + targ.mins_x + random() * targ.size_x; + nearest_y = targ.origin_y + targ.mins_y + random() * targ.size_y; + nearest_z = targ.origin_z + targ.mins_z + random() * targ.size_z; } - //if (targ == attacker) - //{ - // print("hits ", ftos(hits), " / ", ftos(total)); - // print(" finaldmg ", ftos(finaldmg), " force ", vtos(force)); - // print(" (", ftos(a), ")\n"); - //} - if(finaldmg || vlen(force)) - { - if(targ.iscreature) - { - total_damage_to_creatures += finaldmg; + nearest = hitloc * (1 / max(1, hits)); + hitratio = (hits / total); + a = bound(0, tfloordmg + (1-tfloordmg) * hitratio, 1); + finaldmg = finaldmg * a; + a = bound(0, tfloorforce + (1-tfloorforce) * hitratio, 1); + force = force * a; - if(accuracy_isgooddamage(attacker, targ)) - stat_damagedone += finaldmg; - } + if(autocvar_g_throughfloor_debug) + printf(" D=%f F=%f\n", finaldmg, vlen(force)); + } - if(targ == directhitentity || DEATH_ISSPECIAL(deathtype)) - Damage (targ, inflictor, attacker, finaldmg, deathtype, nearest, force); - else - Damage (targ, inflictor, attacker, finaldmg, deathtype | HITTYPE_SPLASH, nearest, force); + //if (targ == attacker) + //{ + // print("hits ", ftos(hits), " / ", ftos(total)); + // print(" finaldmg ", ftos(finaldmg), " force ", vtos(force)); + // print(" (", ftos(a), ")\n"); + //} + if(finaldmg || vlen(force)) + { + if(targ.iscreature) + { + total_damage_to_creatures += finaldmg; + + if(accuracy_isgooddamage(attacker, targ)) + stat_damagedone += finaldmg; } + + if(targ == directhitentity || DEATH_ISSPECIAL(deathtype)) + Damage (targ, inflictor, attacker, finaldmg, deathtype, nearest, force); + else + Damage (targ, inflictor, attacker, finaldmg, deathtype | HITTYPE_SPLASH, nearest, force); } } } + } targ = next; } @@ -1179,6 +1137,11 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e return total_damage_to_creatures; } +float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, float deathtype, entity directhitentity) +{ + return RadiusDamageForSource (inflictor, (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5), inflictor.velocity, attacker, coredamage, edgedamage, rad, cantbe, mustbe, FALSE, forceintensity, deathtype, directhitentity); +} + .float fire_damagepersec; .float fire_endtime; .float fire_deathtype; diff --git a/qcsrc/server/g_hook.qc b/qcsrc/server/g_hook.qc index 4e1b634008..90b91ad3d0 100644 --- a/qcsrc/server/g_hook.qc +++ b/qcsrc/server/g_hook.qc @@ -335,7 +335,7 @@ void FireGrapplingHook (void) missile.state = 0; // not latched onto anything - W_SetupProjectileVelocityEx(missile, v_forward, v_up, autocvar_g_balance_grapplehook_speed_fly, 0, 0, 0, FALSE); + W_SetupProjVelocity_Explicit(missile, v_forward, v_up, autocvar_g_balance_grapplehook_speed_fly, 0, 0, 0, FALSE); missile.angles = vectoangles (missile.velocity); //missile.glow_color = 250; // 244, 250 @@ -476,7 +476,7 @@ void GrappleHookInit() } else { - weapon_action(WEP_HOOK, WR_PRECACHE); + WEP_ACTION(WEP_HOOK, WR_INIT); hook_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK), FALSE, FALSE, 1); hook_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK), FALSE, FALSE, 2); hook_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK), FALSE, FALSE, 3); diff --git a/qcsrc/server/g_triggers.qc b/qcsrc/server/g_triggers.qc index c0e81fa891..3a7bb364c5 100644 --- a/qcsrc/server/g_triggers.qc +++ b/qcsrc/server/g_triggers.qc @@ -1097,9 +1097,6 @@ void spawnfunc_func_snow() Net_LinkEntity(self, FALSE, 0, rainsnow_SendEntity); } - -void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, float deathtype); - .float modelscale; void misc_laser_aim() { diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index 7eda0103c4..019d0a2169 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -653,8 +653,6 @@ void spawnfunc_worldspawn (void) InitGameplayMode(); readlevelcvars(); GrappleHookInit(); - ElectroInit(); - LaserInit(); player_count = 0; bot_waypoints_for_items = autocvar_g_waypoints_for_items; @@ -773,7 +771,7 @@ void spawnfunc_worldspawn (void) addstat(STAT_SWITCHINGWEAPON, AS_INT, switchingweapon); addstat(STAT_GAMESTARTTIME, AS_FLOAT, stat_game_starttime); addstat(STAT_ROUNDSTARTTIME, AS_FLOAT, stat_round_starttime); - addstat(STAT_ALLOW_OLDNEXBEAM, AS_INT, stat_allow_oldnexbeam); + addstat(STAT_ALLOW_OLDVORTEXBEAM, AS_INT, stat_allow_oldvortexbeam); Nagger_Init(); addstat(STAT_STRENGTH_FINISHED, AS_FLOAT, strength_finished); @@ -781,6 +779,7 @@ void spawnfunc_worldspawn (void) addstat(STAT_SUPERWEAPONS_FINISHED, AS_FLOAT, superweapons_finished); addstat(STAT_PRESSED_KEYS, AS_FLOAT, pressedkeys); addstat(STAT_FUEL, AS_INT, ammo_fuel); + addstat(STAT_PLASMA, AS_INT, ammo_plasma); addstat(STAT_SHOTORG, AS_INT, stat_shotorg); addstat(STAT_LEADLIMIT, AS_FLOAT, stat_leadlimit); addstat(STAT_WEAPON_CLIPLOAD, AS_INT, clip_load); @@ -790,10 +789,12 @@ void spawnfunc_worldspawn (void) addstat(STAT_TYPEHIT_TIME, AS_FLOAT, typehit_time); addstat(STAT_LAYED_MINES, AS_INT, minelayer_mines); - addstat(STAT_NEX_CHARGE, AS_FLOAT, nex_charge); - addstat(STAT_NEX_CHARGEPOOL, AS_FLOAT, nex_chargepool_ammo); + addstat(STAT_VORTEX_CHARGE, AS_FLOAT, vortex_charge); + addstat(STAT_VORTEX_CHARGEPOOL, AS_FLOAT, vortex_chargepool_ammo); addstat(STAT_HAGAR_LOAD, AS_INT, hagar_load); + + addstat(STAT_ARC_HEAT, AS_FLOAT, arc_heat_percent); // freeze attacks addstat(STAT_FROZEN, AS_INT, frozen); diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index fc9e84e586..33010d1741 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -355,12 +355,13 @@ string formatmessage(string msg) wep = self.switchweapon; if (!wep) wep = self.cnt; - replacement = W_Name(wep); + replacement = WEP_NAME(wep); } else if (escape == "W") { if (self.items & IT_SHELLS) replacement = "shells"; else if (self.items & IT_NAILS) replacement = "bullets"; else if (self.items & IT_ROCKETS) replacement = "rockets"; else if (self.items & IT_CELLS) replacement = "cells"; + else if (self.items & IT_PLASMA) replacement = "plasma"; else replacement = "batteries"; // ;) } else if (escape == "x") { replacement = cursor_ent.netname; @@ -460,7 +461,6 @@ void GetCvars_handleFloatOnce(string thisname, float f, .float field, string nam stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n")); } } -float w_getbestweapon(entity e); string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo) { string o; @@ -563,6 +563,8 @@ float g_pickup_rockets; float g_pickup_rockets_max; float g_pickup_cells; float g_pickup_cells_max; +float g_pickup_plasma; +float g_pickup_plasma_max; float g_pickup_fuel; float g_pickup_fuel_jetpack; float g_pickup_fuel_max; @@ -595,7 +597,7 @@ float g_pickup_weapons_anyway; float g_weaponarena; WepSet g_weaponarena_weapons; float g_weaponarena_random; -float g_weaponarena_random_with_laser; +float g_weaponarena_random_with_blaster; string g_weaponarena_list; float g_weaponspeedfactor; float g_weaponratefactor; @@ -611,6 +613,7 @@ float start_ammo_shells; float start_ammo_nails; float start_ammo_rockets; float start_ammo_cells; +float start_ammo_plasma; float start_ammo_fuel; float start_health; float start_armorvalue; @@ -622,14 +625,13 @@ float warmup_start_ammo_shells; float warmup_start_ammo_nails; float warmup_start_ammo_rockets; float warmup_start_ammo_cells; +float warmup_start_ammo_plasma; float warmup_start_ammo_fuel; float warmup_start_health; float warmup_start_armorvalue; float g_weapon_stay; -entity get_weaponinfo(float w); - -float want_weapon(string cvarprefix, entity weaponinfo, float allguns) +float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done? { var float i = weaponinfo.weapon; var float d = 0; @@ -649,14 +651,14 @@ float want_weapon(string cvarprefix, entity weaponinfo, float allguns) else if (g_nexball) d = 0; // weapon is set a few lines later else - d = (i == WEP_LASER || i == WEP_SHOTGUN); + d = !(!weaponinfo.weaponstart); if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook d |= (i == WEP_HOOK); - if(weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED) // never default mutator blocked guns + if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns d = 0; - var float t = cvar(strcat(cvarprefix, weaponinfo.netname)); + var float t = weaponinfo.weaponstartoverride; //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n")); @@ -687,6 +689,7 @@ void readplayerstartcvars() start_ammo_nails = 0; start_ammo_rockets = 0; start_ammo_cells = 0; + start_ammo_plasma = 0; start_health = cvar("g_balance_health_start"); start_armorvalue = cvar("g_balance_armor_start"); @@ -766,7 +769,7 @@ void readplayerstartcvars() g_weaponarena_random = cvar("g_weaponarena_random"); else g_weaponarena_random = 0; - g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser"); + g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster"); if (g_weaponarena) { @@ -779,7 +782,7 @@ void readplayerstartcvars() for (i = WEP_FIRST; i <= WEP_LAST; ++i) { e = get_weaponinfo(i); - float w = want_weapon("g_start_weapon_", e, FALSE); + float w = want_weapon(e, FALSE); if(w & 1) start_weapons |= WepSet_FromWeapon(i); if(w & 2) @@ -797,6 +800,7 @@ void readplayerstartcvars() start_ammo_rockets = 999; start_ammo_shells = 999; start_ammo_cells = 999; + start_ammo_plasma = 999; start_ammo_nails = 999; start_ammo_fuel = 999; } @@ -806,6 +810,7 @@ void readplayerstartcvars() start_ammo_nails = cvar("g_start_ammo_nails"); start_ammo_rockets = cvar("g_start_ammo_rockets"); start_ammo_cells = cvar("g_start_ammo_cells"); + start_ammo_plasma = cvar("g_start_ammo_plasma"); start_ammo_fuel = cvar("g_start_ammo_fuel"); } @@ -837,7 +842,7 @@ void readplayerstartcvars() for (i = WEP_FIRST; i <= WEP_LAST; ++i) { e = get_weaponinfo(i); - float w = want_weapon("g_start_weapon_", e, g_warmup_allguns); + float w = want_weapon(e, g_warmup_allguns); if(w & 1) warmup_start_weapons |= WepSet_FromWeapon(i); if(w & 2) @@ -868,18 +873,20 @@ void readplayerstartcvars() { e = get_weaponinfo(i); if(precache_weapons & WepSet_FromWeapon(i)) - weapon_action(i, WR_PRECACHE); + WEP_ACTION(i, WR_INIT); } start_ammo_shells = max(0, start_ammo_shells); start_ammo_nails = max(0, start_ammo_nails); start_ammo_cells = max(0, start_ammo_cells); + start_ammo_plasma = max(0, start_ammo_plasma); start_ammo_rockets = max(0, start_ammo_rockets); start_ammo_fuel = max(0, start_ammo_fuel); warmup_start_ammo_shells = max(0, warmup_start_ammo_shells); warmup_start_ammo_nails = max(0, warmup_start_ammo_nails); warmup_start_ammo_cells = max(0, warmup_start_ammo_cells); + warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma); warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets); warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel); } @@ -979,6 +986,8 @@ void readlevelcvars(void) g_pickup_rockets_max = cvar("g_pickup_rockets_max"); g_pickup_cells = cvar("g_pickup_cells"); g_pickup_cells_max = cvar("g_pickup_cells_max"); + g_pickup_plasma = cvar("g_pickup_plasma"); + g_pickup_plasma_max = cvar("g_pickup_plasma_max"); g_pickup_fuel = cvar("g_pickup_fuel"); g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack"); g_pickup_fuel_max = cvar("g_pickup_fuel_max"); @@ -1357,18 +1366,6 @@ void precache() precache_sound ("weapons/hook_impact.wav"); // hook } - if(autocvar_sv_precacheweapons) - { - //precache weapon models/sounds - float wep; - wep = WEP_FIRST; - while (wep <= WEP_LAST) - { - weapon_action(wep, WR_PRECACHE); - wep = wep + 1; - } - } - precache_model("models/elaser.mdl"); precache_model("models/laser.mdl"); precache_model("models/ebomb.mdl"); @@ -1590,6 +1587,44 @@ void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendf } } + +entity eliminatedPlayers; +.float(entity) isEliminated; +float EliminatedPlayers_SendEntity(entity to, float sendflags) +{ + float i, f, b; + entity e; + WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS); + WriteByte(MSG_ENTITY, sendflags); + + if(sendflags & 1) + { + for(i = 1; i <= maxclients; i += 8) + { + for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e)) + { + if(eliminatedPlayers.isEliminated(e)) + f |= b; + } + WriteByte(MSG_ENTITY, f); + } + } + + return TRUE; +} + +void EliminatedPlayers_Init(float(entity) isEliminated_func) +{ + if(eliminatedPlayers) + { + backtrace("Can't spawn eliminatedPlayers again!"); + return; + } + Net_LinkEntity(eliminatedPlayers = spawn(), FALSE, 0, EliminatedPlayers_SendEntity); + eliminatedPlayers.isEliminated = isEliminated_func; +} + + void adaptor_think2touch() { entity o; diff --git a/qcsrc/server/mutators/base.qh b/qcsrc/server/mutators/base.qh index f4021eb4c0..95d85d7939 100644 --- a/qcsrc/server/mutators/base.qh +++ b/qcsrc/server/mutators/base.qh @@ -109,7 +109,7 @@ MUTATOR_HOOKABLE(WeaponRateFactor); float weapon_rate; MUTATOR_HOOKABLE(SetStartItems); - // adjusts {warmup_}start_{items,weapons,ammo_{cells,rockets,nails,shells,fuel}} + // adjusts {warmup_}start_{items,weapons,ammo_{cells,plasma,rockets,nails,shells,fuel}} MUTATOR_HOOKABLE(BuildMutatorsString); // appends ":mutatorname" to ret_string for logging diff --git a/qcsrc/server/mutators/gamemode_ca.qc b/qcsrc/server/mutators/gamemode_ca.qc index 8b38ceb096..4ba1830048 100644 --- a/qcsrc/server/mutators/gamemode_ca.qc +++ b/qcsrc/server/mutators/gamemode_ca.qc @@ -4,6 +4,14 @@ float redalive, bluealive, yellowalive, pinkalive; float ca_teams; float allowed_to_spawn; +#define ST_CA_ROUNDS 1 +void ca_ScoreRules(float teams) +{ + ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, TRUE); + ScoreInfo_SetLabel_TeamScore(ST_CA_ROUNDS, "rounds", SFL_SORT_PRIO_PRIMARY); + ScoreRules_basics_end(); +} + void CA_count_alive_players() { entity e; @@ -113,42 +121,57 @@ void CA_RoundStart() allowed_to_spawn = FALSE; } -float prev_total_players; +float prev_missing_teams_mask; float CA_CheckTeams() { allowed_to_spawn = TRUE; CA_count_alive_players(); if(CA_ALIVE_TEAMS_OK()) { - if(prev_total_players > 0) + if(prev_missing_teams_mask > 0) Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS); - prev_total_players = -1; + prev_missing_teams_mask = -1; return 1; } - if(prev_total_players != total_players) + if(total_players == 0) + { + if(prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + return 0; + } + float missing_teams_mask = (!redalive) + (!bluealive) * 2; + if(ca_teams >= 3) missing_teams_mask += (!yellowalive) * 4; + if(ca_teams >= 4) missing_teams_mask += (!pinkalive) * 8; + if(prev_missing_teams_mask != missing_teams_mask) { - float p1 = 0, p2 = 0, p3 = 0, p4 = 0; - if(!redalive) p1 = NUM_TEAM_1; - if(!bluealive) p2 = NUM_TEAM_2; - if(ca_teams >= 3) - if(!yellowalive) p3 = NUM_TEAM_3; - if(ca_teams >= 4) - if(!pinkalive) p4 = NUM_TEAM_4; - Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, p1, p2, p3, p4); - prev_total_players = total_players; + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask); + prev_missing_teams_mask = missing_teams_mask; } return 0; } +float ca_isEliminated(entity e) +{ + if(e.caplayer == 1 && (e.deadflag != DEAD_NO || e.frags == FRAGS_LMS_LOSER)) + return TRUE; + if(e.caplayer == 0.5) + return TRUE; + return FALSE; +} + MUTATOR_HOOKFUNCTION(ca_PlayerSpawn) { self.caplayer = 1; + if(!warmup_stage) + eliminatedPlayers.SendFlags |= 1; return 1; } MUTATOR_HOOKFUNCTION(ca_PutClientInServer) { if(!allowed_to_spawn) + if(IS_PLAYER(self)) // this is true even when player is trying to join { self.classname = "observer"; if(self.jointime != time) //not when connecting @@ -156,7 +179,7 @@ MUTATOR_HOOKFUNCTION(ca_PutClientInServer) { self.caplayer = 0.5; if(IS_REAL_CLIENT(self)) - sprint(self, "You will join the game in the next round.\n"); + Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_CA_JOIN_LATE); } } return 1; @@ -167,6 +190,11 @@ MUTATOR_HOOKFUNCTION(ca_reset_map_players) FOR_EACH_CLIENT(self) { self.killcount = 0; + if(!self.caplayer && IS_BOT_CLIENT(self)) + { + self.team = -1; + self.caplayer = 1; + } if(self.caplayer) { self.classname = "player"; @@ -195,10 +223,47 @@ MUTATOR_HOOKFUNCTION(ca_GetTeamCount) return 0; } +entity ca_LastPlayerForTeam() +{ + entity pl, last_pl = world; + FOR_EACH_PLAYER(pl) + { + if(pl.health >= 1) + if(pl != self) + if(pl.team == self.team) + if(!last_pl) + last_pl = pl; + else + return world; + } + return last_pl; +} + +void ca_LastPlayerForTeam_Notify() +{ + if(round_handler_IsActive()) + if(round_handler_IsRoundStarted()) + { + entity pl = ca_LastPlayerForTeam(); + if(pl) + Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_ALONE); + } +} + MUTATOR_HOOKFUNCTION(ca_PlayerDies) { + ca_LastPlayerForTeam_Notify(); if(!allowed_to_spawn) self.respawn_flags = RESPAWN_SILENT; + if(!warmup_stage) + eliminatedPlayers.SendFlags |= 1; + return 1; +} + +MUTATOR_HOOKFUNCTION(ca_ClientDisconnect) +{ + if(self.caplayer == 1) + ca_LastPlayerForTeam_Notify(); return 1; } @@ -209,10 +274,14 @@ MUTATOR_HOOKFUNCTION(ca_ForbidPlayerScore_Clear) MUTATOR_HOOKFUNCTION(ca_MakePlayerObserver) { + if(self.caplayer == 1) + ca_LastPlayerForTeam_Notify(); if(self.killindicator_teamchange == -2) self.caplayer = 0; if(self.caplayer) self.frags = FRAGS_LMS_LOSER; + if(!warmup_stage) + eliminatedPlayers.SendFlags |= 1; return 1; } @@ -236,6 +305,7 @@ MUTATOR_HOOKFUNCTION(ca_SetStartItems) start_ammo_nails = warmup_start_ammo_nails = cvar("g_lms_start_ammo_nails"); start_ammo_rockets = warmup_start_ammo_rockets = cvar("g_lms_start_ammo_rockets"); start_ammo_cells = warmup_start_ammo_cells = cvar("g_lms_start_ammo_cells"); + start_ammo_plasma = warmup_start_ammo_plasma = cvar("g_lms_start_ammo_plasma"); start_ammo_fuel = warmup_start_ammo_fuel = cvar("g_lms_start_ammo_fuel"); return 0; @@ -290,7 +360,7 @@ void ca_Initialize() ca_teams = autocvar_g_ca_teams; ca_teams = bound(2, ca_teams, 4); ret_float = ca_teams; - ScoreRules_ca(ca_teams); + ca_ScoreRules(ca_teams); round_handler_Spawn(CA_CheckTeams, CA_CheckWinner, CA_RoundStart); round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit); @@ -299,6 +369,8 @@ void ca_Initialize() addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat); addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat); addstat(STAT_PINKALIVE, AS_INT, pinkalive_stat); + + EliminatedPlayers_Init(ca_isEliminated); } MUTATOR_DEFINITION(gamemode_ca) @@ -311,6 +383,7 @@ MUTATOR_DEFINITION(gamemode_ca) MUTATOR_HOOK(reset_map_players, ca_reset_map_players, CBC_ORDER_ANY); MUTATOR_HOOK(GetTeamCount, ca_GetTeamCount, CBC_ORDER_EXCLUSIVE); MUTATOR_HOOK(PlayerDies, ca_PlayerDies, CBC_ORDER_ANY); + MUTATOR_HOOK(ClientDisconnect, ca_ClientDisconnect, CBC_ORDER_ANY); MUTATOR_HOOK(ForbidPlayerScore_Clear, ca_ForbidPlayerScore_Clear, CBC_ORDER_ANY); MUTATOR_HOOK(ForbidThrowCurrentWeapon, ca_ForbidThrowCurrentWeapon, CBC_ORDER_ANY); MUTATOR_HOOK(GiveFragsForKill, ca_GiveFragsForKill, CBC_ORDER_FIRST); diff --git a/qcsrc/server/mutators/gamemode_ctf.qc b/qcsrc/server/mutators/gamemode_ctf.qc index 967d8d656f..4e051d1972 100644 --- a/qcsrc/server/mutators/gamemode_ctf.qc +++ b/qcsrc/server/mutators/gamemode_ctf.qc @@ -176,7 +176,7 @@ void ctf_CaptureShield_Touch() vector othermid = (other.absmin + other.absmax) * 0.5; Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * ctf_captureshield_force); - Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_SHIELDED); + if(IS_REAL_CLIENT(other)) { Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_SHIELDED); } } void ctf_CaptureShield_Spawn(entity flag) @@ -257,8 +257,17 @@ void ctf_Handle_Retrieve(entity flag, entity player) flag.owner.flagcarried = flag; // reset flag - setattachment(flag, player, ""); - setorigin(flag, FLAG_CARRY_OFFSET); + if(player.vehicle) + { + setattachment(flag, player.vehicle, ""); + setorigin(flag, VEHICLE_FLAG_OFFSET); + flag.scale = VEHICLE_FLAG_SCALE; + } + else + { + setattachment(flag, player, ""); + setorigin(flag, FLAG_CARRY_OFFSET); + } flag.movetype = MOVETYPE_NONE; flag.takedamage = DAMAGE_NO; flag.solid = SOLID_NOT; @@ -475,8 +484,17 @@ void ctf_Handle_Pickup(entity flag, entity player, float pickuptype) // attach the flag to the player flag.owner = player; player.flagcarried = flag; - setattachment(flag, player, ""); - setorigin(flag, FLAG_CARRY_OFFSET); + if(player.vehicle) + { + setattachment(flag, player.vehicle, ""); + setorigin(flag, VEHICLE_FLAG_OFFSET); + flag.scale = VEHICLE_FLAG_SCALE; + } + else + { + setattachment(flag, player, ""); + setorigin(flag, FLAG_CARRY_OFFSET); + } // flag setup flag.movetype = MOVETYPE_NONE; @@ -757,6 +775,7 @@ void ctf_FlagThink() if((self.pass_target == world) || (self.pass_target.deadflag != DEAD_NO) + || (self.pass_target.flagcarried) || (vlen(self.origin - targ_origin) > autocvar_g_ctf_pass_radius) || ((trace_fraction < 1) && (trace_ent != self.pass_target)) || (time > self.ctf_droptime + autocvar_g_ctf_pass_timelimit)) @@ -783,6 +802,7 @@ void ctf_FlagThink() void ctf_FlagTouch() { if(gameover) { return; } + if(trace_dphitcontents & (DPCONTENTS_PLAYERCLIP | DPCONTENTS_MONSTERCLIP)) { return; } entity toucher = other; float is_not_monster = (!(toucher.flags & FL_MONSTER)); @@ -884,6 +904,9 @@ void ctf_RespawnFlag(entity flag) ctf_FakeTimeLimit(flag.owner, -1); } + if((flag.owner) && (flag.owner.vehicle)) + flag.scale = FLAG_SCALE; + if((flag.ctf_status == FLAG_DROPPED) && (flag.wps_flagdropped)) { WaypointSprite_Kill(flag.wps_flagdropped); } @@ -907,6 +930,8 @@ void ctf_RespawnFlag(entity flag) flag.ctf_dropper = world; flag.ctf_pickuptime = 0; flag.ctf_droptime = 0; + + ctf_CheckStalemate(); } void ctf_Reset() @@ -1264,7 +1289,7 @@ void havocbot_ctf_reset_role(entity bot) // if there is only me on the team switch to offense c = 0; FOR_EACH_PLAYER(head) - if(head.team==bot.team) + if(SAME_TEAM(head, bot)) ++c; if(c==1) @@ -1630,7 +1655,7 @@ void havocbot_role_ctf_defense() } if(closestplayer) - if(closestplayer.team!=self.team) + if(DIFF_TEAM(closestplayer, self)) if(vlen(org - self.origin)>1000) if(checkpvs(self.origin,closestplayer)||random()<0.5) havocbot_goalrating_ctf_ourbase(30000); diff --git a/qcsrc/server/mutators/gamemode_domination.qc b/qcsrc/server/mutators/gamemode_domination.qc index 2569a49d31..8e4d929bee 100644 --- a/qcsrc/server/mutators/gamemode_domination.qc +++ b/qcsrc/server/mutators/gamemode_domination.qc @@ -647,7 +647,8 @@ MUTATOR_DEFINITION(gamemode_domination) MUTATOR_ONREMOVE { - error("This is a game type and it cannot be removed at runtime."); + print("This is a game type and it cannot be removed at runtime."); + return -1; } return 0; diff --git a/qcsrc/server/mutators/gamemode_freezetag.qc b/qcsrc/server/mutators/gamemode_freezetag.qc index ffdc1612dd..f5c76f16f9 100644 --- a/qcsrc/server/mutators/gamemode_freezetag.qc +++ b/qcsrc/server/mutators/gamemode_freezetag.qc @@ -1,10 +1,17 @@ .float freezetag_frozen_time; .float freezetag_frozen_timeout; -.float freezetag_revive_progress; #define ICE_MAX_ALPHA 1 #define ICE_MIN_ALPHA 0.1 float freezetag_teams; +#define SP_FREEZETAG_REVIVALS 4 +void freezetag_ScoreRules(float teams) +{ + ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, TRUE); // SFL_SORT_PRIO_PRIMARY + ScoreInfo_SetLabel_PlayerScore(SP_FREEZETAG_REVIVALS, "revivals", 0); + ScoreRules_basics_end(); +} + void freezetag_count_alive_players() { entity e; @@ -26,31 +33,36 @@ void freezetag_count_alive_players() e.yellowalive_stat = yellowalive; e.pinkalive_stat = pinkalive; } + + eliminatedPlayers.SendFlags |= 1; } #define FREEZETAG_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0)) #define FREEZETAG_ALIVE_TEAMS_OK() (FREEZETAG_ALIVE_TEAMS() == freezetag_teams) -float prev_total_players; +float prev_missing_teams_mask; float freezetag_CheckTeams() { if(FREEZETAG_ALIVE_TEAMS_OK()) { - if(prev_total_players > 0) + if(prev_missing_teams_mask > 0) Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS); - prev_total_players = -1; + prev_missing_teams_mask = -1; return 1; } - if(prev_total_players != total_players) + if(total_players == 0) + { + if(prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + return 0; + } + float missing_teams_mask = (!redalive) + (!bluealive) * 2; + if(freezetag_teams >= 3) missing_teams_mask += (!yellowalive) * 4; + if(freezetag_teams >= 4) missing_teams_mask += (!pinkalive) * 8; + if(prev_missing_teams_mask != missing_teams_mask) { - float p1 = 0, p2 = 0, p3 = 0, p4 = 0; - if(!redalive) p1 = NUM_TEAM_1; - if(!bluealive) p2 = NUM_TEAM_2; - if(freezetag_teams >= 3) - if(!yellowalive) p3 = NUM_TEAM_3; - if(freezetag_teams >= 4) - if(!pinkalive) p4 = NUM_TEAM_4; - Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, p1, p2, p3, p4); - prev_total_players = total_players; + Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask); + prev_missing_teams_mask = missing_teams_mask; } return 0; } @@ -122,6 +134,34 @@ float freezetag_CheckWinner() return 1; } +entity freezetag_LastPlayerForTeam() +{ + entity pl, last_pl = world; + FOR_EACH_PLAYER(pl) + { + if(pl.health >= 1) + if(!pl.frozen) + if(pl != self) + if(pl.team == self.team) + if(!last_pl) + last_pl = pl; + else + return world; + } + return last_pl; +} + +void freezetag_LastPlayerForTeam_Notify() +{ + if(round_handler_IsActive()) + if(round_handler_IsRoundStarted()) + { + entity pl = freezetag_LastPlayerForTeam(); + if(pl) + Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_ALONE); + } +} + void freezetag_Add_Score(entity attacker) { if(attacker == self) @@ -163,6 +203,13 @@ void freezetag_Unfreeze(entity attacker) Unfreeze(self); } +float freezetag_isEliminated(entity e) +{ + if(e.frozen == 1 || e.deadflag != DEAD_NO) + return TRUE; + return FALSE; +} + // ================ // Bot player logic @@ -275,6 +322,8 @@ void havocbot_role_ft_freeing() MUTATOR_HOOKFUNCTION(freezetag_RemovePlayer) { self.health = 0; // neccessary to update correctly alive stats + if(!self.frozen) + freezetag_LastPlayerForTeam_Notify(); freezetag_Unfreeze(world); freezetag_count_alive_players(); return 1; @@ -301,6 +350,7 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDies) { freezetag_Add_Score(frag_attacker); freezetag_count_alive_players(); + freezetag_LastPlayerForTeam_Notify(); } else freezetag_Unfreeze(world); // remove ice @@ -313,6 +363,7 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDies) return 1; freezetag_Freeze(frag_attacker); + freezetag_LastPlayerForTeam_Notify(); if(frag_attacker == frag_target || frag_attacker == world) { @@ -500,7 +551,7 @@ void freezetag_Initialize() if(freezetag_teams < 2) freezetag_teams = autocvar_g_freezetag_teams; freezetag_teams = bound(2, freezetag_teams, 4); - ScoreRules_freezetag(freezetag_teams); + freezetag_ScoreRules(freezetag_teams); round_handler_Spawn(freezetag_CheckTeams, freezetag_CheckWinner, func_null); round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit); @@ -509,6 +560,8 @@ void freezetag_Initialize() addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat); addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat); addstat(STAT_PINKALIVE, AS_INT, pinkalive_stat); + + EliminatedPlayers_Init(freezetag_isEliminated); } MUTATOR_DEFINITION(gamemode_freezetag) diff --git a/qcsrc/server/mutators/gamemode_invasion.qc b/qcsrc/server/mutators/gamemode_invasion.qc index 8448a215e9..364cb7fe3b 100644 --- a/qcsrc/server/mutators/gamemode_invasion.qc +++ b/qcsrc/server/mutators/gamemode_invasion.qc @@ -416,8 +416,6 @@ void invasion_DelayedInit() // Do this check with a delay so we can wait for tea round_handler_Spawn(Invasion_CheckPlayers, Invasion_CheckWinner, Invasion_RoundStart); round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit); - allowed_to_spawn = TRUE; - inv_roundcnt = 0; inv_maxrounds = 15; // 15? } diff --git a/qcsrc/server/mutators/gamemode_keyhunt.qc b/qcsrc/server/mutators/gamemode_keyhunt.qc index ab67a48212..0512fe3f0d 100644 --- a/qcsrc/server/mutators/gamemode_keyhunt.qc +++ b/qcsrc/server/mutators/gamemode_keyhunt.qc @@ -67,6 +67,26 @@ string kh_sound_alarm = "kh/alarm.wav"; // the new siren/alarm float kh_key_dropped, kh_key_carried; +#define ST_KH_CAPS 1 +#define SP_KH_CAPS 4 +#define SP_KH_PUSHES 5 +#define SP_KH_DESTROYS 6 +#define SP_KH_PICKUPS 7 +#define SP_KH_KCKILLS 8 +#define SP_KH_LOSSES 9 +void kh_ScoreRules(float teams) +{ + ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, TRUE); + ScoreInfo_SetLabel_TeamScore( ST_KH_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); + ScoreInfo_SetLabel_PlayerScore(SP_KH_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); + ScoreInfo_SetLabel_PlayerScore(SP_KH_PUSHES, "pushes", 0); + ScoreInfo_SetLabel_PlayerScore(SP_KH_DESTROYS, "destroyed", SFL_LOWER_IS_BETTER); + ScoreInfo_SetLabel_PlayerScore(SP_KH_PICKUPS, "pickups", 0); + ScoreInfo_SetLabel_PlayerScore(SP_KH_KCKILLS, "kckills", 0); + ScoreInfo_SetLabel_PlayerScore(SP_KH_LOSSES, "losses", SFL_LOWER_IS_BETTER); + ScoreRules_basics_end(); +} + float kh_KeyCarrier_waypointsprite_visible_for_player(entity e) // runs all the time { if(!IS_PLAYER(e) || self.team != e.team) @@ -990,7 +1010,7 @@ void kh_Initialize() // sets up th KH environment addstat(STAT_KH_KEYS, AS_INT, kh_state); - ScoreRules_kh(kh_teams); + kh_ScoreRules(kh_teams); } void kh_finalize() diff --git a/qcsrc/server/mutators/gamemode_lms.qc b/qcsrc/server/mutators/gamemode_lms.qc index fb0bb2e865..0684ac8edb 100644 --- a/qcsrc/server/mutators/gamemode_lms.qc +++ b/qcsrc/server/mutators/gamemode_lms.qc @@ -135,6 +135,7 @@ MUTATOR_HOOKFUNCTION(lms_SetStartItems) start_ammo_nails = warmup_start_ammo_nails = cvar("g_lms_start_ammo_nails"); start_ammo_rockets = warmup_start_ammo_rockets = cvar("g_lms_start_ammo_rockets"); start_ammo_cells = warmup_start_ammo_cells = cvar("g_lms_start_ammo_cells"); + start_ammo_plasma = warmup_start_ammo_plasma = cvar("g_lms_start_ammo_plasma"); start_ammo_fuel = warmup_start_ammo_fuel = cvar("g_lms_start_ammo_fuel"); return FALSE; diff --git a/qcsrc/server/mutators/gamemode_nexball.qc b/qcsrc/server/mutators/gamemode_nexball.qc index 8695ca31b2..dd8daccc25 100644 --- a/qcsrc/server/mutators/gamemode_nexball.qc +++ b/qcsrc/server/mutators/gamemode_nexball.qc @@ -22,6 +22,17 @@ float OtherTeam(float t) //works only if there are two teams on the map! return e.team; } +#define ST_NEXBALL_GOALS 1 +#define SP_NEXBALL_GOALS 4 +#define SP_NEXBALL_FAULTS 5 +void nb_ScoreRules(float teams) +{ + ScoreRules_basics(teams, 0, 0, TRUE); + ScoreInfo_SetLabel_TeamScore( ST_NEXBALL_GOALS, "goals", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore( SP_NEXBALL_GOALS, "goals", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore(SP_NEXBALL_FAULTS, "faults", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER); + ScoreRules_basics_end(); +} void LogNB(string mode, entity actor) { @@ -142,7 +153,7 @@ void GiveBall(entity plyr, entity ball) self.weaponentity.weapons = self.weapons; self.weaponentity.switchweapon = self.weapon; self.weapons = WEPSET_PORTO; - weapon_action(WEP_PORTO, WR_RESETPLAYER); + WEP_ACTION(WEP_PORTO, WR_RESETPLAYER); self.switchweapon = WEP_PORTO; W_SwitchWeapon(WEP_PORTO); self = ownr; @@ -457,16 +468,6 @@ void nb_spawnteams(void) } } -// scoreboard setup -void nb_ScoreRules(float teams) -{ - ScoreRules_basics(teams, 0, 0, TRUE); - ScoreInfo_SetLabel_TeamScore( ST_NEXBALL_GOALS, "goals", SFL_SORT_PRIO_PRIMARY); - ScoreInfo_SetLabel_PlayerScore( SP_NEXBALL_GOALS, "goals", SFL_SORT_PRIO_PRIMARY); - ScoreInfo_SetLabel_PlayerScore(SP_NEXBALL_FAULTS, "faults", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER); - ScoreRules_basics_end(); -} - void nb_delayedinit(void) { if(find(world, classname, "nexball_team") == world) @@ -784,7 +785,7 @@ void W_Nexball_Attack2(void) setsize(missile, '0 0 0', '0 0 0'); setorigin(missile, w_shotorg); - W_SetupProjectileVelocity(missile, autocvar_g_balance_nexball_secondary_speed, 0); + W_SetupProjVelocity_Basic(missile, autocvar_g_balance_nexball_secondary_speed, 0); missile.angles = vectoangles(missile.velocity); missile.touch = W_Nexball_Touch; missile.think = SUB_Remove; @@ -855,7 +856,7 @@ float w_nexball_weapon(float req) weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready); } } - else if(req == WR_PRECACHE) + else if(req == WR_INIT) { precache_model("models/weapons/g_porto.md3"); precache_model("models/weapons/v_porto.md3"); @@ -867,7 +868,7 @@ float w_nexball_weapon(float req) } else if(req == WR_SETUP) { - weapon_setup(WEP_PORTO); + //weapon_setup(WEP_PORTO); } // No need to check WR_CHECKAMMO* or WR_AIM, it should always return TRUE return TRUE; @@ -930,7 +931,7 @@ MUTATOR_HOOKFUNCTION(nexball_PlayerPreThink) if(self.weaponentity.weapons) { self.weapons = self.weaponentity.weapons; - weapon_action(WEP_PORTO, WR_RESETPLAYER); + WEP_ACTION(WEP_PORTO, WR_RESETPLAYER); self.switchweapon = self.weaponentity.switchweapon; W_SwitchWeapon(self.switchweapon); @@ -976,7 +977,7 @@ MUTATOR_HOOKFUNCTION(nexball_SetStartItems) MUTATOR_HOOKFUNCTION(nexball_ForbidThrowing) { - if(self.weapon == WEP_GRENADE_LAUNCHER) + if(self.weapon == WEP_MORTAR) return TRUE; return FALSE; @@ -985,7 +986,7 @@ MUTATOR_HOOKFUNCTION(nexball_ForbidThrowing) MUTATOR_HOOKFUNCTION(nexball_FilterItem) { if(self.classname == "droppedweapon") - if(self.weapon == WEP_GRENADE_LAUNCHER) + if(self.weapon == WEP_MORTAR) return TRUE; return FALSE; @@ -1011,7 +1012,7 @@ MUTATOR_DEFINITION(gamemode_nexball) g_nexball_meter_period = rint(g_nexball_meter_period * 32) / 32; //Round to 1/32ths to send as a byte multiplied by 32 addstat(STAT_NB_METERSTART, AS_FLOAT, metertime); - w_porto(WR_PRECACHE); // abuse + W_Porto(WR_INIT); // abuse // General settings /* diff --git a/qcsrc/server/mutators/mutator_instagib.qc b/qcsrc/server/mutators/mutator_instagib.qc index c2ea791ca2..4a2a3e9f98 100644 --- a/qcsrc/server/mutators/mutator_instagib.qc +++ b/qcsrc/server/mutators/mutator_instagib.qc @@ -6,7 +6,7 @@ void spawnfunc_item_minst_cells (void) StartItem ("models/items/a_cells.md3", "misc/itempickup.wav", 45, 0, - "MinstaNex Ammo", IT_CELLS, 0, 0, generic_pickupevalfunc, 100); + "Vaporizer Ammo", IT_CELLS, 0, 0, generic_pickupevalfunc, 100); } void instagib_health_mega() @@ -40,58 +40,58 @@ void instagib_ammocheck() else { self.instagib_needammo = TRUE; - if (self.health == 5) + if (self.health <= 5) { Damage(self, self, self, 5, DEATH_NOAMMO, self.origin, '0 0 0'); Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_INSTAGIB_TERMINATED); } - else if (self.health == 10) + else if (self.health <= 10) { Damage(self, self, self, 5, DEATH_NOAMMO, self.origin, '0 0 0'); Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_1); } - else if (self.health == 20) + else if (self.health <= 20) { Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_2); } - else if (self.health == 30) + else if (self.health <= 30) { Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_3); } - else if (self.health == 40) + else if (self.health <= 40) { Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_4); } - else if (self.health == 50) + else if (self.health <= 50) { Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_5); } - else if (self.health == 60) + else if (self.health <= 60) { Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_6); } - else if (self.health == 70) + else if (self.health <= 70) { Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_7); } - else if (self.health == 80) + else if (self.health <= 80) { Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_8); } - else if (self.health == 90) + else if (self.health <= 90) { Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_INSTAGIB_FINDAMMO); Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_NUM_9); } - else if (self.health == 100) + else { Send_Notification(NOTIF_ONE_ONLY, self, MSG_MULTI, MULTI_INSTAGIB_FINDAMMO); Damage(self, self, self, 10, DEATH_NOAMMO, self.origin, '0 0 0'); @@ -245,28 +245,25 @@ MUTATOR_HOOKFUNCTION(instagib_PlayerDamage) } if(IS_PLAYER(frag_attacker)) - if(DEATH_ISWEAPON(frag_deathtype, WEP_MINSTANEX)) - if(frag_target.armorvalue) + if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER)) { - frag_target.armorvalue -= 1; - Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_target.armorvalue); - frag_damage = 0; - frag_target.damage_dealt += 1; - frag_attacker.damage_dealt += 1; // TODO change this to a future specific hitsound for armor hit - } - - if(IS_PLAYER(frag_attacker)) - if (DEATH_ISWEAPON(frag_deathtype, WEP_LASER)) - { - frag_damage = 0; - frag_mirrordamage = 0; - if (frag_target != frag_attacker) + if(frag_deathtype & HITTYPE_SECONDARY) + { + frag_damage = frag_mirrordamage = 0; + + if(frag_target != frag_attacker) + { + if(frag_target.health > 0) { Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_INSTAGIB_SECONDARY); } + frag_force = '0 0 0'; + } + } + else if(frag_target.armorvalue) { - if (frag_target.health >= 1) - Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_INSTAGIB_SECONDARY); - frag_force = '0 0 0'; - // keep mirrorfrag_force - //frag_attacker = frag_target; + frag_target.armorvalue -= 1; + frag_damage = 0; + frag_target.damage_dealt += 1; + frag_attacker.damage_dealt += 1; // TODO: change this to a specific hitsound for armor hit + Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_target.armorvalue); } } } @@ -296,8 +293,8 @@ MUTATOR_HOOKFUNCTION(instagib_SetStartItems) start_health = 100; start_armorvalue = 0; - start_weapons = WEPSET_MINSTANEX; - warmup_start_weapons = WEPSET_MINSTANEX; + start_weapons = WEPSET_VAPORIZER; + warmup_start_weapons = WEPSET_VAPORIZER; start_items |= IT_UNLIMITED_SUPERWEAPONS; return FALSE; @@ -308,13 +305,13 @@ MUTATOR_HOOKFUNCTION(instagib_FilterItem) if(self.classname == "item_cells") return TRUE; // no normal cells? - if(self.weapon == WEP_MINSTANEX && self.classname == "droppedweapon") + if(self.weapon == WEP_VAPORIZER && self.classname == "droppedweapon") { self.ammo_cells = autocvar_g_instagib_ammo_drop; return FALSE; } - if(self.weapon == WEP_ROCKET_LAUNCHER || self.weapon == WEP_NEX) + if(self.weapon == WEP_DEVASTATOR || self.weapon == WEP_VORTEX) { entity e = spawn(); setorigin(e, self.origin); diff --git a/qcsrc/server/mutators/mutator_nades.qc b/qcsrc/server/mutators/mutator_nades.qc index da75e25ac2..70c4578bd0 100644 --- a/qcsrc/server/mutators/mutator_nades.qc +++ b/qcsrc/server/mutators/mutator_nades.qc @@ -250,7 +250,7 @@ void nade_ice_think() sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, - autocvar_g_nades_nade_radius, self, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy); + autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy); Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self); } @@ -525,12 +525,10 @@ void nade_boom() sound(self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM); sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); - self.takedamage = DAMAGE_NO; - if(nade_blast) { RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, - autocvar_g_nades_nade_radius, self, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy); + autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy); Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self); } @@ -575,20 +573,19 @@ void nade_damage(entity inflictor, entity attacker, float damage, float deathtyp if(self.nade_type == NADE_TYPE_TRANSLOCATE || self.nade_type == NADE_TYPE_SPAWN) return; - if(DEATH_ISWEAPON(deathtype, WEP_LASER)) + if(DEATH_ISWEAPON(deathtype, WEP_BLASTER)) return; - if(DEATH_ISWEAPON(deathtype, WEP_NEX) || DEATH_ISWEAPON(deathtype, WEP_MINSTANEX)) + if(DEATH_ISWEAPON(deathtype, WEP_VORTEX) || DEATH_ISWEAPON(deathtype, WEP_VAPORIZER)) { force *= 6; damage = self.max_health * 0.55; } - if(DEATH_ISWEAPON(deathtype, WEP_UZI)) + if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN)) damage = self.max_health * 0.1; - if(DEATH_ISWEAPON(deathtype, WEP_SHOTGUN)) - if(deathtype & HITTYPE_SECONDARY) + if((DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE) || DEATH_ISWEAPON(deathtype, WEP_SHOTGUN)) && (deathtype & HITTYPE_SECONDARY)) // WEAPONTODO { damage = self.max_health * 0.1; force *= 10; diff --git a/qcsrc/server/mutators/mutator_new_toys.qc b/qcsrc/server/mutators/mutator_new_toys.qc index f11298451c..3e41c42fe0 100644 --- a/qcsrc/server/mutators/mutator_new_toys.qc +++ b/qcsrc/server/mutators/mutator_new_toys.qc @@ -1,7 +1,7 @@ /* -CORE laser nex lg rl cry gl elec hagar fireb hook - minsta porto +CORE laser vortex lg rl cry gl elec hagar fireb hook + vaporizer porto tuba NEW rifle hlac minel seeker @@ -18,28 +18,28 @@ weaponreplace lists. Entity: { -"classname" "weapon_nex" +"classname" "weapon_vortex" "new_toys" "rifle" } --> This will spawn as Rifle in this mutator ONLY, and as Nex otherwise. +-> This will spawn as Rifle in this mutator ONLY, and as Vortex otherwise. { -"classname" "weapon_nex" -"new_toys" "nex rifle" +"classname" "weapon_vortext" +"new_toys" "vortex rifle" } --> This will spawn as either Nex or Rifle in this mutator ONLY, and as Nex otherwise. +-> This will spawn as either Vortex or Rifle in this mutator ONLY, and as Vortex otherwise. { -"classname" "weapon_nex" -"new_toys" "nex" +"classname" "weapon_vortex" +"new_toys" "vortex" } --> This is always a Nex. +-> This is always a Vortex. If the map specifies no "new_toys" argument There will be two default replacements selectable: "replace all" and "replace random". -In "replace all" mode, e.g. Nex will have the default replacement "rifle". -In "replace random" mode, Nex will have the default replacement "nex rifle". +In "replace all" mode, e.g. Vortex will have the default replacement "rifle". +In "replace random" mode, Vortex will have the default replacement "vortex rifle". This mutator's replacements run BEFORE regular weaponreplace! @@ -87,6 +87,7 @@ float nt_IsNewToy(float w) case WEP_MINE_LAYER: case WEP_HLAC: case WEP_RIFLE: + case WEP_SHOCKWAVE: return TRUE; default: return FALSE; @@ -98,9 +99,10 @@ string nt_GetFullReplacement(string w) switch(w) { case "hagar": return "seeker"; - case "rocketlauncher": return "minelayer"; - case "uzi": return "hlac"; - case "nex": return "rifle"; + case "devastator": return "minelayer"; + case "machinegun": return "hlac"; + case "vortex": return "rifle"; + case "shotgun": return "shockwave"; default: return string_null; } } diff --git a/qcsrc/server/mutators/mutator_nix.qc b/qcsrc/server/mutators/mutator_nix.qc index dc12b05209..26209daa11 100644 --- a/qcsrc/server/mutators/mutator_nix.qc +++ b/qcsrc/server/mutators/mutator_nix.qc @@ -1,10 +1,8 @@ float g_nix_with_laser; - +// WEAPONTODO float nix_weapon; -float nix_weapon_ammo; float nix_nextchange; float nix_nextweapon; -float nix_nextweapon_ammo; .float nix_lastchange_id; .float nix_lastinfotime; .float nix_nextincr; @@ -22,7 +20,7 @@ float NIX_CanChooseWeapon(float wpn) } else { - if(wpn == WEP_LASER && g_nix_with_laser) + if(wpn == WEP_BLASTER && g_nix_with_laser) // WEAPONTODO: rename to g_nix_with_blaster return FALSE; if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) return FALSE; @@ -39,7 +37,6 @@ void NIX_ChooseNextWeapon() if(NIX_CanChooseWeapon(j)) RandomSelection_Add(world, j, string_null, 1, (j != nix_weapon)); nix_nextweapon = RandomSelection_chosen_float; - nix_nextweapon_ammo = W_AmmoItemCode(nix_nextweapon); } void NIX_GiveCurrentWeapon() @@ -54,65 +51,68 @@ void NIX_GiveCurrentWeapon() if(dt <= 0) { nix_weapon = nix_nextweapon; - nix_weapon_ammo = nix_nextweapon_ammo; nix_nextweapon = 0; if (!nix_nextchange) // no round played yet? nix_nextchange = time; // start the first round now! else nix_nextchange = time + autocvar_g_balance_nix_roundtime; - //weapon_action(nix_weapon, WR_PRECACHE); // forget it, too slow + //WEP_ACTION(nix_weapon, WR_INIT); // forget it, too slow } + // get weapon info + entity e = get_weaponinfo(nix_weapon); + if(nix_nextchange != self.nix_lastchange_id) // this shall only be called once per round! { - self.nix_lastchange_id = nix_nextchange; - if (self.items & IT_UNLIMITED_WEAPON_AMMO) + self.ammo_shells = self.ammo_nails = self.ammo_rockets = self.ammo_cells = self.ammo_plasma = self.ammo_fuel = 0; + + if(self.items & IT_UNLIMITED_WEAPON_AMMO) { - self.ammo_shells = (nix_weapon_ammo & IT_SHELLS) ? - autocvar_g_pickup_shells_max : 0; - self.ammo_nails = (nix_weapon_ammo & IT_NAILS) ? - autocvar_g_pickup_nails_max : 0; - self.ammo_rockets = (nix_weapon_ammo & IT_ROCKETS) ? - autocvar_g_pickup_rockets_max : 0; - self.ammo_cells = (nix_weapon_ammo & IT_CELLS) ? - autocvar_g_pickup_cells_max : 0; - self.ammo_fuel = (nix_weapon_ammo & IT_FUEL) ? - autocvar_g_pickup_fuel_max : 0; + switch(e.ammo_field) + { + case ammo_shells: self.ammo_shells = autocvar_g_pickup_shells_max; break; + case ammo_nails: self.ammo_nails = autocvar_g_pickup_nails_max; break; + case ammo_rockets: self.ammo_rockets = autocvar_g_pickup_rockets_max; break; + case ammo_cells: self.ammo_cells = autocvar_g_pickup_cells_max; break; + case ammo_plasma: self.ammo_plasma = autocvar_g_pickup_plasma_max; break; + case ammo_fuel: self.ammo_fuel = autocvar_g_pickup_fuel_max; break; + } } else { - self.ammo_shells = (nix_weapon_ammo & IT_SHELLS) ? - autocvar_g_balance_nix_ammo_shells : 0; - self.ammo_nails = (nix_weapon_ammo & IT_NAILS) ? - autocvar_g_balance_nix_ammo_nails : 0; - self.ammo_rockets = (nix_weapon_ammo & IT_ROCKETS) ? - autocvar_g_balance_nix_ammo_rockets : 0; - self.ammo_cells = (nix_weapon_ammo & IT_CELLS) ? - autocvar_g_balance_nix_ammo_cells : 0; - self.ammo_fuel = (nix_weapon_ammo & IT_FUEL) ? - autocvar_g_balance_nix_ammo_fuel : 0; + switch(e.ammo_field) + { + case ammo_shells: self.ammo_shells = autocvar_g_balance_nix_ammo_shells; break; + case ammo_nails: self.ammo_nails = autocvar_g_balance_nix_ammo_nails; break; + case ammo_rockets: self.ammo_rockets = autocvar_g_balance_nix_ammo_rockets; break; + case ammo_cells: self.ammo_cells = autocvar_g_balance_nix_ammo_cells; break; + case ammo_plasma: self.ammo_plasma = autocvar_g_balance_nix_ammo_plasma; break; + case ammo_fuel: self.ammo_fuel = autocvar_g_balance_nix_ammo_fuel; break; + } } + self.nix_nextincr = time + autocvar_g_balance_nix_incrtime; if(dt >= 1 && dt <= 5) self.nix_lastinfotime = -42; else Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon); - weapon_action(nix_weapon, WR_RESETPLAYER); + WEP_ACTION(nix_weapon, WR_RESETPLAYER); // all weapons must be fully loaded when we spawn - entity e; - e = get_weaponinfo(nix_weapon); if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars - self.(weapon_load[nix_weapon]) = cvar(strcat("g_balance_", e.netname, "_reload_ammo")); + self.(weapon_load[nix_weapon]) = e.reloading_ammo; - // nex too - if(autocvar_g_balance_nex_charge) + // vortex too + if(WEP_CVAR(vortex, charge)) { - if(autocvar_g_balance_nex_secondary_chargepool) - self.nex_chargepool_ammo = 1; - self.nex_charge = autocvar_g_balance_nex_charge_start; + if(WEP_CVAR_SEC(vortex, chargepool)) + self.vortex_chargepool_ammo = 1; + self.vortex_charge = WEP_CVAR(vortex, charge_start); } + + // set last change info + self.nix_lastchange_id = nix_nextchange; } if(self.nix_lastinfotime != dt) { @@ -123,22 +123,22 @@ void NIX_GiveCurrentWeapon() if(!(self.items & IT_UNLIMITED_WEAPON_AMMO) && time > self.nix_nextincr) { - if (nix_weapon_ammo & IT_SHELLS) - self.ammo_shells = self.ammo_shells + autocvar_g_balance_nix_ammoincr_shells; - else if (nix_weapon_ammo & IT_NAILS) - self.ammo_nails = self.ammo_nails + autocvar_g_balance_nix_ammoincr_nails; - else if (nix_weapon_ammo & IT_ROCKETS) - self.ammo_rockets = self.ammo_rockets + autocvar_g_balance_nix_ammoincr_rockets; - else if (nix_weapon_ammo & IT_CELLS) - self.ammo_cells = self.ammo_cells + autocvar_g_balance_nix_ammoincr_cells; - if (nix_weapon_ammo & IT_FUEL) // hook uses cells and fuel - self.ammo_fuel = self.ammo_fuel + autocvar_g_balance_nix_ammoincr_fuel; + switch(e.ammo_field) + { + case ammo_shells: self.ammo_shells += autocvar_g_balance_nix_ammoincr_shells; break; + case ammo_nails: self.ammo_nails += autocvar_g_balance_nix_ammoincr_nails; break; + case ammo_rockets: self.ammo_rockets += autocvar_g_balance_nix_ammoincr_rockets; break; + case ammo_cells: self.ammo_cells += autocvar_g_balance_nix_ammoincr_cells; break; + case ammo_plasma: self.ammo_plasma += autocvar_g_balance_nix_ammoincr_plasma; break; + case ammo_fuel: self.ammo_fuel += autocvar_g_balance_nix_ammoincr_fuel; break; + } + self.nix_nextincr = time + autocvar_g_balance_nix_incrtime; } self.weapons = '0 0 0'; if(g_nix_with_laser) - self.weapons &= ~WEPSET_LASER; + self.weapons &= ~WEPSET_BLASTER; self.weapons |= WepSet_FromWeapon(nix_weapon); if(self.switchweapon != nix_weapon) @@ -152,7 +152,7 @@ void NIX_precache() float i; for (i = WEP_FIRST; i <= WEP_LAST; ++i) if (NIX_CanChooseWeapon(i)) - weapon_action(i, WR_PRECACHE); + WEP_ACTION(i, WR_INIT); } MUTATOR_HOOKFUNCTION(nix_ForbidThrowCurrentWeapon) @@ -259,6 +259,7 @@ MUTATOR_DEFINITION(mutator_nix) FOR_EACH_PLAYER(e) if(e.deadflag == DEAD_NO) { e.ammo_cells = start_ammo_cells; + e.ammo_plasma = start_ammo_plasma; e.ammo_shells = start_ammo_shells; e.ammo_nails = start_ammo_nails; e.ammo_rockets = start_ammo_rockets; diff --git a/qcsrc/server/mutators/mutator_touchexplode.qc b/qcsrc/server/mutators/mutator_touchexplode.qc index 30b01b0daa..5f02a8aba2 100644 --- a/qcsrc/server/mutators/mutator_touchexplode.qc +++ b/qcsrc/server/mutators/mutator_touchexplode.qc @@ -12,7 +12,7 @@ void PlayerTouchExplode(entity p1, entity p2) entity e; e = spawn(); setorigin(e, org); - RadiusDamage(e, world, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, world, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE, world); + RadiusDamage(e, world, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, world, world, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE, world); remove(e); } diff --git a/qcsrc/server/progs.src b/qcsrc/server/progs.src index 288b247759..9cc8c9954d 100644 --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@ -11,7 +11,6 @@ sys-post.qh ../warpzonelib/common.qh ../warpzonelib/util_server.qh ../warpzonelib/server.qh - ../common/constants.qh ../common/stats.qh ../common/teams.qh @@ -20,8 +19,6 @@ sys-post.qh ../common/buffs.qh ../common/test.qh ../common/counting.qh -../common/items.qh -../common/explosion_equation.qh ../common/urllib.qh ../common/command/markup.qh ../common/command/rpn.qh @@ -33,6 +30,21 @@ sys-post.qh ../common/monsters/sv_monsters.qh ../common/monsters/spawn.qh +../common/weapons/config.qh +../common/weapons/weapons.qh // TODO +weapons/accuracy.qh +weapons/common.qh +weapons/csqcprojectile.qh // TODO +weapons/hitplot.qh +weapons/selection.qh +weapons/spawning.qh +weapons/throwing.qh +weapons/tracing.qh +weapons/weaponstats.qh +weapons/weaponsystem.qh + +t_items.qh + autocvars.qh constants.qh defs.qh // Should rename this, it has fields and globals @@ -58,8 +70,7 @@ command/getreplies.qh command/cmd.qh command/sv_cmd.qh -accuracy.qh -csqcprojectile.qh + ../common/csqcmodel_settings.qh ../csqcmodellib/common.qh ../csqcmodellib/sv_model.qh @@ -71,9 +82,7 @@ playerstats.qh portals.qh -g_hook.qh -w_electro.qh -w_laser.qh +g_hook.qh // TODO scores.qh @@ -140,13 +149,20 @@ g_models.qc item_key.qc secret.qc -cl_weaponsystem.qc -w_common.qc - -w_all.qc +weapons/accuracy.qc +weapons/common.qc +weapons/csqcprojectile.qc // TODO +weapons/hitplot.qc +weapons/selection.qc +weapons/spawning.qc +weapons/throwing.qc +weapons/tracing.qc +weapons/weaponstats.qc +weapons/weaponsystem.qc +../common/weapons/config.qc +../common/weapons/weapons.qc // TODO t_items.qc -cl_weapons.qc cl_impulse.qc ent_cs.qc @@ -209,15 +225,10 @@ target_spawn.qc func_breakable.qc target_music.qc -../common/items.qc - ../common/nades.qc ../common/buffs.qc - -accuracy.qc ../csqcmodellib/sv_model.qc -csqcprojectile.qc playerdemo.qc @@ -227,8 +238,6 @@ playerstats.qc round_handler.qc -../common/explosion_equation.qc - ../common/monsters/sv_monsters.qc ../common/monsters/monsters.qc diff --git a/qcsrc/server/scores_rules.qc b/qcsrc/server/scores_rules.qc index fbbf938037..6343625c0f 100644 --- a/qcsrc/server/scores_rules.qc +++ b/qcsrc/server/scores_rules.qc @@ -44,52 +44,3 @@ void ScoreRules_generic() ScoreRules_basics_end(); } -// Key hunt stuff -#define ST_KH_CAPS 1 -#define SP_KH_CAPS 4 -#define SP_KH_PUSHES 5 -#define SP_KH_DESTROYS 6 -#define SP_KH_PICKUPS 7 -#define SP_KH_KCKILLS 8 -#define SP_KH_LOSSES 9 -void ScoreRules_kh(float teams) -{ - ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, TRUE); - ScoreInfo_SetLabel_TeamScore (ST_KH_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); - ScoreInfo_SetLabel_PlayerScore(SP_KH_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); - ScoreInfo_SetLabel_PlayerScore(SP_KH_PUSHES, "pushes", 0); - ScoreInfo_SetLabel_PlayerScore(SP_KH_DESTROYS, "destroyed", SFL_LOWER_IS_BETTER); - ScoreInfo_SetLabel_PlayerScore(SP_KH_PICKUPS, "pickups", 0); - ScoreInfo_SetLabel_PlayerScore(SP_KH_KCKILLS, "kckills", 0); - ScoreInfo_SetLabel_PlayerScore(SP_KH_LOSSES, "losses", SFL_LOWER_IS_BETTER); - ScoreRules_basics_end(); -} - -// Nexball stuff -#define ST_NEXBALL_GOALS 1 -#define SP_NEXBALL_GOALS 4 -#define SP_NEXBALL_FAULTS 5 -void ScoreRules_nexball(float teams) -{ - ScoreRules_basics(teams, 0, 0, TRUE); - ScoreInfo_SetLabel_TeamScore( ST_NEXBALL_GOALS, "goals", SFL_SORT_PRIO_PRIMARY); - ScoreInfo_SetLabel_PlayerScore( SP_NEXBALL_GOALS, "goals", SFL_SORT_PRIO_PRIMARY); - ScoreInfo_SetLabel_PlayerScore(SP_NEXBALL_FAULTS, "faults", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER); - ScoreRules_basics_end(); -} - -#define SP_FREEZETAG_REVIVALS 4 -void ScoreRules_freezetag(float teams) -{ - ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, TRUE); // SFL_SORT_PRIO_PRIMARY - ScoreInfo_SetLabel_PlayerScore(SP_FREEZETAG_REVIVALS, "revivals", 0); - ScoreRules_basics_end(); -} - -#define ST_CA_ROUNDS 1 -void ScoreRules_ca(float teams) -{ - ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, TRUE); - ScoreInfo_SetLabel_TeamScore(ST_CA_ROUNDS, "rounds", SFL_SORT_PRIO_PRIMARY); - ScoreRules_basics_end(); -} diff --git a/qcsrc/server/t_items.qc b/qcsrc/server/t_items.qc index 238c6cec50..2e537b4a34 100644 --- a/qcsrc/server/t_items.qc +++ b/qcsrc/server/t_items.qc @@ -1,32 +1,4 @@ -#define ISF_LOCATION 2 -#define ISF_MODEL 4 -#define ISF_STATUS 8 - #define ITS_STAYWEP 1 - #define ITS_ANIMATE1 2 - #define ITS_ANIMATE2 4 - #define ITS_AVAILABLE 8 - #define ITS_ALLOWFB 16 - #define ITS_ALLOWSI 32 - #define ITS_POWERUP 64 -#define ISF_COLORMAP 16 -#define ISF_DROP 32 -#define ISF_ANGLES 64 - -.float ItemStatus; - #ifdef CSQC - -var float autocvar_cl_animate_items = 1; -var float autocvar_cl_ghost_items = 0.45; -var vector autocvar_cl_ghost_items_color = '-1 -1 -1'; -var float autocvar_cl_fullbright_items = 0; -var vector autocvar_cl_weapon_stay_color = '2 0.5 0.5'; -var float autocvar_cl_weapon_stay_alpha = 0.75; -var float autocvar_cl_simple_items = 0; -var string autocvar_cl_simpleitems_postfix = "_simple"; -.float spawntime; -.float gravity; -.vector colormod; void ItemDraw() { if(self.gravity) @@ -95,6 +67,17 @@ void ItemRead(float _IsNew) self.move_angles = self.angles; } + if(sf & ISF_SIZE) + { + self.mins_x = ReadCoord(); + self.mins_y = ReadCoord(); + self.mins_z = ReadCoord(); + self.maxs_x = ReadCoord(); + self.maxs_y = ReadCoord(); + self.maxs_z = ReadCoord(); + setsize(self, self.mins, self.maxs); + } + if(sf & ISF_STATUS) // need to read/write status frist so model can handle simple, fb etc. { self.ItemStatus = ReadByte(); @@ -138,7 +121,7 @@ void ItemRead(float _IsNew) if(sf & ISF_MODEL) { self.drawmask = MASK_NORMAL; - self.movetype = MOVETYPE_NOCLIP; + self.movetype = MOVETYPE_TOSS; self.draw = ItemDraw; if(self.mdl) @@ -186,7 +169,7 @@ void ItemRead(float _IsNew) if(sf & ISF_DROP) { self.gravity = 1; - self.move_angles = '0 0 0'; + //self.move_angles = '0 0 0'; self.move_movetype = MOVETYPE_TOSS; self.move_velocity_x = ReadCoord(); self.move_velocity_y = ReadCoord(); @@ -216,7 +199,6 @@ void ItemRead(float _IsNew) #endif #ifdef SVQC -float autocvar_sv_simple_items; float ItemSend(entity to, float sf) { if(self.gravity) @@ -242,6 +224,16 @@ float ItemSend(entity to, float sf) WriteCoord(MSG_ENTITY, self.angles_z); } + if(sf & ISF_SIZE) + { + WriteCoord(MSG_ENTITY, self.mins_x); + WriteCoord(MSG_ENTITY, self.mins_y); + WriteCoord(MSG_ENTITY, self.mins_z); + WriteCoord(MSG_ENTITY, self.maxs_x); + WriteCoord(MSG_ENTITY, self.maxs_y); + WriteCoord(MSG_ENTITY, self.maxs_z); + } + if(sf & ISF_STATUS) WriteByte(MSG_ENTITY, self.ItemStatus); @@ -289,63 +281,12 @@ float have_pickup_item(void) if(autocvar_g_pickup_items == 0) return FALSE; if(g_weaponarena) - if(self.weapons || (self.items & IT_AMMO)) + if(self.weapons || (self.items & IT_AMMO)) // no item or ammo pickups in weaponarena return FALSE; } return TRUE; } -#define ITEM_RESPAWN_TICKS 10 - -#define ITEM_RESPAWNTIME(i) ((i).respawntime + crandom() * (i).respawntimejitter) - // range: respawntime - respawntimejitter .. respawntime + respawntimejitter -#define ITEM_RESPAWNTIME_INITIAL(i) (ITEM_RESPAWN_TICKS + random() * ((i).respawntime + (i).respawntimejitter - ITEM_RESPAWN_TICKS)) - // range: 10 .. respawntime + respawntimejitter - -floatfield Item_CounterField(float it) -{ - switch(it) - { - case IT_SHELLS: return ammo_shells; - case IT_NAILS: return ammo_nails; - case IT_ROCKETS: return ammo_rockets; - case IT_CELLS: return ammo_cells; - case IT_FUEL: return ammo_fuel; - case IT_5HP: return health; - case IT_25HP: return health; - case IT_HEALTH: return health; - case IT_ARMOR_SHARD: return armorvalue; - case IT_ARMOR: return armorvalue; - // add more things here (health, armor) - default: error("requested item has no counter field"); - } -#ifdef GMQCC - // should never happen - return health; -#endif -} - -string Item_CounterFieldName(float it) -{ - switch(it) - { - case IT_SHELLS: return "shells"; - case IT_NAILS: return "nails"; - case IT_ROCKETS: return "rockets"; - case IT_CELLS: return "cells"; - case IT_FUEL: return "fuel"; - - // add more things here (health, armor) - default: error("requested item has no counter field name"); - } -#ifdef GMQCC - // should never happen - return string_null; -#endif -} - -.float max_armorvalue; -.float pickup_anyway; /* float Item_Customize() { @@ -430,6 +371,16 @@ void Item_Show (entity e, float mode) e.SendFlags |= ISF_STATUS; } +void Item_Think() +{ + self.nextthink = time; + if(self.origin != self.oldorigin) + { + self.oldorigin = self.origin; + ItemUpdate(self); + } +} + void Item_Respawn (void) { Item_Show(self, 1); @@ -442,6 +393,9 @@ void Item_Respawn (void) sound (self, CH_TRIGGER, "misc/itemrespawn.wav", VOL_BASE, ATTEN_NORM); // play respawn sound setorigin (self, self.origin); + self.think = Item_Think; + self.nextthink = time; + //pointparticles(particleeffectnum("item_respawn"), self.origin + self.mins_z * '0 0 1' + '0 0 48', '0 0 0', 1); pointparticles(particleeffectnum("item_respawn"), self.origin + 0.5 * (self.mins + self.maxs), '0 0 0', 1); } @@ -480,7 +434,7 @@ void Item_RespawnCountdown (void) entity wi = get_weaponinfo(self.weapon); if(wi) { - name = wi.model2; + name = wi.wpmodel; rgb = '1 0 0'; } } @@ -505,6 +459,19 @@ void Item_RespawnCountdown (void) } } +void Item_RespawnThink() +{ + self.nextthink = time; + if(self.origin != self.oldorigin) + { + self.oldorigin = self.origin; + ItemUpdate(self); + } + + if(time >= self.wait) + Item_Respawn(); +} + void Item_ScheduleRespawnIn(entity e, float t) { if((e.flags & FL_POWERUP) || (e.weapons & WEPSET_SUPERWEAPONS)) @@ -515,8 +482,9 @@ void Item_ScheduleRespawnIn(entity e, float t) } else { - e.think = Item_Respawn; - e.nextthink = time + t; + e.think = Item_RespawnThink; + e.nextthink = time; + e.wait = time + t; } } @@ -537,29 +505,25 @@ void Item_ScheduleInitialRespawn(entity e) Item_ScheduleRespawnIn(e, game_starttime - time + ITEM_RESPAWNTIME_INITIAL(e)); } -const float ITEM_MODE_NONE = 0; -const float ITEM_MODE_HEALTH = 1; -const float ITEM_MODE_ARMOR = 2; -const float ITEM_MODE_FUEL = 3; -float Item_GiveAmmoTo(entity item, entity player, .float ammofield, float ammomax, float mode) +float Item_GiveAmmoTo(entity item, entity player, .float ammotype, float ammomax, float mode) { - if (!item.ammofield) + if (!item.ammotype) return FALSE; if (item.spawnshieldtime) { - if ((player.ammofield < ammomax) || item.pickup_anyway) + if ((player.ammotype < ammomax) || item.pickup_anyway) { - player.ammofield = bound(player.ammofield, ammomax, player.ammofield + item.ammofield); + player.ammotype = bound(player.ammotype, ammomax, player.ammotype + item.ammotype); goto YEAH; } } else if(g_weapon_stay == 2) { - float mi = min(item.ammofield, ammomax); - if (player.ammofield < mi) + float mi = min(item.ammotype, ammomax); + if (player.ammotype < mi) { - player.ammofield = mi; + player.ammotype = mi; goto YEAH; } } @@ -609,6 +573,7 @@ float Item_GiveTo(entity item, entity player) pickedup |= Item_GiveAmmoTo(item, player, ammo_nails, g_pickup_nails_max, ITEM_MODE_NONE); pickedup |= Item_GiveAmmoTo(item, player, ammo_rockets, g_pickup_rockets_max, ITEM_MODE_NONE); pickedup |= Item_GiveAmmoTo(item, player, ammo_cells, g_pickup_cells_max, ITEM_MODE_NONE); + pickedup |= Item_GiveAmmoTo(item, player, ammo_plasma, g_pickup_plasma_max, ITEM_MODE_NONE); pickedup |= Item_GiveAmmoTo(item, player, health, item.max_health, ITEM_MODE_HEALTH); pickedup |= Item_GiveAmmoTo(item, player, armorvalue, item.max_armorvalue, ITEM_MODE_ARMOR); @@ -623,7 +588,10 @@ float Item_GiveTo(entity item, entity player) pickedup = TRUE; for(i = WEP_FIRST; i <= WEP_LAST; ++i) if(it & WepSet_FromWeapon(i)) + { + W_DropEvent(WR_PICKUP, player, i, item); W_GiveWeapon(player, i); + } } } @@ -682,6 +650,8 @@ void Item_Touch (void) if (!IS_PLAYER(other)) return; + if (other.frozen) + return; if (other.deadflag) return; if (self.solid != SOLID_TRIGGER) @@ -756,8 +726,8 @@ void Item_Reset() if(self.classname != "droppedweapon") { - self.think = func_null; - self.nextthink = 0; + self.think = Item_Think; + self.nextthink = time; if(self.waypointsprite_attached) WaypointSprite_Kill(self.waypointsprite_attached); @@ -819,7 +789,7 @@ float weapon_pickupevalfunc(entity player, entity item) // If I can pick it up if(!item.spawnshieldtime) c = 0; - else if(player.ammo_cells || player.ammo_shells || player.ammo_nails || player.ammo_rockets) + else if(player.ammo_cells || player.ammo_shells || player.ammo_plasma || player.ammo_nails || player.ammo_rockets) { // Skilled bots will grab more c = bound(0, skill / 10, 1) * 0.5; @@ -862,7 +832,7 @@ float weapon_pickupevalfunc(entity player, entity item) float commodity_pickupevalfunc(entity player, entity item) { float c, i; - float need_shells = FALSE, need_nails = FALSE, need_rockets = FALSE, need_cells = FALSE, need_fuel = FALSE; + float need_shells = FALSE, need_nails = FALSE, need_rockets = FALSE, need_cells = FALSE, need_plasma = FALSE, need_fuel = FALSE; entity wi; c = 0; @@ -882,6 +852,8 @@ float commodity_pickupevalfunc(entity player, entity item) need_rockets = TRUE; else if(wi.items & IT_CELLS) need_cells = TRUE; + else if(wi.items & IT_PLASMA) + need_plasma = TRUE; else if(wi.items & IT_FUEL) need_cells = TRUE; } @@ -905,6 +877,10 @@ float commodity_pickupevalfunc(entity player, entity item) if (item.ammo_cells) if (player.ammo_cells < g_pickup_cells_max) c = c + max(0, 1 - player.ammo_cells / g_pickup_cells_max); + if (need_plasma) + if (item.ammo_plasma) + if (player.ammo_plasma < g_pickup_plasma_max) + c = c + max(0, 1 - player.ammo_plasma / g_pickup_plasma_max); if (need_fuel) if (item.ammo_fuel) if (player.ammo_fuel < g_pickup_fuel_max) @@ -925,7 +901,6 @@ void Item_Damage(entity inflictor, entity attacker, float damage, float deathtyp RemoveItem(); } -.float is_item; void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, float defaultrespawntimejitter, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue) { startitem_failed = FALSE; @@ -1012,9 +987,6 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, return; } - if(self.angles != '0 0 0') - self.SendFlags |= ISF_ANGLES; - self.reset = Item_Reset; // it's a level item if(self.spawnflags & 1) @@ -1033,6 +1005,7 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, setsize (self, '-16 -16 0', '16 16 48'); else setsize (self, '-16 -16 0', '16 16 32'); + // note droptofloor returns FALSE if stuck/or would fall too far droptofloor(); waypoint_spawnforitem(self); @@ -1136,6 +1109,10 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, Item_Reset(); Net_LinkEntity(self, FALSE, 0, ItemSend); + + self.SendFlags |= ISF_SIZE; + if(self.angles) + self.SendFlags |= ISF_ANGLES; // call this hook after everything else has been done if(MUTATOR_CALLHOOK(Item_Spawn)) @@ -1145,183 +1122,6 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, return; } } - -float weaponswapping; -float internalteam; - -void weapon_defaultspawnfunc(float wpn) -{ - entity e; - float t; - var .float ammofield; - string s; - entity oldself; - float i, j; - float f; - - if(self.classname != "droppedweapon" && self.classname != "replacedweapon") - { - e = get_weaponinfo(wpn); - - if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) - { - objerror("Attempted to spawn a mutator-blocked weapon rejected"); - startitem_failed = TRUE; - return; - } - - s = W_Apply_Weaponreplace(e.netname); - ret_string = s; - other = e; - MUTATOR_CALLHOOK(SetWeaponreplace); - s = ret_string; - if(s == "") - { - remove(self); - startitem_failed = TRUE; - return; - } - t = tokenize_console(s); - if(t >= 2) - { - self.team = --internalteam; - oldself = self; - for(i = 1; i < t; ++i) - { - s = argv(i); - for(j = WEP_FIRST; j <= WEP_LAST; ++j) - { - e = get_weaponinfo(j); - if(e.netname == s) - { - self = spawn(); - copyentity(oldself, self); - self.classname = "replacedweapon"; - weapon_defaultspawnfunc(j); - break; - } - } - if(j > WEP_LAST) - { - print("The weapon replace list for ", oldself.classname, " contains an unknown weapon ", s, ". Skipped.\n"); - } - } - self = oldself; - } - if(t >= 1) // always the case! - { - s = argv(0); - wpn = 0; - for(j = WEP_FIRST; j <= WEP_LAST; ++j) - { - e = get_weaponinfo(j); - if(e.netname == s) - { - wpn = j; - break; - } - } - if(j > WEP_LAST) - { - print("The weapon replace list for ", self.classname, " contains an unknown weapon ", s, ". Skipped.\n"); - } - } - if(wpn == 0) - { - remove(self); - startitem_failed = TRUE; - return; - } - } - - e = get_weaponinfo(wpn); - - if(!self.respawntime) - { - if(e.weapons & WEPSET_SUPERWEAPONS) - { - self.respawntime = g_pickup_respawntime_superweapon; - self.respawntimejitter = g_pickup_respawntimejitter_superweapon; - } - else - { - self.respawntime = g_pickup_respawntime_weapon; - self.respawntimejitter = g_pickup_respawntimejitter_weapon; - } - } - - if(e.weapons & WEPSET_SUPERWEAPONS) - if(!self.superweapons_finished) - self.superweapons_finished = autocvar_g_balance_superweapons_time; - - if(e.items) - { - for(i = 0, j = 1; i < 24; ++i, j *= 2) - { - if(e.items & j) - { - ammofield = Item_CounterField(j); - if(!self.ammofield) - self.ammofield = cvar(strcat("g_pickup_", Item_CounterFieldName(j), "_weapon")); - } - } - } - - // pickup anyway - if(g_pickup_weapons_anyway) - self.pickup_anyway = TRUE; - - f = FL_WEAPON; - - // no weapon-stay on superweapons - if(e.weapons & WEPSET_SUPERWEAPONS) - f |= FL_NO_WEAPON_STAY; - - // weapon stay isn't supported for teamed weapons - if(self.team) - f |= FL_NO_WEAPON_STAY; - - StartItem(e.model, "weapons/weaponpickup.wav", self.respawntime, self.respawntimejitter, e.message, 0, e.weapon, f, weapon_pickupevalfunc, e.bot_pickupbasevalue); - if (self.modelindex) // don't precache if self was removed - weapon_action(e.weapon, WR_PRECACHE); -} - -void spawnfunc_weapon_shotgun (void); -void spawnfunc_weapon_uzi (void) { - if(autocvar_sv_q3acompat_machineshotgunswap) - if(self.classname != "droppedweapon") - { - weapon_defaultspawnfunc(WEP_SHOTGUN); - return; - } - weapon_defaultspawnfunc(WEP_UZI); -} - -void spawnfunc_weapon_shotgun (void) { - if(autocvar_sv_q3acompat_machineshotgunswap) - if(self.classname != "droppedweapon") - { - weapon_defaultspawnfunc(WEP_UZI); - return; - } - weapon_defaultspawnfunc(WEP_SHOTGUN); -} - -void spawnfunc_weapon_nex (void) -{ - weapon_defaultspawnfunc(WEP_NEX); -} - -void spawnfunc_weapon_minstanex (void) -{ - weapon_defaultspawnfunc(WEP_MINSTANEX); -} - -void spawnfunc_weapon_rocketlauncher (void) -{ - weapon_defaultspawnfunc(WEP_ROCKET_LAUNCHER); -} - void spawnfunc_item_rockets (void) { if(!self.ammo_rockets) self.ammo_rockets = g_pickup_rockets; @@ -1357,6 +1157,15 @@ void spawnfunc_item_cells (void) { StartItem ("models/items/a_cells.md3", "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "cells", IT_CELLS, 0, 0, commodity_pickupevalfunc, 2000); } +void spawnfunc_item_plasma() +{ + if(!self.ammo_plasma) + self.ammo_plasma = g_pickup_plasma; + if(!self.pickup_anyway) + self.pickup_anyway = g_pickup_ammo_anyway; + StartItem ("models/items/a_cells.md3", "misc/itempickup.wav", g_pickup_respawntime_ammo, g_pickup_respawntimejitter_ammo, "plasma", IT_PLASMA, 0, 0, commodity_pickupevalfunc, 2000); +} + void spawnfunc_item_shells (void) { if(!weaponswapping) if(autocvar_sv_q3acompat_machineshotgunswap) @@ -1478,7 +1287,6 @@ void spawnfunc_item_invincible (void) { // compatibility: void spawnfunc_item_quad (void) {self.classname = "item_strength";spawnfunc_item_strength();} -float GiveItems(entity e, float beginarg, float endarg); void target_items_use (void) { if(activator.classname == "droppedweapon") @@ -1549,7 +1357,7 @@ void spawnfunc_target_items (void) { self.weapons |= WepSet_FromWeapon(j); if(self.spawnflags == 0 || self.spawnflags == 2) - weapon_action(e.weapon, WR_PRECACHE); + WEP_ACTION(e.weapon, WR_INIT); break; } } @@ -1600,6 +1408,7 @@ void spawnfunc_target_items (void) if(self.ammo_nails != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_nails), "nails"); if(self.ammo_rockets != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_rockets), "rockets"); if(self.ammo_cells != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_cells), "cells"); + if(self.ammo_plasma != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_plasma), "plasma"); if(self.ammo_fuel != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_fuel), "fuel"); if(self.health != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.health), "health"); if(self.armorvalue != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.health), "armor"); @@ -1621,7 +1430,7 @@ void spawnfunc_target_items (void) e = get_weaponinfo(j); if(argv(i) == e.netname) { - weapon_action(e.weapon, WR_PRECACHE); + WEP_ACTION(e.weapon, WR_INIT); break; } } @@ -1661,13 +1470,6 @@ void spawnfunc_item_jetpack(void) StartItem ("models/items/g_jetpack.md3", "misc/itempickup.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Jet pack", IT_JETPACK, 0, FL_POWERUP, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW); } - -#define OP_SET 0 -#define OP_MIN 1 -#define OP_MAX 2 -#define OP_PLUS 3 -#define OP_MINUS 4 - float GiveWeapon(entity e, float wpn, float op, float val) { WepSet v0, v1; @@ -1777,14 +1579,6 @@ void GiveRot(entity e, float v0, float v1, .float rotfield, float rottime, .floa else if(v0 > v1) e.regenfield = max(e.regenfield, time + regentime); } - -#define PREGIVE_WEAPONS(e) WepSet save_weapons; save_weapons = e.weapons -#define PREGIVE(e,f) float save_##f; save_##f = (e).f -#define POSTGIVE_WEAPON(e,b,snd_incr,snd_decr) GiveSound((e), !!(save_weapons & WepSet_FromWeapon(b)), !!(e.weapons & WepSet_FromWeapon(b)), 0, snd_incr, snd_decr) -#define POSTGIVE_BIT(e,f,b,snd_incr,snd_decr) GiveSound((e), save_##f & (b), (e).f & (b), 0, snd_incr, snd_decr) -#define POSTGIVE_VALUE(e,f,t,snd_incr,snd_decr) GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr) -#define POSTGIVE_VALUE_ROT(e,f,t,rotfield,rottime,regenfield,regentime,snd_incr,snd_decr) GiveRot((e), save_##f, (e).f, rotfield, rottime, regenfield, regentime); GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr) - float GiveItems(entity e, float beginarg, float endarg) { float got, i, j, val, op; @@ -1813,6 +1607,7 @@ float GiveItems(entity e, float beginarg, float endarg) PREGIVE(e, superweapons_finished); PREGIVE(e, ammo_nails); PREGIVE(e, ammo_cells); + PREGIVE(e, ammo_plasma); PREGIVE(e, ammo_shells); PREGIVE(e, ammo_rockets); PREGIVE(e, ammo_fuel); @@ -1866,6 +1661,7 @@ float GiveItems(entity e, float beginarg, float endarg) } case "allammo": got += GiveValue(e, ammo_cells, op, val); + got += GiveValue(e, ammo_plasma, op, val); got += GiveValue(e, ammo_shells, op, val); got += GiveValue(e, ammo_nails, op, val); got += GiveValue(e, ammo_rockets, op, val); @@ -1898,6 +1694,9 @@ float GiveItems(entity e, float beginarg, float endarg) case "cells": got += GiveValue(e, ammo_cells, op, val); break; + case "plasma": + got += GiveValue(e, ammo_plasma, op, val); + break; case "shells": got += GiveValue(e, ammo_shells, op, val); break; @@ -1947,13 +1746,14 @@ float GiveItems(entity e, float beginarg, float endarg) POSTGIVE_WEAPON(e, j, "weapons/weaponpickup.wav", string_null); if (!(save_weapons & WepSet_FromWeapon(j))) if(e.weapons & WepSet_FromWeapon(j)) - weapon_action(wi.weapon, WR_PRECACHE); + WEP_ACTION(wi.weapon, WR_INIT); } } POSTGIVE_VALUE(e, strength_finished, 1, "misc/powerup.wav", "misc/poweroff.wav"); POSTGIVE_VALUE(e, invincible_finished, 1, "misc/powerup_shield.wav", "misc/poweroff.wav"); POSTGIVE_VALUE(e, ammo_nails, 0, "misc/itempickup.wav", string_null); POSTGIVE_VALUE(e, ammo_cells, 0, "misc/itempickup.wav", string_null); + POSTGIVE_VALUE(e, ammo_plasma, 0, "misc/itempickup.wav", string_null); POSTGIVE_VALUE(e, ammo_shells, 0, "misc/itempickup.wav", string_null); POSTGIVE_VALUE(e, ammo_rockets, 0, "misc/itempickup.wav", string_null); POSTGIVE_VALUE_ROT(e, ammo_fuel, 1, pauserotfuel_finished, autocvar_g_balance_pause_fuel_rot, pauseregen_finished, autocvar_g_balance_pause_fuel_regen, "misc/itempickup.wav", string_null); diff --git a/qcsrc/server/t_items.qh b/qcsrc/server/t_items.qh new file mode 100644 index 0000000000..13cc3796fe --- /dev/null +++ b/qcsrc/server/t_items.qh @@ -0,0 +1,169 @@ +// constants +#define WANT_CONST /* we WANT these to be constant, but it conflicts with the declaration in dpdefs/progsdefs.qc */ +const float IT_UNLIMITED_WEAPON_AMMO = 1; // when this bit is set, using a weapon does not reduce ammo. Checkpoints can give this powerup. +const float IT_UNLIMITED_SUPERWEAPONS = 2; // when this bit is set, superweapons don't expire. Checkpoints can give this powerup. +const float IT_CTF_SHIELDED = 4; // set for the flag shield +const float IT_USING_JETPACK = 8; // confirmation that button is pressed +const float IT_JETPACK = 16; // actual item +const float IT_FUEL_REGEN = 32; // fuel regeneration trigger +// where is 64... ? +const float IT_FUEL = 128; +WANT_CONST float IT_SHELLS = 256; +WANT_CONST float IT_NAILS = 512; +WANT_CONST float IT_ROCKETS = 1024; +WANT_CONST float IT_CELLS = 2048; +const float IT_SUPERWEAPON = 4096; +const float IT_STRENGTH = 8192; +const float IT_INVINCIBLE = 16384; +const float IT_HEALTH = 32768; +const float IT_PLASMA = 65536; + +// shared value space (union): + // for items: + WANT_CONST float IT_KEY1 = 131072; + WANT_CONST float IT_KEY2 = 262144; + // for players: + const float IT_RED_FLAG_TAKEN = 32768; + const float IT_RED_FLAG_LOST = 65536; + const float IT_RED_FLAG_CARRYING = 98304; + const float IT_BLUE_FLAG_TAKEN = 131072; + const float IT_BLUE_FLAG_LOST = 262144; + const float IT_BLUE_FLAG_CARRYING = 393216; +// end + +const float IT_5HP = 524288; +const float IT_25HP = 1048576; +const float IT_ARMOR_SHARD = 2097152; +const float IT_ARMOR = 4194304; + +// item masks +const float IT_AMMO = 3968; // IT_FUEL | IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS | IT_PLASMA; +const float IT_PICKUPMASK = 51; // IT_FUEL_REGEN | IT_JETPACK | IT_UNLIMITED_AMMO; // strength and invincible are handled separately +const float IT_UNLIMITED_AMMO = 3; // IT_UNLIMITED_SUPERWEAPONS | IT_UNLIMITED_WEAPON_AMMO; + +const float AMMO_COUNT = 4; // amount of ammo types to show in the inventory panel + +// item networking +#define ISF_LOCATION 2 +#define ISF_MODEL 4 +#define ISF_STATUS 8 + #define ITS_STAYWEP 1 + #define ITS_ANIMATE1 2 + #define ITS_ANIMATE2 4 + #define ITS_AVAILABLE 8 + #define ITS_ALLOWFB 16 + #define ITS_ALLOWSI 32 + #define ITS_POWERUP 64 +#define ISF_COLORMAP 16 +#define ISF_DROP 32 +#define ISF_ANGLES 64 +#define ISF_SIZE 128 + +.float ItemStatus; + +#ifdef CSQC + +var float autocvar_cl_animate_items = 1; +var float autocvar_cl_ghost_items = 0.45; +var vector autocvar_cl_ghost_items_color = '-1 -1 -1'; +var float autocvar_cl_fullbright_items = 0; +var vector autocvar_cl_weapon_stay_color = '2 0.5 0.5'; +var float autocvar_cl_weapon_stay_alpha = 0.75; +var float autocvar_cl_simple_items = 0; +var string autocvar_cl_simpleitems_postfix = "_simple"; +.float spawntime; +.float gravity; +.vector colormod; +void ItemDraw(); + +void ItemDrawSimple(); + +void ItemRead(float _IsNew); + +#endif +#ifdef SVQC +float autocvar_sv_simple_items; +float ItemSend(entity to, float sf); + + +float have_pickup_item(void); + +#define ITEM_RESPAWN_TICKS 10 + +#define ITEM_RESPAWNTIME(i) ((i).respawntime + crandom() * (i).respawntimejitter) + // range: respawntime - respawntimejitter .. respawntime + respawntimejitter +#define ITEM_RESPAWNTIME_INITIAL(i) (ITEM_RESPAWN_TICKS + random() * ((i).respawntime + (i).respawntimejitter - ITEM_RESPAWN_TICKS)) + // range: 10 .. respawntime + respawntimejitter + +.float max_armorvalue; +.float pickup_anyway; + +void Item_Show (entity e, float mode); + +void Item_Respawn (void); + +void Item_RespawnCountdown (void); +void Item_ScheduleRespawnIn(entity e, float t); + +void Item_ScheduleRespawn(entity e); + +void Item_ScheduleInitialRespawn(entity e); +float ITEM_MODE_NONE = 0; +float ITEM_MODE_HEALTH = 1; +float ITEM_MODE_ARMOR = 2; +float ITEM_MODE_FUEL = 3; +float Item_GiveAmmoTo(entity item, entity player, .float ammotype, float ammomax, float mode); + +float Item_GiveTo(entity item, entity player); + +void Item_Touch (void); + +void Item_Reset(); + +void Item_FindTeam(); +// Savage: used for item garbage-collection +// TODO: perhaps nice special effect? + +float ItemSend(entity to, float sf); +void ItemUpdate(entity item); + +// pickup evaluation functions +// these functions decide how desirable an item is to the bots + +float generic_pickupevalfunc(entity player, entity item);// {return item.bot_pickupbasevalue;} // WEAPONTODO + +float weapon_pickupevalfunc(entity player, entity item); + +float commodity_pickupevalfunc(entity player, entity item); + +.float is_item; +void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, float defaultrespawntimejitter, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue); + + +void target_items_use (void); + +#define OP_SET 0 +#define OP_MIN 1 +#define OP_MAX 2 +#define OP_PLUS 3 +#define OP_MINUS 4 + +float GiveWeapon(entity e, float wpn, float op, float val); + +float GiveBit(entity e, .float fld, float bit, float op, float val); + +float GiveValue(entity e, .float fld, float op, float val); + +void GiveSound(entity e, float v0, float v1, float t, string snd_incr, string snd_decr); + +void GiveRot(entity e, float v0, float v1, .float rotfield, float rottime, .float regenfield, float regentime); + +#define PREGIVE_WEAPONS(e) WepSet save_weapons; save_weapons = e.weapons +#define PREGIVE(e,f) float save_##f; save_##f = (e).f +#define POSTGIVE_WEAPON(e,b,snd_incr,snd_decr) GiveSound((e), !!(save_weapons & WepSet_FromWeapon(b)), !!(e.weapons & WepSet_FromWeapon(b)), 0, snd_incr, snd_decr) +#define POSTGIVE_BIT(e,f,b,snd_incr,snd_decr) GiveSound((e), save_##f & (b), (e).f & (b), 0, snd_incr, snd_decr) +#define POSTGIVE_VALUE(e,f,t,snd_incr,snd_decr) GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr) +#define POSTGIVE_VALUE_ROT(e,f,t,rotfield,rottime,regenfield,regentime,snd_incr,snd_decr) GiveRot((e), save_##f, (e).f, rotfield, rottime, regenfield, regentime); GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr) + +float GiveItems(entity e, float beginarg, float endarg); +#endif diff --git a/qcsrc/server/t_quake.qc b/qcsrc/server/t_quake.qc index ab49c6db5d..219a6ae231 100644 --- a/qcsrc/server/t_quake.qc +++ b/qcsrc/server/t_quake.qc @@ -3,7 +3,7 @@ //*********************** void spawnfunc_weapon_nailgun (void) {spawnfunc_weapon_electro();} void spawnfunc_weapon_supernailgun (void) {spawnfunc_weapon_hagar();} -void spawnfunc_weapon_supershotgun (void) {spawnfunc_weapon_uzi();} +void spawnfunc_weapon_supershotgun (void) {spawnfunc_weapon_machinegun();} void spawnfunc_item_spikes (void) {spawnfunc_item_bullets();} //void spawnfunc_item_armor1 (void) {spawnfunc_item_armor_medium;} // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard diff --git a/qcsrc/server/t_quake3.qc b/qcsrc/server/t_quake3.qc index 86a87c043d..7c1a582879 100644 --- a/qcsrc/server/t_quake3.qc +++ b/qcsrc/server/t_quake3.qc @@ -8,13 +8,12 @@ void spawnfunc_ammo_shells() { spawnfunc_item_shells(); } // MG -> MG -void spawnfunc_weapon_machinegun() { spawnfunc_weapon_uzi(); } void spawnfunc_ammo_bullets() { spawnfunc_item_bullets(); } // GL -> Mortar void spawnfunc_ammo_grenades() { spawnfunc_item_rockets(); } -// LG -> Electro +// LG -> Lightning void spawnfunc_weapon_lightning() { spawnfunc_weapon_electro(); } void spawnfunc_ammo_lightning() { spawnfunc_item_cells(); } @@ -22,13 +21,13 @@ void spawnfunc_ammo_lightning() { spawnfunc_item_cells(); } void spawnfunc_weapon_plasmagun() { spawnfunc_weapon_hagar(); } void spawnfunc_ammo_cells() { spawnfunc_item_rockets(); } -// Rail -> Nex -void spawnfunc_weapon_railgun() { spawnfunc_weapon_nex(); } -void spawnfunc_ammo_slugs() { spawnfunc_item_cells(); } +// Rail -> Vortex +void spawnfunc_weapon_railgun() { spawnfunc_weapon_vortex(); } +void spawnfunc_ammo_slugs() { spawnfunc_item_plasma(); } // BFG -> Crylink void spawnfunc_weapon_bfg() { spawnfunc_weapon_crylink(); } -void spawnfunc_ammo_bfg() { spawnfunc_item_cells(); } +void spawnfunc_ammo_bfg() { spawnfunc_item_plasma(); } // RL -> RL void spawnfunc_ammo_rockets() { spawnfunc_item_rockets(); } @@ -71,30 +70,30 @@ void target_give_init() { entity targ; for (targ = world; (targ = find(targ, targetname, self.target)); ) { - if (targ.classname == "weapon_rocketlauncher") { - self.ammo_rockets += targ.count * autocvar_g_balance_rocketlauncher_ammo; - self.netname = "rocketlauncher"; + if (targ.classname == "weapon_rocketlauncher" || targ.classname == "weapon_devastator") { + self.ammo_rockets += targ.count * WEP_CVAR(devastator, ammo); + self.netname = "devastator"; } else if (targ.classname == "weapon_plasmagun") { - self.ammo_rockets += targ.count * autocvar_g_balance_hagar_primary_ammo; + self.ammo_rockets += targ.count * WEP_CVAR_PRI(hagar, ammo); // WEAPONTODO if(self.netname == "") self.netname = "hagar"; else self.netname = strcat(self.netname, " hagar"); } else if (targ.classname == "weapon_bfg") { - self.ammo_cells += targ.count * autocvar_g_balance_crylink_primary_ammo; + self.ammo_cells += targ.count * WEP_CVAR_PRI(crylink, ammo); if(self.netname == "") self.netname = "crylink"; else self.netname = strcat(self.netname, " crylink"); } - else if (targ.classname == "weapon_grenadelauncher") { - self.ammo_rockets += targ.count * autocvar_g_balance_grenadelauncher_primary_ammo; + else if (targ.classname == "weapon_grenadelauncher" || targ.classname == "weapon_mortar") { + self.ammo_rockets += targ.count * WEP_CVAR_PRI(mortar, ammo); // WEAPONTODO if(self.netname == "") - self.netname = "grenadelauncher"; + self.netname = "mortar"; else - self.netname = strcat(self.netname, " grenadelauncher"); + self.netname = strcat(self.netname, " mortar"); } else if (targ.classname == "item_armor_body") self.armorvalue = 100; diff --git a/qcsrc/server/t_teleporters.qc b/qcsrc/server/t_teleporters.qc index 6e7c35b6a6..543c1cf0bb 100644 --- a/qcsrc/server/t_teleporters.qc +++ b/qcsrc/server/t_teleporters.qc @@ -80,6 +80,7 @@ void spawn_tdeath(vector v0, entity e, vector v) #define TELEPORT_NORMAL 1 // play sounds/effects etc #define TELEPORT_SIMPLE 2 // only do teleport, nothing special +void Reset_ArcBeam(entity player, vector forward); void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags) { entity telefragger; @@ -117,6 +118,8 @@ void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angle player.velocity = to_velocity; BITXOR_ASSIGN(player.effects, EF_TELEPORT_BIT); + makevectors(player.angles); + Reset_ArcBeam(player, v_forward); UpdateCSQCProjectileAfterTeleport(player); if(IS_PLAYER(player)) @@ -337,6 +340,8 @@ void spawnfunc_trigger_teleport (void) void WarpZone_PostTeleportPlayer_Callback(entity pl) { + makevectors(pl.angles); + Reset_ArcBeam(pl, v_forward); UpdateCSQCProjectileAfterTeleport(pl); { entity oldself = self; diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index 90302bc45b..bd5d3607c1 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -291,7 +291,7 @@ string getwelcomemessage(void) else modifications = strcat(modifications, ", ", g_weaponarena_list, " Arena"); } - if(autocvar_g_start_weapon_laser == 0) + if(cvar("g_balance_blaster_weaponstart") == 0) modifications = strcat(modifications, ", No start weapons"); if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity"))) modifications = strcat(modifications, ", Low gravity"); @@ -515,7 +515,7 @@ void GetTeamCounts(entity ignore) FOR_EACH_CLIENT(head) { float t; - if(IS_PLAYER(head)) + if(IS_PLAYER(head) || head.caplayer) t = head.team; else if(head.team_forced > 0) t = head.team_forced; // reserve the spot diff --git a/qcsrc/server/tturrets/system/system_main.qc b/qcsrc/server/tturrets/system/system_main.qc index d35573b541..d56a81bbf0 100644 --- a/qcsrc/server/tturrets/system/system_main.qc +++ b/qcsrc/server/tturrets/system/system_main.qc @@ -148,11 +148,11 @@ void turret_projectile_explode() self.event_damage = func_null; #ifdef TURRET_DEBUG float d; - d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, self.owner.shot_force, self.totalfrags, world); + d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world); self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; #else - RadiusDamage (self, self.realowner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, self.owner.shot_force, self.totalfrags, world); + RadiusDamage (self, self.realowner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world); #endif remove(self); } diff --git a/qcsrc/server/tturrets/units/unit_ewheel.qc b/qcsrc/server/tturrets/units/unit_ewheel.qc index b98388984c..e8e677ac8c 100644 --- a/qcsrc/server/tturrets/units/unit_ewheel.qc +++ b/qcsrc/server/tturrets/units/unit_ewheel.qc @@ -13,7 +13,7 @@ void ewheel_attack() { turret_do_updates(self); - _mis = turret_projectile("weapons/lasergun_fire.wav", 1, 0, DEATH_TURRET_EWHEEL, PROJECTILE_LASER, TRUE, TRUE); + _mis = turret_projectile("weapons/lasergun_fire.wav", 1, 0, DEATH_TURRET_EWHEEL, PROJECTILE_BLASTER, TRUE, TRUE); // WEAPONTODO: this is not a projectile made by the blaster, add separate effect for it _mis.missile_flags = MIF_SPLASH; pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1); diff --git a/qcsrc/server/tturrets/units/unit_flac.qc b/qcsrc/server/tturrets/units/unit_flac.qc index 8a01a9752a..3c9e55863f 100644 --- a/qcsrc/server/tturrets/units/unit_flac.qc +++ b/qcsrc/server/tturrets/units/unit_flac.qc @@ -10,11 +10,11 @@ void turret_flac_projectile_think_explode() #ifdef TURRET_DEBUG float d; - d = RadiusDamage (self, self.owner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, self.owner.shot_force, self.totalfrags, world); + d = RadiusDamage (self, self.owner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world); self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d; self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg; #else - RadiusDamage (self, self.realowner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, self.owner.shot_force, self.totalfrags, world); + RadiusDamage (self, self.realowner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world); #endif remove(self); } diff --git a/qcsrc/server/tturrets/units/unit_machinegun.qc b/qcsrc/server/tturrets/units/unit_machinegun.qc index eccc37f03f..d235dfb327 100644 --- a/qcsrc/server/tturrets/units/unit_machinegun.qc +++ b/qcsrc/server/tturrets/units/unit_machinegun.qc @@ -7,7 +7,7 @@ void turret_machinegun_attack() { fireBullet (self.tur_shotorg, self.tur_shotdir_updated,self.shot_spread, 0, self.shot_dmg, self.shot_force, DEATH_TURRET_MACHINEGUN, 0); - UziFlash(); + W_MachineGun_MuzzleFlash(); // WEAPONTODO setattachment(self.muzzle_flash, self.tur_head, "tag_fire"); } diff --git a/qcsrc/server/tturrets/units/unit_walker.qc b/qcsrc/server/tturrets/units/unit_walker.qc index 2eeb08b8fd..a91daa1901 100644 --- a/qcsrc/server/tturrets/units/unit_walker.qc +++ b/qcsrc/server/tturrets/units/unit_walker.qc @@ -51,7 +51,8 @@ void walker_setnoanim() } void walker_rocket_explode() { - RadiusDamage (self, self.owner, autocvar_g_turrets_unit_walker_std_rocket_dmg, 0, autocvar_g_turrets_unit_walker_std_rocket_radius, self, autocvar_g_turrets_unit_walker_std_rocket_force, DEATH_TURRET_WALK_ROCKET, world); + RadiusDamage (self, self.owner, autocvar_g_turrets_unit_walker_std_rocket_dmg, 0, autocvar_g_turrets_unit_walker_std_rocket_radius, self, world, autocvar_g_turrets_unit_walker_std_rocket_force, DEATH_TURRET_WALK_ROCKET, world); + remove (self); } diff --git a/qcsrc/server/vehicles/bumblebee.qc b/qcsrc/server/vehicles/bumblebee.qc index 657d227051..8b8d308f9d 100644 --- a/qcsrc/server/vehicles/bumblebee.qc +++ b/qcsrc/server/vehicles/bumblebee.qc @@ -702,7 +702,7 @@ void bumb_blowup() { RadiusDamage(self, self.enemy, autocvar_g_vehicle_bumblebee_blowup_coredamage, autocvar_g_vehicle_bumblebee_blowup_edgedamage, - autocvar_g_vehicle_bumblebee_blowup_radius, self, + autocvar_g_vehicle_bumblebee_blowup_radius, self, world, autocvar_g_vehicle_bumblebee_blowup_forceintensity, DEATH_VH_BUMB_DEATH, world); @@ -787,7 +787,6 @@ void bumb_die() self.nextthink = 0; setorigin(self, self.pos1); - } void bumb_impact() diff --git a/qcsrc/server/vehicles/racer.qc b/qcsrc/server/vehicles/racer.qc index f009723b7f..fc9cee8657 100644 --- a/qcsrc/server/vehicles/racer.qc +++ b/qcsrc/server/vehicles/racer.qc @@ -539,7 +539,7 @@ void racer_blowup() RadiusDamage (self, self.enemy, autocvar_g_vehicle_racer_blowup_coredamage, autocvar_g_vehicle_racer_blowup_edgedamage, - autocvar_g_vehicle_racer_blowup_radius, world, + autocvar_g_vehicle_racer_blowup_radius, world, world, autocvar_g_vehicle_racer_blowup_forceintensity, DEATH_VH_WAKI_DEATH, world); diff --git a/qcsrc/server/vehicles/raptor.qc b/qcsrc/server/vehicles/raptor.qc index a6ef526499..53e4d35f71 100644 --- a/qcsrc/server/vehicles/raptor.qc +++ b/qcsrc/server/vehicles/raptor.qc @@ -97,7 +97,7 @@ void raptor_bomblet_boom() { RadiusDamage (self, self.realowner, autocvar_g_vehicle_raptor_bomblet_damage, autocvar_g_vehicle_raptor_bomblet_edgedamage, - autocvar_g_vehicle_raptor_bomblet_radius, world, + autocvar_g_vehicle_raptor_bomblet_radius, world, world, autocvar_g_vehicle_raptor_bomblet_force, DEATH_VH_RAPT_BOMB, world); remove(self); } @@ -695,9 +695,10 @@ void raptor_blowup() { self.deadflag = DEAD_DEAD; self.vehicle_exit(VHEF_NORMAL); + RadiusDamage(self, self.enemy, autocvar_g_vehicle_raptor_blowup_coredamage, autocvar_g_vehicle_raptor_blowup_edgedamage, - autocvar_g_vehicle_raptor_blowup_radius, world, + autocvar_g_vehicle_raptor_blowup_radius, world, world, autocvar_g_vehicle_raptor_blowup_forceintensity, DEATH_VH_RAPT_DEATH, world); self.alpha = -1; diff --git a/qcsrc/server/vehicles/spiderbot.qc b/qcsrc/server/vehicles/spiderbot.qc index 1085a904e2..d78ae30a29 100644 --- a/qcsrc/server/vehicles/spiderbot.qc +++ b/qcsrc/server/vehicles/spiderbot.qc @@ -725,7 +725,7 @@ void spiderbot_blowup() RadiusDamage(self, self.enemy, autocvar_g_vehicle_spiderbot_blowup_coredamage, autocvar_g_vehicle_spiderbot_blowup_edgedamage, - autocvar_g_vehicle_spiderbot_blowup_radius, world, + autocvar_g_vehicle_spiderbot_blowup_radius, world, world, autocvar_g_vehicle_spiderbot_blowup_forceintensity, DEATH_VH_SPID_DEATH, world); self.alpha = self.tur_head.alpha = self.gun1.alpha = self.gun2.alpha = -1; diff --git a/qcsrc/server/vehicles/vehicles.qc b/qcsrc/server/vehicles/vehicles.qc index 2ee34b928f..785d39516f 100644 --- a/qcsrc/server/vehicles/vehicles.qc +++ b/qcsrc/server/vehicles/vehicles.qc @@ -3,10 +3,10 @@ float autocvar_g_vehicles_crush_force; float autocvar_g_vehicles_delayspawn; float autocvar_g_vehicles_delayspawn_jitter; -var float autocvar_g_vehicles_nex_damagerate = 0.5; -var float autocvar_g_vehicles_uzi_damagerate = 0.5; +var float autocvar_g_vehicles_vortex_damagerate = 0.5; +var float autocvar_g_vehicles_machinegun_damagerate = 0.5; var float autocvar_g_vehicles_rifle_damagerate = 0.75; -var float autocvar_g_vehicles_minstanex_damagerate = 0.001; +var float autocvar_g_vehicles_vaporizer_damagerate = 0.001; var float autocvar_g_vehicles_tag_damagerate = 5; float autocvar_g_vehicles; @@ -397,7 +397,7 @@ void vehicles_projectile_explode() PROJECTILE_TOUCH; self.event_damage = func_null; - RadiusDamage (self, self.realowner, self.shot_dmg, 0, self.shot_radius, self, self.shot_force, self.totalfrags, other); + RadiusDamage (self, self.realowner, self.shot_dmg, 0, self.shot_radius, self, world, self.shot_force, self.totalfrags, other); remove (self); } @@ -608,6 +608,12 @@ void vehicles_enter() if(self.phase > time) return; + if(other.frozen) + return; + if(other.vehicle) + return; + if(other.deadflag != DEAD_NO) + return; if(teamplay) if(self.team) @@ -908,17 +914,18 @@ void vehicles_damage(entity inflictor, entity attacker, float damage, float deat { self.dmg_time = time; - if(DEATH_ISWEAPON(deathtype, WEP_NEX)) - damage *= autocvar_g_vehicles_nex_damagerate; + // WEAPONTODO + if(DEATH_ISWEAPON(deathtype, WEP_VORTEX)) + damage *= autocvar_g_vehicles_vortex_damagerate; - if(DEATH_ISWEAPON(deathtype, WEP_UZI)) - damage *= autocvar_g_vehicles_uzi_damagerate; + if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN)) + damage *= autocvar_g_vehicles_machinegun_damagerate; if(DEATH_ISWEAPON(deathtype, WEP_RIFLE)) damage *= autocvar_g_vehicles_rifle_damagerate; - if(DEATH_ISWEAPON(deathtype, WEP_MINSTANEX)) - damage *= autocvar_g_vehicles_minstanex_damagerate; + if(DEATH_ISWEAPON(deathtype, WEP_VAPORIZER)) + damage *= autocvar_g_vehicles_vaporizer_damagerate; if(DEATH_ISWEAPON(deathtype, WEP_SEEKER)) damage *= autocvar_g_vehicles_tag_damagerate; diff --git a/qcsrc/server/w_all.qc b/qcsrc/server/w_all.qc deleted file mode 100644 index ac4ef47ed3..0000000000 --- a/qcsrc/server/w_all.qc +++ /dev/null @@ -1,21 +0,0 @@ -// ONLY EVER ADD NEW WEAPONS AT THE END. IF YOU REMOVE ONE, PUT THE LAST ONE ON -// ITS PLACE. THIS IS TO AVOID UNNECESSARY RENUMBERING OF WEAPON IMPULSES. -// IF YOU DISREGARD THIS NOTICE, I'LL KILL YOU WITH THE @!#%'N TUBA -#include "w_laser.qc" -#include "w_shotgun.qc" -#include "w_uzi.qc" -#include "w_grenadelauncher.qc" -#include "w_minelayer.qc" -#include "w_electro.qc" -#include "w_crylink.qc" -#include "w_nex.qc" -#include "w_hagar.qc" -#include "w_rocketlauncher.qc" -#include "w_porto.qc" -#include "w_minstanex.qc" -#include "w_hook.qc" -#include "w_hlac.qc" -#include "w_tuba.qc" -#include "w_rifle.qc" -#include "w_fireball.qc" -#include "w_seeker.qc" diff --git a/qcsrc/server/w_common.qc b/qcsrc/server/w_common.qc deleted file mode 100644 index a7ae4d3266..0000000000 --- a/qcsrc/server/w_common.qc +++ /dev/null @@ -1,376 +0,0 @@ - -void W_GiveWeapon (entity e, float wep) -{ - entity oldself; - - if (!wep) - return; - - e.weapons |= WepSet_FromWeapon(wep); - - oldself = self; - self = e; - - if(IS_PLAYER(other)) - { Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_WEAPON_GOT, wep); } - - self = oldself; -} - -.float railgundistance; -.vector railgunforce; -void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, float deathtype) -{ - vector hitloc, force, endpoint, dir; - entity ent, endent; - float endq3surfaceflags; - float totaldmg; - entity o; - - float length; - vector beampos; - string snd; - entity pseudoprojectile; - float f, ffs; - - pseudoprojectile = world; - - dir = normalize(end - start); - length = vlen(end - start); - force = dir * bforce; - - // go a little bit into the wall because we need to hit this wall later - end = end + dir; - - totaldmg = 0; - - // trace multiple times until we hit a wall, each obstacle will be made - // non-solid so we can hit the next, while doing this we spawn effects and - // note down which entities were hit so we can damage them later - o = self; - while (1) - { - if(self.antilag_debug) - WarpZone_traceline_antilag (self, start, end, FALSE, o, self.antilag_debug); - else - WarpZone_traceline_antilag (self, start, end, FALSE, o, ANTILAG_LATENCY(self)); - if(o && WarpZone_trace_firstzone) - { - o = world; - continue; - } - - if(trace_ent.solid == SOLID_BSP || trace_ent.solid == SOLID_SLIDEBOX) - Damage_DamageInfo(trace_endpos, bdamage, 0, 0, force, deathtype, trace_ent.species, self); - - // if it is world we can't hurt it so stop now - if (trace_ent == world || trace_fraction == 1) - break; - - // make the entity non-solid so we can hit the next one - trace_ent.railgunhit = TRUE; - trace_ent.railgunhitloc = end; - trace_ent.railgunhitsolidbackup = trace_ent.solid; - trace_ent.railgundistance = vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos) - start); - trace_ent.railgunforce = WarpZone_TransformVelocity(WarpZone_trace_transform, force); - - // stop if this is a wall - if (trace_ent.solid == SOLID_BSP) - break; - - // make the entity non-solid - trace_ent.solid = SOLID_NOT; - } - - endpoint = trace_endpos; - endent = trace_ent; - endq3surfaceflags = trace_dphitq3surfaceflags; - - // find all the entities the railgun hit and restore their solid state - ent = findfloat(world, railgunhit, TRUE); - while (ent) - { - // restore their solid type - ent.solid = ent.railgunhitsolidbackup; - ent = findfloat(ent, railgunhit, TRUE); - } - - // spawn a temporary explosion entity for RadiusDamage calls - //explosion = spawn(); - - // Find all non-hit players the beam passed close by - if(deathtype == WEP_MINSTANEX || deathtype == WEP_NEX) - { - FOR_EACH_REALCLIENT(msg_entity) - if(msg_entity != self) - if(!msg_entity.railgunhit) - if(!(IS_SPEC(msg_entity) && msg_entity.enemy == self)) // we use realclient, so spectators can hear the whoosh too - { - // nearest point on the beam - beampos = start + dir * bound(0, (msg_entity.origin - start) * dir, length); - - f = bound(0, 1 - vlen(beampos - msg_entity.origin) / 512, 1); - if(f <= 0) - continue; - - snd = strcat("weapons/nexwhoosh", ftos(floor(random() * 3) + 1), ".wav"); - - if(!pseudoprojectile) - pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume - soundtoat(MSG_ONE, pseudoprojectile, beampos, CH_SHOTS, snd, VOL_BASE * f, ATTEN_NONE); - } - - if(pseudoprojectile) - remove(pseudoprojectile); - } - - // find all the entities the railgun hit and hurt them - ent = findfloat(world, railgunhit, TRUE); - while (ent) - { - // get the details we need to call the damage function - hitloc = ent.railgunhitloc; - - f = ExponentialFalloff(mindist, maxdist, halflifedist, ent.railgundistance); - ffs = ExponentialFalloff(mindist, maxdist, forcehalflifedist, ent.railgundistance); - - if(accuracy_isgooddamage(self.realowner, ent)) - totaldmg += bdamage * f; - - // apply the damage - if (ent.takedamage) - Damage (ent, self, self, bdamage * f, deathtype, hitloc, ent.railgunforce * ffs); - - // create a small explosion to throw gibs around (if applicable) - //setorigin (explosion, hitloc); - //RadiusDamage (explosion, self, 10, 0, 50, world, 300, deathtype); - - ent.railgunhitloc = '0 0 0'; - ent.railgunhitsolidbackup = SOLID_NOT; - ent.railgunhit = FALSE; - ent.railgundistance = 0; - - // advance to the next entity - ent = findfloat(ent, railgunhit, TRUE); - } - - // calculate hits and fired shots for hitscan - accuracy_add(self, self.weapon, 0, min(bdamage, totaldmg)); - - trace_endpos = endpoint; - trace_ent = endent; - trace_dphitq3surfaceflags = endq3surfaceflags; -} - -float fireBullet_trace_callback_eff; -entity fireBullet_last_hit; -void fireBullet_trace_callback(vector start, vector hit, vector end) -{ - if(vlen(hit - start) > 16) - trailparticles(world, fireBullet_trace_callback_eff, start, hit); - WarpZone_trace_forent = world; - fireBullet_last_hit = world; -} - -void fireBullet(vector start, vector dir, float spread, float max_solid_penetration, float damage, float force, float dtype, float tracereffects) -{ - // TODO antilag takeback - vector end; - - dir = normalize(dir + randomvec() * spread); - end = start + dir * MAX_SHOT_DISTANCE; - - entity pl; - fireBullet_last_hit = world; - float solid_penetration_left = 1; - float total_damage = 0; - - if(tracereffects & EF_RED) - fireBullet_trace_callback_eff = particleeffectnum("tr_rifle"); - else if(tracereffects & EF_BLUE) - fireBullet_trace_callback_eff = particleeffectnum("tr_rifle_weak"); - else - fireBullet_trace_callback_eff = particleeffectnum("tr_bullet"); - - float lag = ANTILAG_LATENCY(self); - if(lag < 0.001) - lag = 0; - if (!IS_REAL_CLIENT(self)) - lag = 0; - if(autocvar_g_antilag == 0 || self.cvar_cl_noantilag) - lag = 0; // only do hitscan, but no antilag - if(lag) - { - FOR_EACH_PLAYER(pl) - if(pl != self) - antilag_takeback(pl, time - lag); - FOR_EACH_MONSTER(pl) - antilag_takeback(pl, time - lag); - } - - WarpZone_trace_forent = self; - - for (;;) - { - // TODO also show effect while tracing - WarpZone_TraceBox_ThroughZone(start, '0 0 0', '0 0 0', end, FALSE, WarpZone_trace_forent, world, fireBullet_trace_callback); - dir = WarpZone_TransformVelocity(WarpZone_trace_transform, dir); - end = WarpZone_TransformOrigin(WarpZone_trace_transform, end); - start = trace_endpos; - entity hit = trace_ent; - - // When hitting sky, stop. - if (pointcontents(start) == CONTENT_SKY) - break; - - if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) - break; - - // if we hit "weapclip", bail out - // - // rationale of this check: - // - // any shader that is solid, nodraw AND trans is meant to clip weapon - // shots and players, but has no other effect! - // - // if it is not trans, it is caulk and should not have this side effect - // - // matching shaders: - // common/weapclip (intended) - // common/noimpact (is supposed to eat projectiles, but is erased anyway) - float is_weapclip = 0; - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW) - if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)) - if (!(trace_dphitcontents & DPCONTENTS_OPAQUE)) - is_weapclip = 1; - - if(!hit || hit.solid == SOLID_BSP || hit.solid == SOLID_SLIDEBOX) - Damage_DamageInfo(start, damage * solid_penetration_left, 0, 0, max(1, force) * dir * solid_penetration_left, dtype, hit.species, self); - - if (hit && hit != WarpZone_trace_forent && hit != fireBullet_last_hit) // Avoid self-damage (except after going through a warp); avoid hitting the same entity twice (engine bug). - { - fireBullet_last_hit = hit; - yoda = 0; - float g = accuracy_isgooddamage(self, hit); - Damage(hit, self, self, damage * solid_penetration_left, dtype, start, force * dir * solid_penetration_left); - // calculate hits for ballistic weapons - if(g) - { - // do not exceed 100% - float added_damage = min(damage - total_damage, damage * solid_penetration_left); - total_damage += damage * solid_penetration_left; - accuracy_add(self, self.weapon, 0, added_damage); - } - } - - if (is_weapclip) - break; - - // go through solid! - // outside the world? forget it - if(start_x > world.maxs_x || start_y > world.maxs_y || start_z > world.maxs_z || start_x < world.mins_x || start_y < world.mins_y || start_z < world.mins_z) - break; - - float maxdist; - if(max_solid_penetration < 0) - break; - else if(hit.ballistics_density < -1) - break; // -2: no solid penetration, ever - else if(hit.ballistics_density < 0) - maxdist = vlen(hit.maxs - hit.mins) + 1; // -1: infinite travel distance - else if(hit.ballistics_density == 0) - maxdist = max_solid_penetration * solid_penetration_left; - else - maxdist = max_solid_penetration * solid_penetration_left * hit.ballistics_density; - - if(maxdist <= autocvar_g_ballistics_mindistance) - break; - - // move the entity along its velocity until it's out of solid, then let it resume - // The previously hit entity is ignored here! - traceline_inverted (start, start + dir * maxdist, MOVE_NORMAL, WarpZone_trace_forent, TRUE, hit); - if(trace_fraction == 1) // 1: we never got out of solid - break; - - float dist_taken = max(autocvar_g_ballistics_mindistance, vlen(trace_endpos - start)); - solid_penetration_left *= (dist_taken / maxdist); - - // Only show effect when going through a player (invisible otherwise) - if (hit && (hit.solid != SOLID_BSP)) - if(vlen(trace_endpos - start) > 4) - trailparticles(self, fireBullet_trace_callback_eff, start, trace_endpos); - - start = trace_endpos; - - if(hit.solid == SOLID_BSP) - Damage_DamageInfo(start, 0, 0, 0, max(1, force) * normalize(dir) * -solid_penetration_left, dtype, 0, self); - } - - if(lag) - { - FOR_EACH_PLAYER(pl) - if(pl != self) - antilag_restore(pl); - FOR_EACH_MONSTER(pl) - antilag_restore(pl); - } -} - -float W_CheckProjectileDamage(entity inflictor, entity projowner, float deathtype, float exception) -{ - float is_from_contents = (deathtype == DEATH_SLIME || deathtype == DEATH_LAVA); - float is_from_owner = (inflictor == projowner); - float is_from_exception = (exception != -1); - - //dprint(strcat("W_CheckProjectileDamage: from_contents ", ftos(is_from_contents), " : from_owner ", ftos(is_from_owner), " : exception ", strcat(ftos(is_from_exception), " (", ftos(exception), "). \n"))); - - if(autocvar_g_projectiles_damage <= -2) - { - return FALSE; // no damage to projectiles at all, not even with the exceptions - } - else if(autocvar_g_projectiles_damage == -1) - { - if(is_from_exception) - return (exception); // if exception is detected, allow it to override - else - return FALSE; // otherwise, no other damage is allowed - } - else if(autocvar_g_projectiles_damage == 0) - { - if(is_from_exception) - return (exception); // if exception is detected, allow it to override - else if (!is_from_contents) - return FALSE; // otherwise, only allow damage from contents - } - else if(autocvar_g_projectiles_damage == 1) - { - if(is_from_exception) - return (exception); // if exception is detected, allow it to override - else if (!(is_from_contents || is_from_owner)) - return FALSE; // otherwise, only allow self damage and damage from contents - } - else if(autocvar_g_projectiles_damage == 2) // allow any damage, but override for exceptions - { - if(is_from_exception) - return (exception); // if exception is detected, allow it to override - } - - return TRUE; // if none of these return, then allow damage anyway. -} - -void W_PrepareExplosionByDamage(entity attacker, void() explode) -{ - self.takedamage = DAMAGE_NO; - self.event_damage = func_null; - - if(IS_CLIENT(attacker) && !autocvar_g_projectiles_keep_owner) - { - self.owner = attacker; - self.realowner = attacker; - } - - // do not explode NOW but in the NEXT FRAME! - // because recursive calls to RadiusDamage are not allowed - self.nextthink = time; - self.think = explode; -} diff --git a/qcsrc/server/w_crylink.qc b/qcsrc/server/w_crylink.qc deleted file mode 100644 index f7ad7583d1..0000000000 --- a/qcsrc/server/w_crylink.qc +++ /dev/null @@ -1,727 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ CRYLINK, -/* function */ w_crylink, -/* ammotype */ IT_CELLS, -/* impulse */ 6, -/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH, -/* rating */ BOT_PICKUP_RATING_MID, -/* model */ "crylink", -/* shortname */ "crylink", -/* fullname */ _("Crylink") -); -#else -#ifdef SVQC -.float gravity; -.float crylink_waitrelease; -.entity crylink_lastgroup; - -.entity queuenext; -.entity queueprev; - -void W_Crylink_CheckLinks(entity e) -{ - float i; - entity p; - - if(e == world) - error("W_Crylink_CheckLinks: entity is world"); - if(e.classname != "spike" || wasfreed(e)) - error(sprintf("W_Crylink_CheckLinks: entity is not a spike but a %s (freed: %d)", e.classname, wasfreed(e))); - - p = e; - for(i = 0; i < 1000; ++i) - { - if(p.queuenext.queueprev != p || p.queueprev.queuenext != p) - error("W_Crylink_CheckLinks: queue is inconsistent"); - p = p.queuenext; - if(p == e) - break; - } - if(i >= 1000) - error("W_Crylink_CheckLinks: infinite chain"); -} - -void W_Crylink_Dequeue_Raw(entity own, entity prev, entity me, entity next) -{ - W_Crylink_CheckLinks(next); - if(me == own.crylink_lastgroup) - own.crylink_lastgroup = ((me == next) ? world : next); - prev.queuenext = next; - next.queueprev = prev; - me.classname = "spike_oktoremove"; - if(me != next) - W_Crylink_CheckLinks(next); -} - -void W_Crylink_Dequeue(entity e) -{ - W_Crylink_Dequeue_Raw(e.realowner, e.queueprev, e, e.queuenext); -} - -void W_Crylink_Reset(void) -{ - W_Crylink_Dequeue(self); - remove(self); -} - -// force projectile to explode -void W_Crylink_LinkExplode (entity e, entity e2) -{ - float a; - - if(e == e2) - return; - - a = bound(0, 1 - (time - e.fade_time) * e.fade_rate, 1); - - if(e == e.realowner.crylink_lastgroup) - e.realowner.crylink_lastgroup = world; - - if(e.projectiledeathtype & HITTYPE_SECONDARY) - RadiusDamage (e, e.realowner, autocvar_g_balance_crylink_secondary_damage * a, autocvar_g_balance_crylink_secondary_edgedamage * a, autocvar_g_balance_crylink_secondary_radius, world, autocvar_g_balance_crylink_secondary_force * a, e.projectiledeathtype, other); - else - RadiusDamage (e, e.realowner, autocvar_g_balance_crylink_primary_damage * a, autocvar_g_balance_crylink_primary_edgedamage * a, autocvar_g_balance_crylink_primary_radius, world, autocvar_g_balance_crylink_primary_force * a, e.projectiledeathtype, other); - - W_Crylink_LinkExplode(e.queuenext, e2); - - e.classname = "spike_oktoremove"; - remove (e); -} - -// adjust towards center -// returns the origin where they will meet... and the time till the meeting is -// stored in w_crylink_linkjoin_time. -// could possibly network this origin and time, and display a special particle -// effect when projectiles meet there :P -// jspeed: joining speed (calculate this as join spread * initial speed) -float w_crylink_linkjoin_time; -vector W_Crylink_LinkJoin(entity e, float jspeed) -{ - vector avg_origin, avg_velocity; - vector targ_origin; - float avg_dist, n; - entity p; - - // FIXME remove this debug code - W_Crylink_CheckLinks(e); - - w_crylink_linkjoin_time = 0; - - avg_origin = e.origin; - avg_velocity = e.velocity; - n = 1; - for(p = e; (p = p.queuenext) != e; ) - { - avg_origin += WarpZone_RefSys_TransformOrigin(p, e, p.origin); - avg_velocity += WarpZone_RefSys_TransformVelocity(p, e, p.velocity); - ++n; - } - avg_origin *= (1.0 / n); - avg_velocity *= (1.0 / n); - - if(n < 2) - return avg_origin; // nothing to do - - // yes, mathematically we can do this in ONE step, but beware of 32bit floats... - avg_dist = pow(vlen(e.origin - avg_origin), 2); - for(p = e; (p = p.queuenext) != e; ) - avg_dist += pow(vlen(WarpZone_RefSys_TransformOrigin(p, e, p.origin) - avg_origin), 2); - avg_dist *= (1.0 / n); - avg_dist = sqrt(avg_dist); - - if(avg_dist == 0) - return avg_origin; // no change needed - - if(jspeed == 0) - { - e.velocity = avg_velocity; - UpdateCSQCProjectile(e); - for(p = e; (p = p.queuenext) != e; ) - { - p.velocity = WarpZone_RefSys_TransformVelocity(e, p, avg_velocity); - UpdateCSQCProjectile(p); - } - targ_origin = avg_origin + 1000000000 * normalize(avg_velocity); // HUUUUUUGE - } - else - { - w_crylink_linkjoin_time = avg_dist / jspeed; - targ_origin = avg_origin + w_crylink_linkjoin_time * avg_velocity; - - e.velocity = (targ_origin - e.origin) * (1.0 / w_crylink_linkjoin_time); - UpdateCSQCProjectile(e); - for(p = e; (p = p.queuenext) != e; ) - { - p.velocity = WarpZone_RefSys_TransformVelocity(e, p, (targ_origin - WarpZone_RefSys_TransformOrigin(p, e, p.origin)) * (1.0 / w_crylink_linkjoin_time)); - UpdateCSQCProjectile(p); - } - - // analysis: - // jspeed -> +infinity: - // w_crylink_linkjoin_time -> +0 - // targ_origin -> avg_origin - // p->velocity -> HUEG towards center - // jspeed -> 0: - // w_crylink_linkjoin_time -> +/- infinity - // targ_origin -> avg_velocity * +/- infinity - // p->velocity -> avg_velocity - // jspeed -> -infinity: - // w_crylink_linkjoin_time -> -0 - // targ_origin -> avg_origin - // p->velocity -> HUEG away from center - } - - W_Crylink_CheckLinks(e); - - return targ_origin; -} - -void W_Crylink_LinkJoinEffect_Think() -{ - // is there at least 2 projectiles very close? - entity e, p; - float n; - e = self.owner.crylink_lastgroup; - n = 0; - if(e) - { - if(vlen(e.origin - self.origin) < vlen(e.velocity) * frametime) - ++n; - for(p = e; (p = p.queuenext) != e; ) - { - if(vlen(p.origin - self.origin) < vlen(p.velocity) * frametime) - ++n; - } - if(n >= 2) - { - if(e.projectiledeathtype & HITTYPE_SECONDARY) - { - if(autocvar_g_balance_crylink_secondary_joinexplode) - { - n = n / autocvar_g_balance_crylink_secondary_shots; - RadiusDamage (e, e.realowner, autocvar_g_balance_crylink_secondary_joinexplode_damage * n, - autocvar_g_balance_crylink_secondary_joinexplode_edgedamage * n, - autocvar_g_balance_crylink_secondary_joinexplode_radius * n, e.realowner, - autocvar_g_balance_crylink_secondary_joinexplode_force * n, e.projectiledeathtype, other); - - pointparticles(particleeffectnum("crylink_joinexplode"), self.origin, '0 0 0', n); - } - } - else - { - if(autocvar_g_balance_crylink_primary_joinexplode) - { - n = n / autocvar_g_balance_crylink_primary_shots; - RadiusDamage (e, e.realowner, autocvar_g_balance_crylink_primary_joinexplode_damage * n, - autocvar_g_balance_crylink_primary_joinexplode_edgedamage * n, - autocvar_g_balance_crylink_primary_joinexplode_radius * n, e.realowner, - autocvar_g_balance_crylink_primary_joinexplode_force * n, e.projectiledeathtype, other); - - pointparticles(particleeffectnum("crylink_joinexplode"), self.origin, '0 0 0', n); - } - } - } - } - remove(self); -} - -float W_Crylink_Touch_WouldHitFriendly(entity projectile, float rad) -{ - entity head = WarpZone_FindRadius((projectile.origin + (projectile.mins + projectile.maxs) * 0.5), rad + MAX_DAMAGEEXTRARADIUS, FALSE); - float hit_friendly = 0; - float hit_enemy = 0; - - while(head) - { - if((head.takedamage != DAMAGE_NO) && (head.deadflag == DEAD_NO)) - { - if(SAME_TEAM(head, projectile.realowner)) - ++hit_friendly; - else - ++hit_enemy; - } - - head = head.chain; - } - - return (hit_enemy ? FALSE : hit_friendly); -} - -// NO bounce protection, as bounces are limited! -void W_Crylink_Touch (void) -{ - float finalhit; - float f; - PROJECTILE_TOUCH; - - float a; - a = bound(0, 1 - (time - self.fade_time) * self.fade_rate, 1); - - finalhit = ((self.cnt <= 0) || (other.takedamage != DAMAGE_NO)); - if(finalhit) - f = 1; - else - f = autocvar_g_balance_crylink_primary_bouncedamagefactor; - if(a) - f *= a; - - float totaldamage = RadiusDamage(self, self.realowner, autocvar_g_balance_crylink_primary_damage * f, autocvar_g_balance_crylink_primary_edgedamage * f, autocvar_g_balance_crylink_primary_radius, world, autocvar_g_balance_crylink_primary_force * f, self.projectiledeathtype, other); - - if(totaldamage && ((autocvar_g_balance_crylink_primary_linkexplode == 2) || ((autocvar_g_balance_crylink_primary_linkexplode == 1) && !W_Crylink_Touch_WouldHitFriendly(self, autocvar_g_balance_crylink_primary_radius)))) - { - if(self == self.realowner.crylink_lastgroup) - self.realowner.crylink_lastgroup = world; - W_Crylink_LinkExplode(self.queuenext, self); - self.classname = "spike_oktoremove"; - remove (self); - return; - } - else if(finalhit) - { - // just unlink - W_Crylink_Dequeue(self); - remove(self); - return; - } - self.cnt = self.cnt - 1; - self.angles = vectoangles(self.velocity); - self.owner = world; - self.projectiledeathtype |= HITTYPE_BOUNCE; - // commented out as it causes a little hitch... - //if(proj.cnt == 0) - // CSQCProjectile(proj, TRUE, PROJECTILE_CRYLINK, TRUE); -} - -void W_Crylink_Touch2 (void) -{ - float finalhit; - float f; - PROJECTILE_TOUCH; - - float a; - a = bound(0, 1 - (time - self.fade_time) * self.fade_rate, 1); - - finalhit = ((self.cnt <= 0) || (other.takedamage != DAMAGE_NO)); - if(finalhit) - f = 1; - else - f = autocvar_g_balance_crylink_secondary_bouncedamagefactor; - if(a) - f *= a; - - float totaldamage = RadiusDamage(self, self.realowner, autocvar_g_balance_crylink_secondary_damage * f, autocvar_g_balance_crylink_secondary_edgedamage * f, autocvar_g_balance_crylink_secondary_radius, world, autocvar_g_balance_crylink_secondary_force * f, self.projectiledeathtype, other); - - if(totaldamage && ((autocvar_g_balance_crylink_secondary_linkexplode == 2) || ((autocvar_g_balance_crylink_secondary_linkexplode == 1) && !W_Crylink_Touch_WouldHitFriendly(self, autocvar_g_balance_crylink_secondary_radius)))) - { - if(self == self.realowner.crylink_lastgroup) - self.realowner.crylink_lastgroup = world; - W_Crylink_LinkExplode(self.queuenext, self); - self.classname = "spike_oktoremove"; - remove (self); - return; - } - else if(finalhit) - { - // just unlink - W_Crylink_Dequeue(self); - remove(self); - return; - } - self.cnt = self.cnt - 1; - self.angles = vectoangles(self.velocity); - self.owner = world; - self.projectiledeathtype |= HITTYPE_BOUNCE; - // commented out as it causes a little hitch... - //if(proj.cnt == 0) - // CSQCProjectile(proj, TRUE, PROJECTILE_CRYLINK, TRUE); -} - -void W_Crylink_Fadethink (void) -{ - W_Crylink_Dequeue(self); - remove(self); -} - -void W_Crylink_Attack (void) -{ - float counter, shots; - entity proj, prevproj, firstproj; - vector s; - vector forward, right, up; - float maxdmg; - - W_DecreaseAmmo(ammo_cells, autocvar_g_balance_crylink_primary_ammo, autocvar_g_balance_crylink_reload_ammo); - - maxdmg = autocvar_g_balance_crylink_primary_damage*autocvar_g_balance_crylink_primary_shots; - maxdmg *= 1 + autocvar_g_balance_crylink_primary_bouncedamagefactor * autocvar_g_balance_crylink_primary_bounces; - if(autocvar_g_balance_crylink_primary_joinexplode) - maxdmg += autocvar_g_balance_crylink_primary_joinexplode_damage; - - W_SetupShot (self, FALSE, 2, "weapons/crylink_fire.wav", CH_WEAPON_A, maxdmg); - forward = v_forward; - right = v_right; - up = v_up; - - shots = autocvar_g_balance_crylink_primary_shots; - pointparticles(particleeffectnum("crylink_muzzleflash"), w_shotorg, w_shotdir * 1000, shots); - proj = prevproj = firstproj = world; - for(counter = 0; counter < shots; ++counter) - { - proj = spawn (); - proj.reset = W_Crylink_Reset; - proj.realowner = proj.owner = self; - proj.classname = "spike"; - proj.bot_dodge = TRUE; - proj.bot_dodgerating = autocvar_g_balance_crylink_primary_damage; - if(shots == 1) { - proj.queuenext = proj; - proj.queueprev = proj; - } - else if(counter == 0) { // first projectile, store in firstproj for now - firstproj = proj; - } - else if(counter == shots - 1) { // last projectile, link up with first projectile - prevproj.queuenext = proj; - firstproj.queueprev = proj; - proj.queuenext = firstproj; - proj.queueprev = prevproj; - } - else { // else link up with previous projectile - prevproj.queuenext = proj; - proj.queueprev = prevproj; - } - - prevproj = proj; - - proj.movetype = MOVETYPE_BOUNCEMISSILE; - PROJECTILE_MAKETRIGGER(proj); - proj.projectiledeathtype = WEP_CRYLINK; - //proj.gravity = 0.001; - - setorigin (proj, w_shotorg); - setsize(proj, '0 0 0', '0 0 0'); - - - s = '0 0 0'; - if (counter == 0) - s = '0 0 0'; - else - { - makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1))); - s_y = v_forward_x; - s_z = v_forward_y; - } - s = s * autocvar_g_balance_crylink_primary_spread * g_weaponspreadfactor; - W_SetupProjectileVelocityEx(proj, w_shotdir + right * s_y + up * s_z, v_up, autocvar_g_balance_crylink_primary_speed, 0, 0, 0, FALSE); - proj.touch = W_Crylink_Touch; - - proj.think = W_Crylink_Fadethink; - if(counter == 0) - { - proj.fade_time = time + autocvar_g_balance_crylink_primary_middle_lifetime; - proj.fade_rate = 1 / autocvar_g_balance_crylink_primary_middle_fadetime; - proj.nextthink = time + autocvar_g_balance_crylink_primary_middle_lifetime + autocvar_g_balance_crylink_primary_middle_fadetime; - } - else - { - proj.fade_time = time + autocvar_g_balance_crylink_primary_other_lifetime; - proj.fade_rate = 1 / autocvar_g_balance_crylink_primary_other_fadetime; - proj.nextthink = time + autocvar_g_balance_crylink_primary_other_lifetime + autocvar_g_balance_crylink_primary_other_fadetime; - } - proj.teleport_time = time + autocvar_g_balance_crylink_primary_joindelay; - proj.cnt = autocvar_g_balance_crylink_primary_bounces; - //proj.scale = 1 + 1 * proj.cnt; - - proj.angles = vectoangles (proj.velocity); - - //proj.glow_size = 20; - - proj.flags = FL_PROJECTILE; - proj.missile_flags = MIF_SPLASH; - - CSQCProjectile(proj, TRUE, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), TRUE); - - other = proj; MUTATOR_CALLHOOK(EditProjectile); - } - if(autocvar_g_balance_crylink_primary_joinspread != 0) - { - self.crylink_lastgroup = proj; - W_Crylink_CheckLinks(proj); - self.crylink_waitrelease = 1; - } -} - -void W_Crylink_Attack2 (void) -{ - float counter, shots; - entity proj, prevproj, firstproj; - vector s; - vector forward, right, up; - float maxdmg; - - W_DecreaseAmmo(ammo_cells, autocvar_g_balance_crylink_secondary_ammo, autocvar_g_balance_crylink_reload_ammo); - - maxdmg = autocvar_g_balance_crylink_secondary_damage*autocvar_g_balance_crylink_secondary_shots; - maxdmg *= 1 + autocvar_g_balance_crylink_secondary_bouncedamagefactor * autocvar_g_balance_crylink_secondary_bounces; - if(autocvar_g_balance_crylink_secondary_joinexplode) - maxdmg += autocvar_g_balance_crylink_secondary_joinexplode_damage; - - W_SetupShot (self, FALSE, 2, "weapons/crylink_fire2.wav", CH_WEAPON_A, maxdmg); - forward = v_forward; - right = v_right; - up = v_up; - - shots = autocvar_g_balance_crylink_secondary_shots; - pointparticles(particleeffectnum("crylink_muzzleflash"), w_shotorg, w_shotdir * 1000, shots); - proj = prevproj = firstproj = world; - for(counter = 0; counter < shots; ++counter) - { - proj = spawn (); - proj.reset = W_Crylink_Reset; - proj.realowner = proj.owner = self; - proj.classname = "spike"; - proj.bot_dodge = TRUE; - proj.bot_dodgerating = autocvar_g_balance_crylink_secondary_damage; - if(shots == 1) { - proj.queuenext = proj; - proj.queueprev = proj; - } - else if(counter == 0) { // first projectile, store in firstproj for now - firstproj = proj; - } - else if(counter == shots - 1) { // last projectile, link up with first projectile - prevproj.queuenext = proj; - firstproj.queueprev = proj; - proj.queuenext = firstproj; - proj.queueprev = prevproj; - } - else { // else link up with previous projectile - prevproj.queuenext = proj; - proj.queueprev = prevproj; - } - - prevproj = proj; - - proj.movetype = MOVETYPE_BOUNCEMISSILE; - PROJECTILE_MAKETRIGGER(proj); - proj.projectiledeathtype = WEP_CRYLINK | HITTYPE_SECONDARY; - //proj.gravity = 0.001; - - setorigin (proj, w_shotorg); - setsize(proj, '0 0 0', '0 0 0'); - - if(autocvar_g_balance_crylink_secondary_spreadtype == 1) - { - s = '0 0 0'; - if (counter == 0) - s = '0 0 0'; - else - { - makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1))); - s_y = v_forward_x; - s_z = v_forward_y; - } - s = s * autocvar_g_balance_crylink_secondary_spread * g_weaponspreadfactor; - s = w_shotdir + right * s_y + up * s_z; - } - else - { - s = (w_shotdir + (((counter + 0.5) / shots) * 2 - 1) * v_right * autocvar_g_balance_crylink_secondary_spread * g_weaponspreadfactor); - } - - W_SetupProjectileVelocityEx(proj, s, v_up, autocvar_g_balance_crylink_secondary_speed, 0, 0, 0, FALSE); - proj.touch = W_Crylink_Touch2; - proj.think = W_Crylink_Fadethink; - if(counter == (shots - 1) / 2) - { - proj.fade_time = time + autocvar_g_balance_crylink_secondary_middle_lifetime; - proj.fade_rate = 1 / autocvar_g_balance_crylink_secondary_middle_fadetime; - proj.nextthink = time + autocvar_g_balance_crylink_secondary_middle_lifetime + autocvar_g_balance_crylink_secondary_middle_fadetime; - } - else - { - proj.fade_time = time + autocvar_g_balance_crylink_secondary_line_lifetime; - proj.fade_rate = 1 / autocvar_g_balance_crylink_secondary_line_fadetime; - proj.nextthink = time + autocvar_g_balance_crylink_secondary_line_lifetime + autocvar_g_balance_crylink_secondary_line_fadetime; - } - proj.teleport_time = time + autocvar_g_balance_crylink_secondary_joindelay; - proj.cnt = autocvar_g_balance_crylink_secondary_bounces; - //proj.scale = 1 + 1 * proj.cnt; - - proj.angles = vectoangles (proj.velocity); - - //proj.glow_size = 20; - - proj.flags = FL_PROJECTILE; - proj.missile_flags = MIF_SPLASH; - - CSQCProjectile(proj, TRUE, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), TRUE); - - other = proj; MUTATOR_CALLHOOK(EditProjectile); - } - if(autocvar_g_balance_crylink_secondary_joinspread != 0) - { - self.crylink_lastgroup = proj; - W_Crylink_CheckLinks(proj); - self.crylink_waitrelease = 2; - } -} - -void spawnfunc_weapon_crylink (void) -{ - weapon_defaultspawnfunc(WEP_CRYLINK); -} - -float w_crylink(float req) -{ - float ammo_amount; - if (req == WR_AIM) - { - if (random() < 0.10) - self.BUTTON_ATCK = bot_aim(autocvar_g_balance_crylink_primary_speed, 0, autocvar_g_balance_crylink_primary_middle_lifetime, FALSE); - else - self.BUTTON_ATCK2 = bot_aim(autocvar_g_balance_crylink_secondary_speed, 0, autocvar_g_balance_crylink_secondary_middle_lifetime, FALSE); - } - else if (req == WR_THINK) - { - if(autocvar_g_balance_crylink_reload_ammo && self.clip_load < min(autocvar_g_balance_crylink_primary_ammo, autocvar_g_balance_crylink_secondary_ammo)) // forced reload - weapon_action(self.weapon, WR_RELOAD); - - if (self.BUTTON_ATCK) - { - if (self.crylink_waitrelease != 1) - if (weapon_prepareattack(0, autocvar_g_balance_crylink_primary_refire)) - { - W_Crylink_Attack(); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_crylink_primary_animtime, w_ready); - } - } - - if(self.BUTTON_ATCK2 && autocvar_g_balance_crylink_secondary) - { - if (self.crylink_waitrelease != 2) - if (weapon_prepareattack(1, autocvar_g_balance_crylink_secondary_refire)) - { - W_Crylink_Attack2(); - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_crylink_secondary_animtime, w_ready); - } - } - - if ((self.crylink_waitrelease == 1 && !self.BUTTON_ATCK) || (self.crylink_waitrelease == 2 && !self.BUTTON_ATCK2)) - { - if (!self.crylink_lastgroup || time > self.crylink_lastgroup.teleport_time) - { - // fired and released now! - if(self.crylink_lastgroup) - { - vector pos; - entity linkjoineffect; - - if(self.crylink_waitrelease == 1) - { - pos = W_Crylink_LinkJoin(self.crylink_lastgroup, autocvar_g_balance_crylink_primary_joinspread * autocvar_g_balance_crylink_primary_speed); - - } - else - { - pos = W_Crylink_LinkJoin(self.crylink_lastgroup, autocvar_g_balance_crylink_secondary_joinspread * autocvar_g_balance_crylink_secondary_speed); - } - - linkjoineffect = spawn(); - linkjoineffect.think = W_Crylink_LinkJoinEffect_Think; - linkjoineffect.classname = "linkjoineffect"; - linkjoineffect.nextthink = time + w_crylink_linkjoin_time; - linkjoineffect.owner = self; - setorigin(linkjoineffect, pos); - } - self.crylink_waitrelease = 0; - if(!w_crylink(WR_CHECKAMMO1) && !w_crylink(WR_CHECKAMMO2)) - if (!(self.items & IT_UNLIMITED_WEAPON_AMMO)) - { - // ran out of ammo! - self.cnt = WEP_CRYLINK; - self.switchweapon = w_getbestweapon(self); - } - } - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/weapons/g_crylink.md3"); - precache_model ("models/weapons/v_crylink.md3"); - precache_model ("models/weapons/h_crylink.iqm"); - precache_sound ("weapons/crylink_fire.wav"); - precache_sound ("weapons/crylink_fire2.wav"); - precache_sound ("weapons/crylink_linkjoin.wav"); - //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_CRYLINK); - self.current_ammo = ammo_cells; - } - else if (req == WR_CHECKAMMO1) - { - // don't "run out of ammo" and switch weapons while waiting for release - if(self.crylink_lastgroup && self.crylink_waitrelease) - return TRUE; - - ammo_amount = self.ammo_cells >= autocvar_g_balance_crylink_primary_ammo; - ammo_amount += self.(weapon_load[WEP_CRYLINK]) >= autocvar_g_balance_crylink_primary_ammo; - return ammo_amount; - } - else if (req == WR_CHECKAMMO2) - { - // don't "run out of ammo" and switch weapons while waiting for release - if(self.crylink_lastgroup && self.crylink_waitrelease) - return TRUE; - - ammo_amount = self.ammo_cells >= autocvar_g_balance_crylink_secondary_ammo; - ammo_amount += self.(weapon_load[WEP_CRYLINK]) >= autocvar_g_balance_crylink_secondary_ammo; - return ammo_amount; - } - else if (req == WR_RELOAD) - { - W_Reload(min(autocvar_g_balance_crylink_primary_ammo, autocvar_g_balance_crylink_secondary_ammo), autocvar_g_balance_crylink_reload_ammo, autocvar_g_balance_crylink_reload_time, "weapons/reload.wav"); - } - else if (req == WR_SUICIDEMESSAGE) - { - return WEAPON_CRYLINK_SUICIDE; - } - else if (req == WR_KILLMESSAGE) - { - return WEAPON_CRYLINK_MURDER; - } - return TRUE; -} -#endif -#ifdef CSQC -float w_crylink(float req) -{ - if(req == WR_IMPACTEFFECT) - { - vector org2; - org2 = w_org + w_backoff * 2; - if(w_deathtype & HITTYPE_SECONDARY) - { - pointparticles(particleeffectnum("crylink_impact"), org2, '0 0 0', 1); - if(!w_issilent) - sound(self, CH_SHOTS, "weapons/crylink_impact2.wav", VOL_BASE, ATTEN_NORM); - } - else - { - pointparticles(particleeffectnum("crylink_impactbig"), org2, '0 0 0', 1); - if(!w_issilent) - sound(self, CH_SHOTS, "weapons/crylink_impact.wav", VOL_BASE, ATTEN_NORM); - } - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/crylink_impact2.wav"); - precache_sound("weapons/crylink_impact.wav"); - } - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_electro.qc b/qcsrc/server/w_electro.qc deleted file mode 100644 index ea2cf8bf05..0000000000 --- a/qcsrc/server/w_electro.qc +++ /dev/null @@ -1,618 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ ELECTRO, -/* function */ w_electro, -/* ammotype */ IT_CELLS, -/* impulse */ 5, -/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH, -/* rating */ BOT_PICKUP_RATING_MID, -/* model */ "electro", -/* shortname */ "electro", -/* fullname */ _("Electro") -); -#else -#ifdef SVQC -.float electro_count; -.float electro_secondarytime; - -void W_Plasma_Explode_Combo (void); - -void W_Plasma_TriggerCombo(vector org, float rad, entity own) -{ - entity e; - e = WarpZone_FindRadius(org, rad, TRUE); - while (e) - { - if (e.classname == "plasma") - { - // change owner to whoever caused the combo explosion - e.realowner = own; - e.takedamage = DAMAGE_NO; - e.classname = "plasma_chain"; - e.think = W_Plasma_Explode_Combo; - e.nextthink = time + vlen(e.WarpZone_findradius_dist) / autocvar_g_balance_electro_combo_speed; // delay combo chains, looks cooler - } - e = e.chain; - } -} - -void W_Plasma_Explode (void) -{ - if(other.takedamage == DAMAGE_AIM) - if(IS_PLAYER(other)) - if(DIFF_TEAM(self.realowner, other)) - if(other.deadflag == DEAD_NO) - if(IsFlying(other)) - Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_ELECTROBITCH); - - self.event_damage = func_null; - self.takedamage = DAMAGE_NO; - if (self.movetype == MOVETYPE_BOUNCE) - { - RadiusDamage (self, self.realowner, autocvar_g_balance_electro_secondary_damage, autocvar_g_balance_electro_secondary_edgedamage, autocvar_g_balance_electro_secondary_radius, world, autocvar_g_balance_electro_secondary_force, self.projectiledeathtype, other); - } - else - { - W_Plasma_TriggerCombo(self.origin, autocvar_g_balance_electro_primary_comboradius, self.realowner); - RadiusDamage (self, self.realowner, autocvar_g_balance_electro_primary_damage, autocvar_g_balance_electro_primary_edgedamage, autocvar_g_balance_electro_primary_radius, world, autocvar_g_balance_electro_primary_force, self.projectiledeathtype, other); - } - - remove (self); -} - -void W_Plasma_Explode_Combo (void) -{ - W_Plasma_TriggerCombo(self.origin, autocvar_g_balance_electro_combo_comboradius, self.realowner); - - self.event_damage = func_null; - RadiusDamage (self, self.realowner, autocvar_g_balance_electro_combo_damage, autocvar_g_balance_electro_combo_edgedamage, autocvar_g_balance_electro_combo_radius, world, autocvar_g_balance_electro_combo_force, WEP_ELECTRO | HITTYPE_BOUNCE, world); // use THIS type for a combo because primary can't bounce - remove (self); -} - -void W_Plasma_Touch (void) -{ - //self.velocity = self.velocity * 0.1; - - PROJECTILE_TOUCH; - if (other.takedamage == DAMAGE_AIM) { - W_Plasma_Explode (); - } else { - //UpdateCSQCProjectile(self); - spamsound (self, CH_SHOTS, "weapons/electro_bounce.wav", VOL_BASE, ATTEN_NORM); - self.projectiledeathtype |= HITTYPE_BOUNCE; - } -} - -void W_Plasma_TouchExplode (void) -{ - PROJECTILE_TOUCH; - W_Plasma_Explode (); -} - -void W_Plasma_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) -{ - if(self.health <= 0) - return; - - // note: combos are usually triggered by W_Plasma_TriggerCombo, not damage - float is_combo = (inflictor.classname == "plasma_chain" || inflictor.classname == "plasma_prim"); - - if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, (is_combo ? 1 : -1))) - return; // g_projectiles_damage says to halt - - self.health = self.health - damage; - if (self.health <= 0) - { - self.takedamage = DAMAGE_NO; - self.nextthink = time; - if (is_combo) - { - // change owner to whoever caused the combo explosion - self.realowner = inflictor.realowner; - self.classname = "plasma_chain"; - self.think = W_Plasma_Explode_Combo; - self.nextthink = time + min(autocvar_g_balance_electro_combo_radius, vlen(self.origin - inflictor.origin)) / autocvar_g_balance_electro_combo_speed; // delay combo chains, looks cooler - // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bounding the length, because inflictor may be in a galaxy far far away (warpzones) - } - else - { - self.use = W_Plasma_Explode; - self.think = adaptor_think2use; // not _hittype_splash, as this runs "immediately" - } - } -} - -void W_Electro_Attack() -{ - entity proj; - - W_DecreaseAmmo(ammo_cells, autocvar_g_balance_electro_primary_ammo, autocvar_g_balance_electro_reload_ammo); - - W_SetupShot_ProjectileSize (self, '0 0 -3', '0 0 -3', FALSE, 2, "weapons/electro_fire.wav", CH_WEAPON_A, autocvar_g_balance_electro_primary_damage); - - pointparticles(particleeffectnum("electro_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - proj = spawn (); - proj.classname = "plasma_prim"; - proj.owner = proj.realowner = self; - proj.bot_dodge = TRUE; - proj.bot_dodgerating = autocvar_g_balance_electro_primary_damage; - proj.use = W_Plasma_Explode; - proj.think = adaptor_think2use_hittype_splash; - proj.nextthink = time + autocvar_g_balance_electro_primary_lifetime; - PROJECTILE_MAKETRIGGER(proj); - proj.projectiledeathtype = WEP_ELECTRO; - setorigin(proj, w_shotorg); - - proj.movetype = MOVETYPE_FLY; - W_SETUPPROJECTILEVELOCITY(proj, g_balance_electro_primary); - proj.angles = vectoangles(proj.velocity); - proj.touch = W_Plasma_TouchExplode; - setsize(proj, '0 0 -3', '0 0 -3'); - proj.flags = FL_PROJECTILE; - proj.missile_flags = MIF_SPLASH; - - CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO_BEAM, TRUE); - - other = proj; MUTATOR_CALLHOOK(EditProjectile); -} - -void W_Electro_Attack2() -{ - entity proj; - - W_DecreaseAmmo(ammo_cells, autocvar_g_balance_electro_secondary_ammo, autocvar_g_balance_electro_reload_ammo); - - W_SetupShot_ProjectileSize (self, '0 0 -4', '0 0 -4', FALSE, 2, "weapons/electro_fire2.wav", CH_WEAPON_A, autocvar_g_balance_electro_secondary_damage); - - w_shotdir = v_forward; // no TrueAim for grenades please - - pointparticles(particleeffectnum("electro_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - proj = spawn (); - proj.classname = "plasma"; - proj.owner = proj.realowner = self; - proj.use = W_Plasma_Explode; - proj.think = adaptor_think2use_hittype_splash; - proj.bot_dodge = TRUE; - proj.bot_dodgerating = autocvar_g_balance_electro_secondary_damage; - proj.nextthink = time + autocvar_g_balance_electro_secondary_lifetime; - PROJECTILE_MAKETRIGGER(proj); - proj.projectiledeathtype = WEP_ELECTRO | HITTYPE_SECONDARY; - setorigin(proj, w_shotorg); - - //proj.glow_size = 50; - //proj.glow_color = 45; - proj.movetype = MOVETYPE_BOUNCE; - W_SETUPPROJECTILEVELOCITY_UP(proj, g_balance_electro_secondary); - proj.touch = W_Plasma_Touch; - setsize(proj, '0 0 -4', '0 0 -4'); - proj.takedamage = DAMAGE_YES; - proj.damageforcescale = autocvar_g_balance_electro_secondary_damageforcescale; - proj.health = autocvar_g_balance_electro_secondary_health; - proj.event_damage = W_Plasma_Damage; - proj.flags = FL_PROJECTILE; - proj.damagedbycontents = (autocvar_g_balance_electro_secondary_damagedbycontents); - - proj.bouncefactor = autocvar_g_balance_electro_secondary_bouncefactor; - proj.bouncestop = autocvar_g_balance_electro_secondary_bouncestop; - proj.missile_flags = MIF_SPLASH | MIF_ARC; - -#if 0 - entity p2; - p2 = spawn(); - copyentity(proj, p2); - setmodel(p2, "models/ebomb.mdl"); - setsize(p2, proj.mins, proj.maxs); -#endif - - CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO, FALSE); // no culling, it has sound - - other = proj; MUTATOR_CALLHOOK(EditProjectile); -} - -.vector hook_start, hook_end; -float lgbeam_send(entity to, float sf) -{ - WriteByte(MSG_ENTITY, ENT_CLIENT_LGBEAM); - sf = sf & 0x7F; - if(sound_allowed(MSG_BROADCAST, self.realowner)) - sf |= 0x80; - WriteByte(MSG_ENTITY, sf); - if(sf & 1) - { - WriteByte(MSG_ENTITY, num_for_edict(self.realowner)); - WriteCoord(MSG_ENTITY, autocvar_g_balance_electro_primary_range); - } - if(sf & 2) - { - WriteCoord(MSG_ENTITY, self.hook_start_x); - WriteCoord(MSG_ENTITY, self.hook_start_y); - WriteCoord(MSG_ENTITY, self.hook_start_z); - } - if(sf & 4) - { - WriteCoord(MSG_ENTITY, self.hook_end_x); - WriteCoord(MSG_ENTITY, self.hook_end_y); - WriteCoord(MSG_ENTITY, self.hook_end_z); - } - return TRUE; -} -.entity lgbeam; -.float prevlgfire; -float lgbeam_checkammo() -{ - if(self.realowner.items & IT_UNLIMITED_WEAPON_AMMO) - return TRUE; - else if(autocvar_g_balance_electro_reload_ammo) - return self.realowner.clip_load > 0; - else - return self.realowner.ammo_cells > 0; -} - -entity lgbeam_owner_ent; -void lgbeam_think() -{ - entity owner_player; - owner_player = self.realowner; - - owner_player.prevlgfire = time; - if (self != owner_player.lgbeam) - { - remove(self); - return; - } - - if (owner_player.weaponentity.state != WS_INUSE || !lgbeam_checkammo() || owner_player.deadflag != DEAD_NO || !owner_player.BUTTON_ATCK || owner_player.frozen) - { - if(self == owner_player.lgbeam) - owner_player.lgbeam = world; - remove(self); - return; - } - - self.nextthink = time; - - makevectors(owner_player.v_angle); - - float dt, f; - dt = frametime; - - // if this weapon is reloadable, decrease its load. Else decrease the player's ammo - if (!(owner_player.items & IT_UNLIMITED_WEAPON_AMMO)) - { - if(autocvar_g_balance_electro_primary_ammo) - { - if(autocvar_g_balance_electro_reload_ammo) - { - dt = min(dt, owner_player.clip_load / autocvar_g_balance_electro_primary_ammo); - owner_player.clip_load = max(0, owner_player.clip_load - autocvar_g_balance_electro_primary_ammo * frametime); - owner_player.(weapon_load[WEP_ELECTRO]) = owner_player.clip_load; - } - else - { - dt = min(dt, owner_player.ammo_cells / autocvar_g_balance_electro_primary_ammo); - owner_player.ammo_cells = max(0, owner_player.ammo_cells - autocvar_g_balance_electro_primary_ammo * frametime); - } - } - } - - W_SetupShot_Range(owner_player, TRUE, 0, "", 0, autocvar_g_balance_electro_primary_damage * dt, autocvar_g_balance_electro_primary_range); - if(!lgbeam_owner_ent) - { - lgbeam_owner_ent = spawn(); - lgbeam_owner_ent.classname = "lgbeam_owner_ent"; - } - WarpZone_traceline_antilag(lgbeam_owner_ent, w_shotorg, w_shotend, MOVE_NORMAL, lgbeam_owner_ent, ANTILAG_LATENCY(owner_player)); - - // apply the damage - if(trace_ent) - { - vector force; - force = w_shotdir * autocvar_g_balance_electro_primary_force + '0 0 1' * autocvar_g_balance_electro_primary_force_up; - - f = ExponentialFalloff(autocvar_g_balance_electro_primary_falloff_mindist, autocvar_g_balance_electro_primary_falloff_maxdist, autocvar_g_balance_electro_primary_falloff_halflifedist, vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos) - w_shotorg)); - - if(accuracy_isgooddamage(owner_player, trace_ent)) - accuracy_add(owner_player, WEP_ELECTRO, 0, autocvar_g_balance_electro_primary_damage * dt * f); - Damage (trace_ent, owner_player, owner_player, autocvar_g_balance_electro_primary_damage * dt * f, WEP_ELECTRO, trace_endpos, force * dt); - } - W_Plasma_TriggerCombo(trace_endpos, autocvar_g_balance_electro_primary_comboradius, owner_player); - - // draw effect - if(w_shotorg != self.hook_start) - { - self.SendFlags |= 2; - self.hook_start = w_shotorg; - } - if(w_shotend != self.hook_end) - { - self.SendFlags |= 4; - self.hook_end = w_shotend; - } -} - -// experimental lightning gun -void W_Electro_Attack3 (void) -{ - // only play fire sound if 0.5 sec has passed since player let go the fire button - if(time - self.prevlgfire > 0.5) - sound (self, CH_WEAPON_A, "weapons/lgbeam_fire.wav", VOL_BASE, ATTEN_NORM); - - entity beam, oldself; - - self.lgbeam = beam = spawn(); - beam.classname = "lgbeam"; - beam.solid = SOLID_NOT; - beam.think = lgbeam_think; - beam.owner = beam.realowner = self; - beam.movetype = MOVETYPE_NONE; - beam.shot_spread = 0; - beam.bot_dodge = TRUE; - beam.bot_dodgerating = autocvar_g_balance_electro_primary_damage; - Net_LinkEntity(beam, FALSE, 0, lgbeam_send); - - oldself = self; - self = beam; - self.think(); - self = oldself; -} - -void ElectroInit() -{ - weapon_action(WEP_ELECTRO, WR_PRECACHE); - electro_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ELECTRO), FALSE, FALSE, 1); - electro_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ELECTRO), FALSE, FALSE, 2); - electro_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ELECTRO), FALSE, FALSE, 3); - electro_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ELECTRO), FALSE, FALSE, 4); -} - -void spawnfunc_weapon_electro (void) -{ - weapon_defaultspawnfunc(WEP_ELECTRO); -} - -void w_electro_checkattack() -{ - if(self.electro_count > 1) - if(self.BUTTON_ATCK2) - if(weapon_prepareattack(1, -1)) - { - W_Electro_Attack2(); - self.electro_count -= 1; - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_electro_secondary_animtime, w_electro_checkattack); - return; - } - - w_ready(); -} - -.float bot_secondary_electromooth; -.float BUTTON_ATCK_prev; -float w_electro(float req) -{ - float ammo_amount; - if (req == WR_AIM) - { - self.BUTTON_ATCK=FALSE; - self.BUTTON_ATCK2=FALSE; - if(vlen(self.origin-self.enemy.origin) > 1000) - self.bot_secondary_electromooth = 0; - if(self.bot_secondary_electromooth == 0) - { - float shoot; - - if(autocvar_g_balance_electro_primary_speed) - shoot = bot_aim(autocvar_g_balance_electro_primary_speed, 0, autocvar_g_balance_electro_primary_lifetime, FALSE); - else - shoot = bot_aim(1000000, 0, 0.001, FALSE); - - if(shoot) - { - self.BUTTON_ATCK = TRUE; - if(random() < 0.01) self.bot_secondary_electromooth = 1; - } - } - else - { - if(bot_aim(autocvar_g_balance_electro_secondary_speed, autocvar_g_balance_grenadelauncher_secondary_speed_up, autocvar_g_balance_electro_secondary_lifetime, TRUE)) - { - self.BUTTON_ATCK2 = TRUE; - if(random() < 0.03) self.bot_secondary_electromooth = 0; - } - } - } - else if (req == WR_THINK) - { - if(autocvar_g_balance_electro_reload_ammo) // forced reload - { - ammo_amount = 0; - if(autocvar_g_balance_electro_lightning) - { - if(self.clip_load > 0) - ammo_amount = 1; - } - else if(self.clip_load >= autocvar_g_balance_electro_primary_ammo) - ammo_amount = 1; - if(self.clip_load >= autocvar_g_balance_electro_secondary_ammo) - ammo_amount += 1; - - if(!ammo_amount) - { - weapon_action(self.weapon, WR_RELOAD); - return FALSE; - } - } - if (self.BUTTON_ATCK) - { - if(autocvar_g_balance_electro_lightning) - if(self.BUTTON_ATCK_prev) - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_electro_primary_animtime, w_ready); - - if (weapon_prepareattack(0, (autocvar_g_balance_electro_lightning ? 0 : autocvar_g_balance_electro_primary_refire))) - { - if(autocvar_g_balance_electro_lightning) - { - if ((!self.lgbeam) || wasfreed(self.lgbeam)) - { - W_Electro_Attack3(); - } - if(!self.BUTTON_ATCK_prev) - { - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_electro_primary_animtime, w_ready); - self.BUTTON_ATCK_prev = 1; - } - } - else - { - W_Electro_Attack(); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_electro_primary_animtime, w_ready); - } - } - } else { - if(autocvar_g_balance_electro_lightning) - { - if (self.BUTTON_ATCK_prev != 0) - { - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_electro_primary_animtime, w_ready); - ATTACK_FINISHED(self) = time + autocvar_g_balance_electro_primary_refire * W_WeaponRateFactor(); - } - self.BUTTON_ATCK_prev = 0; - } - - if (self.BUTTON_ATCK2) - { - if (time >= self.electro_secondarytime) - if (weapon_prepareattack(1, autocvar_g_balance_electro_secondary_refire)) - { - W_Electro_Attack2(); - self.electro_count = autocvar_g_balance_electro_secondary_count; - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_electro_secondary_animtime, w_electro_checkattack); - self.electro_secondarytime = time + autocvar_g_balance_electro_secondary_refire2 * W_WeaponRateFactor(); - } - } - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/weapons/g_electro.md3"); - precache_model ("models/weapons/v_electro.md3"); - precache_model ("models/weapons/h_electro.iqm"); - precache_sound ("weapons/electro_bounce.wav"); - precache_sound ("weapons/electro_fire.wav"); - precache_sound ("weapons/electro_fire2.wav"); - precache_sound ("weapons/electro_impact.wav"); - precache_sound ("weapons/electro_impact_combo.wav"); - //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else - if(autocvar_g_balance_electro_lightning) - { - precache_sound ("weapons/lgbeam_fire.wav"); - } - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_ELECTRO); - self.current_ammo = ammo_cells; - } - else if (req == WR_CHECKAMMO1) - { - if(autocvar_g_balance_electro_lightning) - { - if(!autocvar_g_balance_electro_primary_ammo) - ammo_amount = 1; - else - ammo_amount = self.ammo_cells > 0; - ammo_amount += self.(weapon_load[WEP_ELECTRO]) > 0; - } - else - { - ammo_amount = self.ammo_cells >= autocvar_g_balance_electro_primary_ammo; - ammo_amount += self.(weapon_load[WEP_ELECTRO]) >= autocvar_g_balance_electro_primary_ammo; - } - return ammo_amount; - } - else if (req == WR_CHECKAMMO2) - { - if(autocvar_g_balance_electro_combo_safeammocheck) // true if you can fire at least one secondary blob AND one primary shot after it, otherwise false. - { - ammo_amount = self.ammo_cells >= autocvar_g_balance_electro_secondary_ammo + autocvar_g_balance_electro_primary_ammo; - ammo_amount += self.(weapon_load[WEP_ELECTRO]) >= autocvar_g_balance_electro_secondary_ammo + autocvar_g_balance_electro_primary_ammo; - } - else - { - ammo_amount = self.ammo_cells >= autocvar_g_balance_electro_secondary_ammo; - ammo_amount += self.(weapon_load[WEP_ELECTRO]) >= autocvar_g_balance_electro_secondary_ammo; - } - return ammo_amount; - } - else if (req == WR_RESETPLAYER) - { - self.electro_secondarytime = time; - } - else if (req == WR_RELOAD) - { - W_Reload(min(autocvar_g_balance_electro_primary_ammo, autocvar_g_balance_electro_secondary_ammo), autocvar_g_balance_electro_reload_ammo, autocvar_g_balance_electro_reload_time, "weapons/reload.wav"); - } - else if (req == WR_SUICIDEMESSAGE) - { - if(w_deathtype & HITTYPE_SECONDARY) - return WEAPON_ELECTRO_SUICIDE_ORBS; - else - return WEAPON_ELECTRO_SUICIDE_BOLT; - } - else if (req == WR_KILLMESSAGE) - { - if(w_deathtype & HITTYPE_SECONDARY) - { - return WEAPON_ELECTRO_MURDER_ORBS; - } - else - { - if(w_deathtype & HITTYPE_BOUNCE) - return WEAPON_ELECTRO_MURDER_COMBO; - else - return WEAPON_ELECTRO_MURDER_BOLT; - } - } - return TRUE; -} -#endif -#ifdef CSQC -float w_electro(float req) -{ - if(req == WR_IMPACTEFFECT) - { - vector org2; - org2 = w_org + w_backoff * 6; - if(w_deathtype & HITTYPE_SECONDARY) - { - pointparticles(particleeffectnum("electro_ballexplode"), org2, '0 0 0', 1); - if(!w_issilent) - sound(self, CH_SHOTS, "weapons/electro_impact.wav", VOL_BASE, ATTEN_NORM); - } - else - { - if(w_deathtype & HITTYPE_BOUNCE) - { - // this is sent as "primary (w_deathtype & HITTYPE_BOUNCE)" to distinguish it from (w_deathtype & HITTYPE_SECONDARY) bounced balls - pointparticles(particleeffectnum("electro_combo"), org2, '0 0 0', 1); - if(!w_issilent) - sound(self, CH_SHOTS, "weapons/electro_impact_combo.wav", VOL_BASE, ATTEN_NORM); - } - else - { - pointparticles(particleeffectnum("electro_impact"), org2, '0 0 0', 1); - if(!w_issilent) - sound(self, CH_SHOTS, "weapons/electro_impact.wav", VOL_BASE, ATTEN_NORM); - } - } - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/electro_impact.wav"); - precache_sound("weapons/electro_impact_combo.wav"); - } - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_electro.qh b/qcsrc/server/w_electro.qh deleted file mode 100644 index 98c0be13ec..0000000000 --- a/qcsrc/server/w_electro.qh +++ /dev/null @@ -1,2 +0,0 @@ -void ElectroInit(); -vector electro_shotorigin[4]; diff --git a/qcsrc/server/w_fireball.qc b/qcsrc/server/w_fireball.qc deleted file mode 100644 index 88e9d770fb..0000000000 --- a/qcsrc/server/w_fireball.qc +++ /dev/null @@ -1,434 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ FIREBALL, -/* function */ w_fireball, -/* ammotype */ 0, -/* impulse */ 9, -/* flags */ WEP_FLAG_SUPERWEAPON | WEP_TYPE_SPLASH, -/* rating */ BOT_PICKUP_RATING_MID, -/* model */ "fireball", -/* shortname */ "fireball", -/* fullname */ _("Fireball") -); -#else -#ifdef SVQC -.float bot_primary_fireballmooth; // whatever a mooth is -.vector fireball_impactvec; -.float fireball_primarytime; - -void W_Fireball_Explode (void) -{ - entity e; - float dist; - float points; - vector dir; - float d; - - self.event_damage = func_null; - self.takedamage = DAMAGE_NO; - - // 1. dist damage - d = (self.realowner.health + self.realowner.armorvalue); - RadiusDamage (self, self.realowner, autocvar_g_balance_fireball_primary_damage, autocvar_g_balance_fireball_primary_edgedamage, autocvar_g_balance_fireball_primary_radius, world, autocvar_g_balance_fireball_primary_force, self.projectiledeathtype, other); - if(self.realowner.health + self.realowner.armorvalue >= d) - if(!self.cnt) - { - modeleffect_spawn("models/sphere/sphere.md3", 0, 0, self.origin, '0 0 0', '0 0 0', '0 0 0', 0, autocvar_g_balance_fireball_primary_bfgradius, 0.2, 0.05, 0.25); - - // 2. bfg effect - // NOTE: this cannot be made warpzone aware by design. So, better intentionally ignore warpzones here. - for(e = findradius(self.origin, autocvar_g_balance_fireball_primary_bfgradius); e; e = e.chain) - if(e != self.realowner) if(e.takedamage == DAMAGE_AIM) if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self)) - { - // can we see fireball? - traceline(e.origin + e.view_ofs, self.origin, MOVE_NORMAL, e); - if(/* trace_startsolid || */ trace_fraction != 1) // startsolid should be never happening anyway - continue; - // can we see player who shot fireball? - traceline(e.origin + e.view_ofs, self.realowner.origin + self.realowner.view_ofs, MOVE_NORMAL, e); - if(trace_ent != self.realowner) - if(/* trace_startsolid || */ trace_fraction != 1) - continue; - dist = vlen(self.origin - e.origin - e.view_ofs); - points = (1 - sqrt(dist / autocvar_g_balance_fireball_primary_bfgradius)); - if(points <= 0) - continue; - dir = normalize(e.origin + e.view_ofs - self.origin); - - if(accuracy_isgooddamage(self.realowner, e)) - accuracy_add(self.realowner, WEP_FIREBALL, 0, autocvar_g_balance_fireball_primary_bfgdamage * points); - - Damage(e, self, self.realowner, autocvar_g_balance_fireball_primary_bfgdamage * points, self.projectiledeathtype | HITTYPE_BOUNCE | HITTYPE_SPLASH, e.origin + e.view_ofs, autocvar_g_balance_fireball_primary_bfgforce * dir); - pointparticles(particleeffectnum("fireball_bfgdamage"), e.origin, -1 * dir, 1); - } - } - - remove (self); -} - -void W_Fireball_TouchExplode (void) -{ - PROJECTILE_TOUCH; - W_Fireball_Explode (); -} - -void W_Fireball_LaserPlay(float dt, float dist, float damage, float edgedamage, float burntime) -{ - entity e; - float d; - vector p; - - if(damage <= 0) - return; - - RandomSelection_Init(); - for(e = WarpZone_FindRadius(self.origin, dist, TRUE); e; e = e.chain) - if(e != self.realowner) if(e.takedamage == DAMAGE_AIM) if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self)) - { - p = e.origin; - p_x += e.mins_x + random() * (e.maxs_x - e.mins_x); - p_y += e.mins_y + random() * (e.maxs_y - e.mins_y); - p_z += e.mins_z + random() * (e.maxs_z - e.mins_z); - d = vlen(WarpZone_UnTransformOrigin(e, self.origin) - p); - if(d < dist) - { - e.fireball_impactvec = p; - RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e)); - } - } - if(RandomSelection_chosen_ent) - { - d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec); - d = damage + (edgedamage - damage) * (d / dist); - Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE); - //trailparticles(self, particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec); - pointparticles(particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1); - } -} - -void W_Fireball_Think() -{ - if(time > self.pushltime) - { - self.cnt = 1; - self.projectiledeathtype |= HITTYPE_SPLASH; - W_Fireball_Explode(); - return; - } - - W_Fireball_LaserPlay(0.1, autocvar_g_balance_fireball_primary_laserradius, autocvar_g_balance_fireball_primary_laserdamage, autocvar_g_balance_fireball_primary_laseredgedamage, autocvar_g_balance_fireball_primary_laserburntime); - - self.nextthink = time + 0.1; -} - -void W_Fireball_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) -{ - if(self.health <= 0) - return; - - if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions - return; // g_projectiles_damage says to halt - - self.health = self.health - damage; - if (self.health <= 0) - { - self.cnt = 1; - W_PrepareExplosionByDamage(attacker, W_Fireball_Explode); - } -} - -void W_Fireball_Attack1() -{ - entity proj; - - W_SetupShot_ProjectileSize (self, '-16 -16 -16', '16 16 16', FALSE, 2, "weapons/fireball_fire2.wav", CH_WEAPON_A, autocvar_g_balance_fireball_primary_damage + autocvar_g_balance_fireball_primary_bfgdamage); - - pointparticles(particleeffectnum("fireball_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - proj = spawn (); - proj.classname = "plasma_prim"; - proj.owner = proj.realowner = self; - proj.bot_dodge = TRUE; - proj.bot_dodgerating = autocvar_g_balance_fireball_primary_damage; - proj.pushltime = time + autocvar_g_balance_fireball_primary_lifetime; - proj.use = W_Fireball_Explode; - proj.think = W_Fireball_Think; - proj.nextthink = time; - proj.health = autocvar_g_balance_fireball_primary_health; - proj.team = self.team; - proj.event_damage = W_Fireball_Damage; - proj.takedamage = DAMAGE_YES; - proj.damageforcescale = autocvar_g_balance_fireball_primary_damageforcescale; - PROJECTILE_MAKETRIGGER(proj); - proj.projectiledeathtype = WEP_FIREBALL; - setorigin(proj, w_shotorg); - - proj.movetype = MOVETYPE_FLY; - W_SETUPPROJECTILEVELOCITY(proj, g_balance_fireball_primary); - proj.angles = vectoangles(proj.velocity); - proj.touch = W_Fireball_TouchExplode; - setsize(proj, '-16 -16 -16', '16 16 16'); - proj.flags = FL_PROJECTILE; - proj.missile_flags = MIF_SPLASH | MIF_PROXY; - - CSQCProjectile(proj, TRUE, PROJECTILE_FIREBALL, TRUE); - - other = proj; MUTATOR_CALLHOOK(EditProjectile); -} - -void W_Fireball_AttackEffect(float i, vector f_diff) -{ - W_SetupShot_ProjectileSize (self, '-16 -16 -16', '16 16 16', FALSE, 0, "", 0, 0); - w_shotorg += f_diff_x * v_up + f_diff_y * v_right; - pointparticles(particleeffectnum("fireball_preattack_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); -} - -void W_Fireball_Attack1_Frame4() -{ - W_Fireball_Attack1(); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_fireball_primary_animtime, w_ready); -} - -void W_Fireball_Attack1_Frame3() -{ - W_Fireball_AttackEffect(0, '+1.25 +3.75 0'); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_fireball_primary_animtime, W_Fireball_Attack1_Frame4); -} - -void W_Fireball_Attack1_Frame2() -{ - W_Fireball_AttackEffect(0, '-1.25 +3.75 0'); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_fireball_primary_animtime, W_Fireball_Attack1_Frame3); -} - -void W_Fireball_Attack1_Frame1() -{ - W_Fireball_AttackEffect(1, '+1.25 -3.75 0'); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_fireball_primary_animtime, W_Fireball_Attack1_Frame2); -} - -void W_Fireball_Attack1_Frame0() -{ - W_Fireball_AttackEffect(0, '-1.25 -3.75 0'); - sound (self, CH_WEAPON_SINGLE, "weapons/fireball_prefire2.wav", VOL_BASE, ATTEN_NORM); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_fireball_primary_animtime, W_Fireball_Attack1_Frame1); -} - -void W_Firemine_Think() -{ - if(time > self.pushltime) - { - remove(self); - return; - } - - // make it "hot" once it leaves its owner - if(self.owner) - { - if(vlen(self.origin - self.owner.origin - self.owner.view_ofs) > autocvar_g_balance_fireball_secondary_laserradius) - { - self.cnt += 1; - if(self.cnt == 3) - self.owner = world; - } - else - self.cnt = 0; - } - - W_Fireball_LaserPlay(0.1, autocvar_g_balance_fireball_secondary_laserradius, autocvar_g_balance_fireball_secondary_laserdamage, autocvar_g_balance_fireball_secondary_laseredgedamage, autocvar_g_balance_fireball_secondary_laserburntime); - - self.nextthink = time + 0.1; -} - -void W_Firemine_Touch (void) -{ - PROJECTILE_TOUCH; - if (other.takedamage == DAMAGE_AIM) - if(Fire_AddDamage(other, self.realowner, autocvar_g_balance_fireball_secondary_damage, autocvar_g_balance_fireball_secondary_damagetime, self.projectiledeathtype) >= 0) - { - remove(self); - return; - } - self.projectiledeathtype |= HITTYPE_BOUNCE; -} - -void W_Fireball_Attack2() -{ - entity proj; - vector f_diff; - float c; - - c = mod(self.bulletcounter, 4); - switch(c) - { - case 0: - f_diff = '-1.25 -3.75 0'; - break; - case 1: - f_diff = '+1.25 -3.75 0'; - break; - case 2: - f_diff = '-1.25 +3.75 0'; - break; - case 3: - default: - f_diff = '+1.25 +3.75 0'; - break; - } - W_SetupShot_ProjectileSize(self, '-4 -4 -4', '4 4 4', FALSE, 2, "weapons/fireball_fire.wav", CH_WEAPON_A, autocvar_g_balance_fireball_secondary_damage); - traceline(w_shotorg, w_shotorg + f_diff_x * v_up + f_diff_y * v_right, MOVE_NORMAL, self); - w_shotorg = trace_endpos; - - pointparticles(particleeffectnum("fireball_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - proj = spawn (); - proj.owner = proj.realowner = self; - proj.classname = "grenade"; - proj.bot_dodge = TRUE; - proj.bot_dodgerating = autocvar_g_balance_fireball_secondary_damage; - proj.movetype = MOVETYPE_BOUNCE; - proj.projectiledeathtype = WEP_FIREBALL | HITTYPE_SECONDARY; - proj.touch = W_Firemine_Touch; - PROJECTILE_MAKETRIGGER(proj); - setsize(proj, '-4 -4 -4', '4 4 4'); - setorigin(proj, w_shotorg); - proj.think = W_Firemine_Think; - proj.nextthink = time; - proj.damageforcescale = autocvar_g_balance_fireball_secondary_damageforcescale; - proj.pushltime = time + autocvar_g_balance_fireball_secondary_lifetime; - W_SETUPPROJECTILEVELOCITY_UP(proj, g_balance_fireball_secondary); - - proj.angles = vectoangles(proj.velocity); - proj.flags = FL_PROJECTILE; - proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC; - - CSQCProjectile(proj, TRUE, PROJECTILE_FIREMINE, TRUE); - - other = proj; MUTATOR_CALLHOOK(EditProjectile); -} - -void spawnfunc_weapon_fireball (void) -{ - weapon_defaultspawnfunc(WEP_FIREBALL); -} - -float w_fireball(float req) -{ - //float ammo_amount; - if (req == WR_AIM) - { - self.BUTTON_ATCK = FALSE; - self.BUTTON_ATCK2 = FALSE; - if (self.bot_primary_fireballmooth == 0) - { - if(bot_aim(autocvar_g_balance_fireball_primary_speed, 0, autocvar_g_balance_fireball_primary_lifetime, FALSE)) - { - self.BUTTON_ATCK = TRUE; - if(random() < 0.02) self.bot_primary_fireballmooth = 0; - } - } - else - { - if(bot_aim(autocvar_g_balance_fireball_secondary_speed, autocvar_g_balance_fireball_secondary_speed_up, autocvar_g_balance_fireball_secondary_lifetime, TRUE)) - { - self.BUTTON_ATCK2 = TRUE; - if(random() < 0.01) self.bot_primary_fireballmooth = 1; - } - } - } - else if (req == WR_THINK) - { - if (self.BUTTON_ATCK) - { - if (time >= self.fireball_primarytime) - if (weapon_prepareattack(0, autocvar_g_balance_fireball_primary_refire)) - { - W_Fireball_Attack1_Frame0(); - self.fireball_primarytime = time + autocvar_g_balance_fireball_primary_refire2 * W_WeaponRateFactor(); - } - } - else if (self.BUTTON_ATCK2) - { - if (weapon_prepareattack(1, autocvar_g_balance_fireball_secondary_refire)) - { - W_Fireball_Attack2(); - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_fireball_secondary_animtime, w_ready); - } - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/weapons/g_fireball.md3"); - precache_model ("models/weapons/v_fireball.md3"); - precache_model ("models/weapons/h_fireball.iqm"); - precache_model ("models/sphere/sphere.md3"); - precache_sound ("weapons/fireball_fire.wav"); - precache_sound ("weapons/fireball_fire2.wav"); - precache_sound ("weapons/fireball_prefire2.wav"); - //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_FIREBALL); - self.current_ammo = ammo_none; - } - else if (req == WR_CHECKAMMO1) - { - return 1; - } - else if (req == WR_CHECKAMMO2) - { - return 1; - } - else if (req == WR_RESETPLAYER) - { - self.fireball_primarytime = time; - } - else if (req == WR_SUICIDEMESSAGE) - { - if(w_deathtype & HITTYPE_SECONDARY) - return WEAPON_FIREBALL_SUICIDE_FIREMINE; - else - return WEAPON_FIREBALL_SUICIDE_BLAST; - } - else if (req == WR_KILLMESSAGE) - { - if(w_deathtype & HITTYPE_SECONDARY) - { - return WEAPON_FIREBALL_MURDER_FIREMINE; - } - else - { - return WEAPON_FIREBALL_MURDER_BLAST; - } - } - return TRUE; -} -#endif -#ifdef CSQC -float w_fireball(float req) -{ - if(req == WR_IMPACTEFFECT) - { - vector org2; - if(w_deathtype & HITTYPE_SECONDARY) - { - // firemine goes out silently - } - else - { - org2 = w_org + w_backoff * 16; - pointparticles(particleeffectnum("fireball_explode"), org2, '0 0 0', 1); - if(!w_issilent) - sound(self, CH_SHOTS, "weapons/fireball_impact2.wav", VOL_BASE, ATTEN_NORM * 0.25); // long range boom - } - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/fireball_impact2.wav"); - } - - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_grenadelauncher.qc b/qcsrc/server/w_grenadelauncher.qc deleted file mode 100644 index 17144d3a53..0000000000 --- a/qcsrc/server/w_grenadelauncher.qc +++ /dev/null @@ -1,414 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ GRENADE_LAUNCHER, -/* function */ w_glauncher, -/* ammotype */ IT_ROCKETS, -/* impulse */ 4, -/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH, -/* rating */ BOT_PICKUP_RATING_MID, -/* model */ "gl", -/* shortname */ "grenadelauncher", -/* fullname */ _("Mortar") -); -#else -#ifdef SVQC -.float gl_detonate_later; -.float gl_bouncecnt; - -void W_Grenade_Explode (void) -{ - if(other.takedamage == DAMAGE_AIM) - if(IS_PLAYER(other)) - if(DIFF_TEAM(self.realowner, other)) - if(other.deadflag == DEAD_NO) - if(IsFlying(other)) - Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); - - self.event_damage = func_null; - self.takedamage = DAMAGE_NO; - - if(self.movetype == MOVETYPE_NONE) - self.velocity = self.oldvelocity; - - RadiusDamage (self, self.realowner, autocvar_g_balance_grenadelauncher_primary_damage, autocvar_g_balance_grenadelauncher_primary_edgedamage, autocvar_g_balance_grenadelauncher_primary_radius, world, autocvar_g_balance_grenadelauncher_primary_force, self.projectiledeathtype, other); - - remove (self); -} - -void W_Grenade_Explode2 (void) -{ - if(other.takedamage == DAMAGE_AIM) - if(IS_PLAYER(other)) - if(DIFF_TEAM(self.realowner, other)) - if(other.deadflag == DEAD_NO) - if(IsFlying(other)) - Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); - - self.event_damage = func_null; - self.takedamage = DAMAGE_NO; - - if(self.movetype == MOVETYPE_NONE) - self.velocity = self.oldvelocity; - - RadiusDamage (self, self.realowner, autocvar_g_balance_grenadelauncher_secondary_damage, autocvar_g_balance_grenadelauncher_secondary_edgedamage, autocvar_g_balance_grenadelauncher_secondary_radius, world, autocvar_g_balance_grenadelauncher_secondary_force, self.projectiledeathtype, other); - - remove (self); -} - -void W_Grenade_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) -{ - if (self.health <= 0) - return; - - if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions - return; // g_projectiles_damage says to halt - - self.health = self.health - damage; - - if (self.health <= 0) - W_PrepareExplosionByDamage(attacker, self.use); -} - -void W_Grenade_Think1 (void) -{ - self.nextthink = time; - if (time > self.cnt) - { - other = world; - self.projectiledeathtype |= HITTYPE_BOUNCE; - W_Grenade_Explode (); - return; - } - if(self.gl_detonate_later && self.gl_bouncecnt >= autocvar_g_balance_grenadelauncher_primary_remote_minbouncecnt) - W_Grenade_Explode(); -} - -void W_Grenade_Touch1 (void) -{ - PROJECTILE_TOUCH; - if (other.takedamage == DAMAGE_AIM || autocvar_g_balance_grenadelauncher_primary_type == 0) // always explode when hitting a player, or if normal mortar projectile - { - self.use (); - } - else if (autocvar_g_balance_grenadelauncher_primary_type == 1) // bounce - { - float r; - r = random() * 6; - if(r < 1) - spamsound (self, CH_SHOTS, "weapons/grenade_bounce1.wav", VOL_BASE, ATTEN_NORM); - else if(r < 2) - spamsound (self, CH_SHOTS, "weapons/grenade_bounce2.wav", VOL_BASE, ATTEN_NORM); - else if(r < 3) - spamsound (self, CH_SHOTS, "weapons/grenade_bounce3.wav", VOL_BASE, ATTEN_NORM); - else if(r < 4) - spamsound (self, CH_SHOTS, "weapons/grenade_bounce4.wav", VOL_BASE, ATTEN_NORM); - else if(r < 5) - spamsound (self, CH_SHOTS, "weapons/grenade_bounce5.wav", VOL_BASE, ATTEN_NORM); - else - spamsound (self, CH_SHOTS, "weapons/grenade_bounce6.wav", VOL_BASE, ATTEN_NORM); - self.projectiledeathtype |= HITTYPE_BOUNCE; - self.gl_bouncecnt += 1; - } - else if(autocvar_g_balance_grenadelauncher_primary_type == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick - { - spamsound (self, CH_SHOTS, "weapons/grenade_stick.wav", VOL_BASE, ATTEN_NORM); - - // let it stick whereever it is - self.oldvelocity = self.velocity; - self.velocity = '0 0 0'; - self.movetype = MOVETYPE_NONE; // also disables gravity - self.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO - UpdateCSQCProjectile(self); - - // do not respond to any more touches - self.solid = SOLID_NOT; - - self.nextthink = min(self.nextthink, time + autocvar_g_balance_grenadelauncher_primary_lifetime_stick); - } -} - -void W_Grenade_Touch2 (void) -{ - PROJECTILE_TOUCH; - if (other.takedamage == DAMAGE_AIM || autocvar_g_balance_grenadelauncher_secondary_type == 0) // always explode when hitting a player, or if normal mortar projectile - { - self.use (); - } - else if (autocvar_g_balance_grenadelauncher_secondary_type == 1) // bounce - { - float r; - r = random() * 6; - if(r < 1) - spamsound (self, CH_SHOTS, "weapons/grenade_bounce1.wav", VOL_BASE, ATTEN_NORM); - else if(r < 2) - spamsound (self, CH_SHOTS, "weapons/grenade_bounce2.wav", VOL_BASE, ATTEN_NORM); - else if(r < 3) - spamsound (self, CH_SHOTS, "weapons/grenade_bounce3.wav", VOL_BASE, ATTEN_NORM); - else if(r < 4) - spamsound (self, CH_SHOTS, "weapons/grenade_bounce4.wav", VOL_BASE, ATTEN_NORM); - else if(r < 5) - spamsound (self, CH_SHOTS, "weapons/grenade_bounce5.wav", VOL_BASE, ATTEN_NORM); - else - spamsound (self, CH_SHOTS, "weapons/grenade_bounce6.wav", VOL_BASE, ATTEN_NORM); - self.projectiledeathtype |= HITTYPE_BOUNCE; - self.gl_bouncecnt += 1; - - if (autocvar_g_balance_grenadelauncher_secondary_lifetime_bounce && self.gl_bouncecnt == 1) - self.nextthink = time + autocvar_g_balance_grenadelauncher_secondary_lifetime_bounce; - - } - else if(autocvar_g_balance_grenadelauncher_secondary_type == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick - { - spamsound (self, CH_SHOTS, "weapons/grenade_stick.wav", VOL_BASE, ATTEN_NORM); - - // let it stick whereever it is - self.oldvelocity = self.velocity; - self.velocity = '0 0 0'; - self.movetype = MOVETYPE_NONE; // also disables gravity - self.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO - UpdateCSQCProjectile(self); - - // do not respond to any more touches - self.solid = SOLID_NOT; - - self.nextthink = min(self.nextthink, time + autocvar_g_balance_grenadelauncher_secondary_lifetime_stick); - } -} - -void W_Grenade_Attack (void) -{ - entity gren; - - W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_grenadelauncher_primary_ammo, autocvar_g_balance_grenadelauncher_reload_ammo); - - W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', FALSE, 4, "weapons/grenade_fire.wav", CH_WEAPON_A, autocvar_g_balance_grenadelauncher_primary_damage); - w_shotdir = v_forward; // no TrueAim for grenades please - - pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - gren = spawn (); - gren.owner = gren.realowner = self; - gren.classname = "grenade"; - gren.bot_dodge = TRUE; - gren.bot_dodgerating = autocvar_g_balance_grenadelauncher_primary_damage; - gren.movetype = MOVETYPE_BOUNCE; - gren.bouncefactor = autocvar_g_balance_grenadelauncher_bouncefactor; - gren.bouncestop = autocvar_g_balance_grenadelauncher_bouncestop; - PROJECTILE_MAKETRIGGER(gren); - gren.projectiledeathtype = WEP_GRENADE_LAUNCHER; - setorigin(gren, w_shotorg); - setsize(gren, '-3 -3 -3', '3 3 3'); - - gren.cnt = time + autocvar_g_balance_grenadelauncher_primary_lifetime; - gren.nextthink = time; - gren.think = W_Grenade_Think1; - gren.use = W_Grenade_Explode; - gren.touch = W_Grenade_Touch1; - - gren.takedamage = DAMAGE_YES; - gren.health = autocvar_g_balance_grenadelauncher_primary_health; - gren.damageforcescale = autocvar_g_balance_grenadelauncher_primary_damageforcescale; - gren.event_damage = W_Grenade_Damage; - gren.damagedbycontents = TRUE; - gren.missile_flags = MIF_SPLASH | MIF_ARC; - W_SETUPPROJECTILEVELOCITY_UP(gren, g_balance_grenadelauncher_primary); - - gren.angles = vectoangles (gren.velocity); - gren.flags = FL_PROJECTILE; - - if(autocvar_g_balance_grenadelauncher_primary_type == 0 || autocvar_g_balance_grenadelauncher_primary_type == 2) - CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE, TRUE); - else - CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE_BOUNCING, TRUE); - - other = gren; MUTATOR_CALLHOOK(EditProjectile); -} - -void W_Grenade_Attack2 (void) -{ - entity gren; - - W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_grenadelauncher_secondary_ammo, autocvar_g_balance_grenadelauncher_reload_ammo); - - W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', FALSE, 4, "weapons/grenade_fire.wav", CH_WEAPON_A, autocvar_g_balance_grenadelauncher_secondary_damage); - w_shotdir = v_forward; // no TrueAim for grenades please - - pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - gren = spawn (); - gren.owner = gren.realowner = self; - gren.classname = "grenade"; - gren.bot_dodge = TRUE; - gren.bot_dodgerating = autocvar_g_balance_grenadelauncher_secondary_damage; - gren.movetype = MOVETYPE_BOUNCE; - gren.bouncefactor = autocvar_g_balance_grenadelauncher_bouncefactor; - gren.bouncestop = autocvar_g_balance_grenadelauncher_bouncestop; - PROJECTILE_MAKETRIGGER(gren); - gren.projectiledeathtype = WEP_GRENADE_LAUNCHER | HITTYPE_SECONDARY; - setorigin(gren, w_shotorg); - setsize(gren, '-3 -3 -3', '3 3 3'); - - gren.nextthink = time + autocvar_g_balance_grenadelauncher_secondary_lifetime; - gren.think = adaptor_think2use_hittype_splash; - gren.use = W_Grenade_Explode2; - gren.touch = W_Grenade_Touch2; - - gren.takedamage = DAMAGE_YES; - gren.health = autocvar_g_balance_grenadelauncher_secondary_health; - gren.damageforcescale = autocvar_g_balance_grenadelauncher_secondary_damageforcescale; - gren.event_damage = W_Grenade_Damage; - gren.damagedbycontents = TRUE; - gren.missile_flags = MIF_SPLASH | MIF_ARC; - W_SETUPPROJECTILEVELOCITY_UP(gren, g_balance_grenadelauncher_secondary); - - gren.angles = vectoangles (gren.velocity); - gren.flags = FL_PROJECTILE; - - if(autocvar_g_balance_grenadelauncher_secondary_type == 0 || autocvar_g_balance_grenadelauncher_secondary_type == 2) - CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE, TRUE); - else - CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE_BOUNCING, TRUE); - - other = gren; MUTATOR_CALLHOOK(EditProjectile); -} - -void spawnfunc_weapon_grenadelauncher (void) -{ - weapon_defaultspawnfunc(WEP_GRENADE_LAUNCHER); -} - -.float bot_secondary_grenademooth; -float w_glauncher(float req) -{ - entity nade; - float nadefound; - float ammo_amount; - - if (req == WR_AIM) - { - self.BUTTON_ATCK = FALSE; - self.BUTTON_ATCK2 = FALSE; - if (self.bot_secondary_grenademooth == 0) - { - if(bot_aim(autocvar_g_balance_grenadelauncher_primary_speed, autocvar_g_balance_grenadelauncher_primary_speed_up, autocvar_g_balance_grenadelauncher_primary_lifetime, TRUE)) - { - self.BUTTON_ATCK = TRUE; - if(random() < 0.01) self.bot_secondary_grenademooth = 1; - } - } - else - { - if(bot_aim(autocvar_g_balance_grenadelauncher_secondary_speed, autocvar_g_balance_grenadelauncher_secondary_speed_up, autocvar_g_balance_grenadelauncher_secondary_lifetime, TRUE)) - { - self.BUTTON_ATCK2 = TRUE; - if(random() < 0.02) self.bot_secondary_grenademooth = 0; - } - } - } - else if (req == WR_THINK) - { - if(autocvar_g_balance_grenadelauncher_reload_ammo && self.clip_load < min(autocvar_g_balance_grenadelauncher_primary_ammo, autocvar_g_balance_grenadelauncher_secondary_ammo)) // forced reload - weapon_action(self.weapon, WR_RELOAD); - else if (self.BUTTON_ATCK) - { - if (weapon_prepareattack(0, autocvar_g_balance_grenadelauncher_primary_refire)) - { - W_Grenade_Attack(); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_grenadelauncher_primary_animtime, w_ready); - } - } - else if (self.BUTTON_ATCK2) - { - if (cvar("g_balance_grenadelauncher_secondary_remote_detonateprimary")) - { - nadefound = 0; - for(nade = world; (nade = find(nade, classname, "grenade")); ) if(nade.realowner == self) - { - if(!nade.gl_detonate_later) - { - nade.gl_detonate_later = TRUE; - nadefound = 1; - } - } - if(nadefound) - sound (self, CH_WEAPON_B, "weapons/rocket_det.wav", VOL_BASE, ATTEN_NORM); - } - else if (weapon_prepareattack(1, autocvar_g_balance_grenadelauncher_secondary_refire)) - { - W_Grenade_Attack2(); - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_grenadelauncher_secondary_animtime, w_ready); - } - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/weapons/g_gl.md3"); - precache_model ("models/weapons/v_gl.md3"); - precache_model ("models/weapons/h_gl.iqm"); - precache_sound ("weapons/grenade_bounce1.wav"); - precache_sound ("weapons/grenade_bounce2.wav"); - precache_sound ("weapons/grenade_bounce3.wav"); - precache_sound ("weapons/grenade_bounce4.wav"); - precache_sound ("weapons/grenade_bounce5.wav"); - precache_sound ("weapons/grenade_bounce6.wav"); - precache_sound ("weapons/grenade_stick.wav"); - precache_sound ("weapons/grenade_fire.wav"); - //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_GRENADE_LAUNCHER); - self.current_ammo = ammo_rockets; - } - else if (req == WR_CHECKAMMO1) - { - ammo_amount = self.ammo_rockets >= autocvar_g_balance_grenadelauncher_primary_ammo; - ammo_amount += self.(weapon_load[WEP_GRENADE_LAUNCHER]) >= autocvar_g_balance_grenadelauncher_primary_ammo; - return ammo_amount; - } - else if (req == WR_CHECKAMMO2) - { - ammo_amount = self.ammo_rockets >= autocvar_g_balance_grenadelauncher_secondary_ammo; - ammo_amount += self.(weapon_load[WEP_GRENADE_LAUNCHER]) >= autocvar_g_balance_grenadelauncher_secondary_ammo; - return ammo_amount; - } - else if (req == WR_RELOAD) - { - W_Reload(min(autocvar_g_balance_grenadelauncher_primary_ammo, autocvar_g_balance_grenadelauncher_secondary_ammo), autocvar_g_balance_grenadelauncher_reload_ammo, autocvar_g_balance_grenadelauncher_reload_time, "weapons/reload.wav"); - } - else if (req == WR_SUICIDEMESSAGE) - { - if(w_deathtype & HITTYPE_SECONDARY) - return WEAPON_MORTAR_SUICIDE_BOUNCE; - else - return WEAPON_MORTAR_SUICIDE_EXPLODE; - } - else if (req == WR_KILLMESSAGE) - { - if(w_deathtype & HITTYPE_SECONDARY) - return WEAPON_MORTAR_MURDER_BOUNCE; - else - return WEAPON_MORTAR_MURDER_EXPLODE; - } - return TRUE; -} -#endif -#ifdef CSQC -float w_glauncher(float req) -{ - if(req == WR_IMPACTEFFECT) - { - vector org2; - org2 = w_org + w_backoff * 12; - pointparticles(particleeffectnum("grenade_explode"), org2, '0 0 0', 1); - if(!w_issilent) - sound(self, CH_SHOTS, "weapons/grenade_impact.wav", VOL_BASE, ATTEN_NORM); - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/grenade_impact.wav"); - } - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_hagar.qc b/qcsrc/server/w_hagar.qc deleted file mode 100644 index 05768fc50b..0000000000 --- a/qcsrc/server/w_hagar.qc +++ /dev/null @@ -1,489 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ HAGAR, -/* function */ w_hagar, -/* ammotype */ IT_ROCKETS, -/* impulse */ 8, -/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH, -/* rating */ BOT_PICKUP_RATING_MID, -/* model */ "hagar", -/* shortname */ "hagar", -/* fullname */ _("Hagar") -); -#else -#ifdef SVQC -// NO bounce protection, as bounces are limited! - -void W_Hagar_Explode (void) -{ - self.event_damage = func_null; - RadiusDamage (self, self.realowner, autocvar_g_balance_hagar_primary_damage, autocvar_g_balance_hagar_primary_edgedamage, autocvar_g_balance_hagar_primary_radius, world, autocvar_g_balance_hagar_primary_force, self.projectiledeathtype, other); - - remove (self); -} - -void W_Hagar_Explode2 (void) -{ - self.event_damage = func_null; - RadiusDamage (self, self.realowner, autocvar_g_balance_hagar_secondary_damage, autocvar_g_balance_hagar_secondary_edgedamage, autocvar_g_balance_hagar_secondary_radius, world, autocvar_g_balance_hagar_secondary_force, self.projectiledeathtype, other); - - remove (self); -} - -void W_Hagar_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) -{ - if (self.health <= 0) - return; - - float is_linkexplode = ( ((inflictor.owner != world) ? (inflictor.owner == self.owner) : TRUE) - && (inflictor.projectiledeathtype & HITTYPE_SECONDARY) - && (self.projectiledeathtype & HITTYPE_SECONDARY)); - - if(is_linkexplode) - is_linkexplode = (is_linkexplode && autocvar_g_balance_hagar_secondary_load_linkexplode); - else - is_linkexplode = -1; // not secondary load, so continue as normal without exception. - - if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, is_linkexplode)) - return; // g_projectiles_damage says to halt - - self.health = self.health - damage; - self.angles = vectoangles(self.velocity); - - if (self.health <= 0) - W_PrepareExplosionByDamage(attacker, self.think); -} - -void W_Hagar_Touch (void) -{ - PROJECTILE_TOUCH; - self.use (); -} - -void W_Hagar_Touch2 (void) -{ - PROJECTILE_TOUCH; - - if(self.cnt > 0 || other.takedamage == DAMAGE_AIM) { - self.use(); - } else { - self.cnt++; - pointparticles(particleeffectnum("hagar_bounce"), self.origin, self.velocity, 1); - self.angles = vectoangles (self.velocity); - self.owner = world; - self.projectiledeathtype |= HITTYPE_BOUNCE; - } -} - -void W_Hagar_Attack (void) -{ - entity missile; - - W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_hagar_primary_ammo, autocvar_g_balance_hagar_reload_ammo); - - W_SetupShot (self, FALSE, 2, "weapons/hagar_fire.wav", CH_WEAPON_A, autocvar_g_balance_hagar_primary_damage); - - pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - missile = spawn (); - missile.owner = missile.realowner = self; - missile.classname = "missile"; - missile.bot_dodge = TRUE; - missile.bot_dodgerating = autocvar_g_balance_hagar_primary_damage; - - missile.takedamage = DAMAGE_YES; - missile.health = autocvar_g_balance_hagar_primary_health; - missile.damageforcescale = autocvar_g_balance_hagar_primary_damageforcescale; - missile.event_damage = W_Hagar_Damage; - missile.damagedbycontents = TRUE; - - missile.touch = W_Hagar_Touch; - missile.use = W_Hagar_Explode; - missile.think = adaptor_think2use_hittype_splash; - missile.nextthink = time + autocvar_g_balance_hagar_primary_lifetime; - PROJECTILE_MAKETRIGGER(missile); - missile.projectiledeathtype = WEP_HAGAR; - setorigin (missile, w_shotorg); - setsize(missile, '0 0 0', '0 0 0'); - - missile.movetype = MOVETYPE_FLY; - W_SETUPPROJECTILEVELOCITY(missile, g_balance_hagar_primary); - - missile.angles = vectoangles (missile.velocity); - missile.flags = FL_PROJECTILE; - missile.missile_flags = MIF_SPLASH; - - CSQCProjectile(missile, TRUE, PROJECTILE_HAGAR, TRUE); - - other = missile; MUTATOR_CALLHOOK(EditProjectile); -} - -void W_Hagar_Attack2 (void) -{ - entity missile; - - W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_hagar_secondary_ammo, autocvar_g_balance_hagar_reload_ammo); - - W_SetupShot (self, FALSE, 2, "weapons/hagar_fire.wav", CH_WEAPON_A, autocvar_g_balance_hagar_secondary_damage); - - pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - missile = spawn (); - missile.owner = missile.realowner = self; - missile.classname = "missile"; - missile.bot_dodge = TRUE; - missile.bot_dodgerating = autocvar_g_balance_hagar_secondary_damage; - - missile.takedamage = DAMAGE_YES; - missile.health = autocvar_g_balance_hagar_secondary_health; - missile.damageforcescale = autocvar_g_balance_hagar_secondary_damageforcescale; - missile.event_damage = W_Hagar_Damage; - missile.damagedbycontents = TRUE; - - missile.touch = W_Hagar_Touch2; - missile.cnt = 0; - missile.use = W_Hagar_Explode2; - missile.think = adaptor_think2use_hittype_splash; - missile.nextthink = time + autocvar_g_balance_hagar_secondary_lifetime_min + random() * autocvar_g_balance_hagar_secondary_lifetime_rand; - PROJECTILE_MAKETRIGGER(missile); - missile.projectiledeathtype = WEP_HAGAR | HITTYPE_SECONDARY; - setorigin (missile, w_shotorg); - setsize(missile, '0 0 0', '0 0 0'); - - missile.movetype = MOVETYPE_BOUNCEMISSILE; - W_SETUPPROJECTILEVELOCITY(missile, g_balance_hagar_secondary); - - missile.angles = vectoangles (missile.velocity); - missile.flags = FL_PROJECTILE; - missile.missile_flags = MIF_SPLASH; - - CSQCProjectile(missile, TRUE, PROJECTILE_HAGAR_BOUNCING, TRUE); - - other = missile; MUTATOR_CALLHOOK(EditProjectile); -} - -.float hagar_loadstep, hagar_loadblock, hagar_loadbeep, hagar_warning; -void W_Hagar_Attack2_Load_Release (void) -{ - // time to release the rockets we've loaded - - entity missile; - float counter, shots, spread_pershot; - vector s; - vector forward, right, up; - - if(!self.hagar_load) - return; - - weapon_prepareattack_do(1, autocvar_g_balance_hagar_secondary_refire); - - W_SetupShot (self, FALSE, 2, "weapons/hagar_fire.wav", CH_WEAPON_A, autocvar_g_balance_hagar_secondary_damage); - pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - forward = v_forward; - right = v_right; - up = v_up; - - shots = self.hagar_load; - missile = world; - for(counter = 0; counter < shots; ++counter) - { - missile = spawn (); - missile.owner = missile.realowner = self; - missile.classname = "missile"; - missile.bot_dodge = TRUE; - missile.bot_dodgerating = autocvar_g_balance_hagar_secondary_damage; - - missile.takedamage = DAMAGE_YES; - missile.health = autocvar_g_balance_hagar_secondary_health; - missile.damageforcescale = autocvar_g_balance_hagar_secondary_damageforcescale; - missile.event_damage = W_Hagar_Damage; - missile.damagedbycontents = TRUE; - - missile.touch = W_Hagar_Touch; // not bouncy - missile.use = W_Hagar_Explode2; - missile.think = adaptor_think2use_hittype_splash; - missile.nextthink = time + autocvar_g_balance_hagar_secondary_lifetime_min + random() * autocvar_g_balance_hagar_secondary_lifetime_rand; - PROJECTILE_MAKETRIGGER(missile); - missile.projectiledeathtype = WEP_HAGAR | HITTYPE_SECONDARY; - setorigin (missile, w_shotorg); - setsize(missile, '0 0 0', '0 0 0'); - missile.movetype = MOVETYPE_FLY; - missile.missile_flags = MIF_SPLASH; - - // per-shot spread calculation: the more shots there are, the less spread is applied (based on the bias cvar) - spread_pershot = ((shots - 1) / (autocvar_g_balance_hagar_secondary_load_max - 1)); - spread_pershot = (1 - (spread_pershot * autocvar_g_balance_hagar_secondary_load_spread_bias)); - spread_pershot = (autocvar_g_balance_hagar_secondary_spread * spread_pershot * g_weaponspreadfactor); - - // pattern spread calculation - s = '0 0 0'; - if (counter == 0) - s = '0 0 0'; - else - { - makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1))); - s_y = v_forward_x; - s_z = v_forward_y; - } - s = s * autocvar_g_balance_hagar_secondary_load_spread * g_weaponspreadfactor; - - W_SetupProjectileVelocityEx(missile, w_shotdir + right * s_y + up * s_z, v_up, autocvar_g_balance_hagar_secondary_speed, 0, 0, spread_pershot, FALSE); - - missile.angles = vectoangles (missile.velocity); - missile.flags = FL_PROJECTILE; - - CSQCProjectile(missile, TRUE, PROJECTILE_HAGAR, TRUE); - - other = missile; MUTATOR_CALLHOOK(EditProjectile); - } - - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_hagar_secondary_load_animtime, w_ready); - self.hagar_loadstep = time + autocvar_g_balance_hagar_secondary_refire * W_WeaponRateFactor(); - self.hagar_load = 0; -} - -void W_Hagar_Attack2_Load (void) -{ - // loadable hagar secondary attack, must always run each frame - - if(time < game_starttime) - return; - - float loaded, enough_ammo; - loaded = self.hagar_load >= autocvar_g_balance_hagar_secondary_load_max; - - // this is different than WR_CHECKAMMO when it comes to reloading - if(autocvar_g_balance_hagar_reload_ammo) - enough_ammo = self.(weapon_load[WEP_HAGAR]) >= autocvar_g_balance_hagar_secondary_ammo; - else - enough_ammo = self.ammo_rockets >= autocvar_g_balance_hagar_secondary_ammo; - - if(self.BUTTON_ATCK2) - { - if(self.BUTTON_ATCK && autocvar_g_balance_hagar_secondary_load_abort) - { - if(self.hagar_load) - { - // if we pressed primary fire while loading, unload all rockets and abort - self.weaponentity.state = WS_READY; - W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_hagar_secondary_ammo * self.hagar_load * -1, autocvar_g_balance_hagar_reload_ammo); // give back ammo - self.hagar_load = 0; - sound(self, CH_WEAPON_A, "weapons/hagar_beep.wav", VOL_BASE, ATTEN_NORM); - - // pause until we can load rockets again, once we re-press the alt fire button - self.hagar_loadstep = time + autocvar_g_balance_hagar_secondary_load_speed * W_WeaponRateFactor(); - - // require letting go of the alt fire button before we can load again - self.hagar_loadblock = TRUE; - } - } - else - { - // check if we can attempt to load another rocket - if(!loaded && enough_ammo) - { - if(!self.hagar_loadblock && self.hagar_loadstep < time) - { - W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_hagar_secondary_ammo, autocvar_g_balance_hagar_reload_ammo); - self.weaponentity.state = WS_INUSE; - self.hagar_load += 1; - sound(self, CH_WEAPON_B, "weapons/hagar_load.wav", VOL_BASE * 0.8, ATTEN_NORM); // sound is too loud according to most - - if (self.hagar_load >= autocvar_g_balance_hagar_secondary_load_max) - self.hagar_loadstep = time + autocvar_g_balance_hagar_secondary_load_hold * W_WeaponRateFactor(); - else - self.hagar_loadstep = time + autocvar_g_balance_hagar_secondary_load_speed * W_WeaponRateFactor(); - } - } - else if(!self.hagar_loadbeep && self.hagar_load) // prevents the beep from playing each frame - { - // if this is the last rocket we can load, play a beep sound to notify the player - sound(self, CH_WEAPON_A, "weapons/hagar_beep.wav", VOL_BASE, ATTEN_NORM); - self.hagar_loadbeep = TRUE; - } - } - } - else if(self.hagar_loadblock) - { - // the alt fire button has been released, so re-enable loading if blocked - self.hagar_loadblock = FALSE; - } - - if(self.hagar_load) - { - // play warning sound if we're about to release - if((loaded || !enough_ammo) && self.hagar_loadstep - 0.5 < time && autocvar_g_balance_hagar_secondary_load_hold >= 0) - { - if(!self.hagar_warning && self.hagar_load) // prevents the beep from playing each frame - { - // we're about to automatically release after holding time, play a beep sound to notify the player - sound(self, CH_WEAPON_A, "weapons/hagar_beep.wav", VOL_BASE, ATTEN_NORM); - self.hagar_warning = TRUE; - } - } - - // release if player let go of button or if they've held it in too long - if(!self.BUTTON_ATCK2 || ((loaded || !enough_ammo) && self.hagar_loadstep < time && autocvar_g_balance_hagar_secondary_load_hold >= 0)) - { - self.weaponentity.state = WS_READY; - W_Hagar_Attack2_Load_Release(); - } - } - else - { - self.hagar_loadbeep = FALSE; - self.hagar_warning = FALSE; - } - - // we aren't checking ammo during an attack, so we must do it here - if (!(weapon_action(self.weapon, WR_CHECKAMMO1) + weapon_action(self.weapon, WR_CHECKAMMO2))) - { - // note: this doesn't force the switch - W_SwitchToOtherWeapon(self); - return; - } -} - -void spawnfunc_weapon_hagar (void) -{ - weapon_defaultspawnfunc(WEP_HAGAR); -} - -float w_hagar(float req) -{ - float ammo_amount; - if (req == WR_AIM) - if (random()>0.15) - self.BUTTON_ATCK = bot_aim(autocvar_g_balance_hagar_primary_speed, 0, autocvar_g_balance_hagar_primary_lifetime, FALSE); - else - { - // not using secondary_speed since these are only 15% and should cause some ricochets without re-aiming - self.BUTTON_ATCK2 = bot_aim(autocvar_g_balance_hagar_primary_speed, 0, autocvar_g_balance_hagar_primary_lifetime, FALSE); - } - else if (req == WR_THINK) - { - float loadable_secondary; - loadable_secondary = (autocvar_g_balance_hagar_secondary_load && autocvar_g_balance_hagar_secondary); - - if (loadable_secondary) - W_Hagar_Attack2_Load(); // must always run each frame - if(autocvar_g_balance_hagar_reload_ammo && self.clip_load < min(autocvar_g_balance_hagar_primary_ammo, autocvar_g_balance_hagar_secondary_ammo)) // forced reload - weapon_action(self.weapon, WR_RELOAD); - else if (self.BUTTON_ATCK && !self.hagar_load && !self.hagar_loadblock) // not while secondary is loaded or awaiting reset - { - if (weapon_prepareattack(0, autocvar_g_balance_hagar_primary_refire)) - { - W_Hagar_Attack(); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_hagar_primary_refire, w_ready); - } - } - else if (self.BUTTON_ATCK2 && !loadable_secondary && autocvar_g_balance_hagar_secondary) - { - if (weapon_prepareattack(1, autocvar_g_balance_hagar_secondary_refire)) - { - W_Hagar_Attack2(); - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_hagar_secondary_refire, w_ready); - } - } - } - else if (req == WR_GONETHINK) - { - // we lost the weapon and want to prepare switching away - if(self.hagar_load) - { - self.weaponentity.state = WS_READY; - W_Hagar_Attack2_Load_Release(); - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/weapons/g_hagar.md3"); - precache_model ("models/weapons/v_hagar.md3"); - precache_model ("models/weapons/h_hagar.iqm"); - precache_sound ("weapons/hagar_fire.wav"); - precache_sound ("weapons/hagar_load.wav"); - precache_sound ("weapons/hagar_beep.wav"); - //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_HAGAR); - self.current_ammo = ammo_rockets; - self.hagar_loadblock = FALSE; - - if(self.hagar_load) - { - W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_hagar_secondary_ammo * self.hagar_load * -1, autocvar_g_balance_hagar_reload_ammo); // give back ammo if necessary - self.hagar_load = 0; - } - } - else if (req == WR_CHECKAMMO1) - { - ammo_amount = self.ammo_rockets >= autocvar_g_balance_hagar_primary_ammo; - ammo_amount += self.(weapon_load[WEP_HAGAR]) >= autocvar_g_balance_hagar_primary_ammo; - return ammo_amount; - } - else if (req == WR_CHECKAMMO2) - { - ammo_amount = self.ammo_rockets >= autocvar_g_balance_hagar_secondary_ammo; - ammo_amount += self.(weapon_load[WEP_HAGAR]) >= autocvar_g_balance_hagar_secondary_ammo; - return ammo_amount; - } - else if (req == WR_RESETPLAYER) - { - self.hagar_load = 0; - } - else if (req == WR_PLAYERDEATH) - { - // if we have any rockets loaded when we die, release them - if(self.hagar_load && autocvar_g_balance_hagar_secondary_load_releasedeath) - W_Hagar_Attack2_Load_Release(); - } - else if (req == WR_RELOAD) - { - if (!self.hagar_load) // require releasing loaded rockets first - W_Reload(min(autocvar_g_balance_hagar_primary_ammo, autocvar_g_balance_hagar_secondary_ammo), autocvar_g_balance_hagar_reload_ammo, autocvar_g_balance_hagar_reload_time, "weapons/reload.wav"); - } - else if (req == WR_SUICIDEMESSAGE) - { - return WEAPON_HAGAR_SUICIDE; - } - else if (req == WR_KILLMESSAGE) - { - if(w_deathtype & HITTYPE_SECONDARY) - return WEAPON_HAGAR_MURDER_BURST; - else - return WEAPON_HAGAR_MURDER_SPRAY; - } - return TRUE; -} -#endif -#ifdef CSQC -float w_hagar(float req) -{ - if(req == WR_IMPACTEFFECT) - { - vector org2; - org2 = w_org + w_backoff * 6; - pointparticles(particleeffectnum("hagar_explode"), org2, '0 0 0', 1); - if(!w_issilent) - { - if (w_random<0.15) - sound(self, CH_SHOTS, "weapons/hagexp1.wav", VOL_BASE, ATTEN_NORM); - else if (w_random<0.7) - sound(self, CH_SHOTS, "weapons/hagexp2.wav", VOL_BASE, ATTEN_NORM); - else - sound(self, CH_SHOTS, "weapons/hagexp3.wav", VOL_BASE, ATTEN_NORM); - } - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/hagexp1.wav"); - precache_sound("weapons/hagexp2.wav"); - precache_sound("weapons/hagexp3.wav"); - } - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_hlac.qc b/qcsrc/server/w_hlac.qc deleted file mode 100644 index 5642ef0577..0000000000 --- a/qcsrc/server/w_hlac.qc +++ /dev/null @@ -1,261 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ HLAC, -/* function */ w_hlac, -/* ammotype */ IT_CELLS, -/* impulse */ 6, -/* flags */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH, -/* rating */ BOT_PICKUP_RATING_MID, -/* model */ "hlac", -/* shortname */ "hlac", -/* fullname */ _("Heavy Laser Assault Cannon") -); -#else -#ifdef SVQC - -void W_HLAC_Touch (void) -{ - PROJECTILE_TOUCH; - - self.event_damage = func_null; - - if(self.projectiledeathtype & HITTYPE_SECONDARY) - RadiusDamage (self, self.realowner, autocvar_g_balance_hlac_secondary_damage, autocvar_g_balance_hlac_secondary_edgedamage, autocvar_g_balance_hlac_secondary_radius, world, autocvar_g_balance_hlac_secondary_force, self.projectiledeathtype, other); - else - RadiusDamage (self, self.realowner, autocvar_g_balance_hlac_primary_damage, autocvar_g_balance_hlac_primary_edgedamage, autocvar_g_balance_hlac_primary_radius, world, autocvar_g_balance_hlac_primary_force, self.projectiledeathtype, other); - - remove (self); -} - -void W_HLAC_Attack (void) -{ - entity missile; - float spread; - - W_DecreaseAmmo(ammo_cells, autocvar_g_balance_hlac_primary_ammo, autocvar_g_balance_hlac_reload_ammo); - - spread = autocvar_g_balance_hlac_primary_spread_min + (autocvar_g_balance_hlac_primary_spread_add * self.misc_bulletcounter); - spread = min(spread,autocvar_g_balance_hlac_primary_spread_max); - if(self.crouch) - spread = spread * autocvar_g_balance_hlac_primary_spread_crouchmod; - - W_SetupShot (self, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_A, autocvar_g_balance_hlac_primary_damage); - pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - if (!autocvar_g_norecoil) - { - self.punchangle_x = random () - 0.5; - self.punchangle_y = random () - 0.5; - } - - missile = spawn (); - missile.owner = missile.realowner = self; - missile.classname = "hlacbolt"; - missile.bot_dodge = TRUE; - - missile.bot_dodgerating = autocvar_g_balance_hlac_primary_damage; - - missile.movetype = MOVETYPE_FLY; - PROJECTILE_MAKETRIGGER(missile); - - setorigin (missile, w_shotorg); - setsize(missile, '0 0 0', '0 0 0'); - - W_SetupProjectileVelocity(missile, autocvar_g_balance_hlac_primary_speed, spread); - //missile.angles = vectoangles (missile.velocity); // csqc - - missile.touch = W_HLAC_Touch; - missile.think = SUB_Remove; - - missile.nextthink = time + autocvar_g_balance_hlac_primary_lifetime; - - missile.flags = FL_PROJECTILE; - missile.projectiledeathtype = WEP_HLAC; - - CSQCProjectile(missile, TRUE, PROJECTILE_HLAC, TRUE); - - other = missile; MUTATOR_CALLHOOK(EditProjectile); -} - -void W_HLAC_Attack2f (void) -{ - entity missile; - float spread; - - spread = autocvar_g_balance_hlac_secondary_spread; - - - if(self.crouch) - spread = spread * autocvar_g_balance_hlac_secondary_spread_crouchmod; - - W_SetupShot (self, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_A, autocvar_g_balance_hlac_secondary_damage); - pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - missile = spawn (); - missile.owner = missile.realowner = self; - missile.classname = "hlacbolt"; - missile.bot_dodge = TRUE; - - missile.bot_dodgerating = autocvar_g_balance_hlac_secondary_damage; - - missile.movetype = MOVETYPE_FLY; - PROJECTILE_MAKETRIGGER(missile); - - setorigin (missile, w_shotorg); - setsize(missile, '0 0 0', '0 0 0'); - - W_SetupProjectileVelocity(missile, autocvar_g_balance_hlac_secondary_speed, spread); - //missile.angles = vectoangles (missile.velocity); // csqc - - missile.touch = W_HLAC_Touch; - missile.think = SUB_Remove; - - missile.nextthink = time + autocvar_g_balance_hlac_secondary_lifetime; - - missile.flags = FL_PROJECTILE; - missile.missile_flags = MIF_SPLASH; - missile.projectiledeathtype = WEP_HLAC | HITTYPE_SECONDARY; - - CSQCProjectile(missile, TRUE, PROJECTILE_HLAC, TRUE); - - other = missile; MUTATOR_CALLHOOK(EditProjectile); -} - -void W_HLAC_Attack2 (void) -{ - float i; - - W_DecreaseAmmo(ammo_cells, autocvar_g_balance_hlac_secondary_ammo, autocvar_g_balance_hlac_reload_ammo); - - for(i=autocvar_g_balance_hlac_secondary_shots;i>0;--i) - W_HLAC_Attack2f(); - - if (!autocvar_g_norecoil) - { - self.punchangle_x = random () - 0.5; - self.punchangle_y = random () - 0.5; - } -} - -// weapon frames -void HLAC_fire1_02() -{ - if(self.weapon != self.switchweapon) // abort immediately if switching - { - w_ready(); - return; - } - - if (self.BUTTON_ATCK) - { - if (!weapon_action(self.weapon, WR_CHECKAMMO1)) - if (!(self.items & IT_UNLIMITED_WEAPON_AMMO)) - { - W_SwitchWeapon_Force(self, w_getbestweapon(self)); - w_ready(); - return; - } - - ATTACK_FINISHED(self) = time + autocvar_g_balance_hlac_primary_refire * W_WeaponRateFactor(); - W_HLAC_Attack(); - self.misc_bulletcounter = self.misc_bulletcounter + 1; - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_hlac_primary_refire, HLAC_fire1_02); - } - else - { - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_hlac_primary_animtime, w_ready); - } -} - -void spawnfunc_weapon_hlac (void) -{ - weapon_defaultspawnfunc(WEP_HLAC); -} - -float w_hlac(float req) -{ - float ammo_amount; - if (req == WR_AIM) - self.BUTTON_ATCK = bot_aim(autocvar_g_balance_hlac_primary_speed, 0, autocvar_g_balance_hlac_primary_lifetime, FALSE); - else if (req == WR_THINK) - { - if(autocvar_g_balance_hlac_reload_ammo && self.clip_load < min(autocvar_g_balance_hlac_primary_ammo, autocvar_g_balance_hlac_secondary_ammo)) // forced reload - weapon_action(self.weapon, WR_RELOAD); - else if (self.BUTTON_ATCK) - { - if (weapon_prepareattack(0, autocvar_g_balance_hlac_primary_refire)) - { - self.misc_bulletcounter = 0; - W_HLAC_Attack(); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_hlac_primary_refire, HLAC_fire1_02); - } - } - - else if (self.BUTTON_ATCK2 && autocvar_g_balance_hlac_secondary) - { - if (weapon_prepareattack(1, autocvar_g_balance_hlac_secondary_refire)) - { - W_HLAC_Attack2(); - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_hlac_secondary_animtime, w_ready); - } - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/weapons/g_hlac.md3"); - precache_model ("models/weapons/v_hlac.md3"); - precache_model ("models/weapons/h_hlac.iqm"); - precache_sound ("weapons/lasergun_fire.wav"); - //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else - - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_HLAC); - self.current_ammo = ammo_cells; - } - else if (req == WR_CHECKAMMO1) - { - ammo_amount = self.ammo_cells >= autocvar_g_balance_hlac_primary_ammo; - ammo_amount += self.(weapon_load[WEP_HLAC]) >= autocvar_g_balance_hlac_primary_ammo; - return ammo_amount; - } - else if (req == WR_CHECKAMMO2) - { - ammo_amount = self.ammo_cells >= autocvar_g_balance_hlac_secondary_ammo; - ammo_amount += self.(weapon_load[WEP_HLAC]) >= autocvar_g_balance_hlac_secondary_ammo; - return ammo_amount; - } - else if (req == WR_RELOAD) - { - W_Reload(min(autocvar_g_balance_hlac_primary_ammo, autocvar_g_balance_hlac_secondary_ammo), autocvar_g_balance_hlac_reload_ammo, autocvar_g_balance_hlac_reload_time, "weapons/reload.wav"); - } - else if (req == WR_SUICIDEMESSAGE) - { - return WEAPON_HLAC_SUICIDE; - } - else if (req == WR_KILLMESSAGE) - { - return WEAPON_HLAC_MURDER; - } - return TRUE; -} -#endif -#ifdef CSQC -float w_hlac(float req) -{ - if(req == WR_IMPACTEFFECT) - { - vector org2; - org2 = w_org + w_backoff * 6; - pointparticles(particleeffectnum("laser_impact"), org2, w_backoff * 1000, 1); - if(!w_issilent) - sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTEN_NORM); - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/laserimpact.wav"); - } - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_hook.qc b/qcsrc/server/w_hook.qc deleted file mode 100644 index f0310c702e..0000000000 --- a/qcsrc/server/w_hook.qc +++ /dev/null @@ -1,307 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ HOOK, -/* function */ w_hook, -/* ammotype */ IT_CELLS|IT_FUEL, -/* impulse */ 0, -/* flags */ WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH, -/* rating */ 0, -/* model */ "hookgun", -/* shortname */ "hook", -/* fullname */ _("Grappling Hook") -); -#else -#ifdef SVQC -.float dmg; -.float dmg_edge; -.float dmg_radius; -.float dmg_force; -.float dmg_power; -.float dmg_duration; -.float dmg_last; -.float hook_refire; -.float hook_time_hooked; -.float hook_time_fueldecrease; - -void W_Hook_ExplodeThink (void) -{ - float dt, dmg_remaining_next, f; - - dt = time - self.teleport_time; - dmg_remaining_next = pow(bound(0, 1 - dt / self.dmg_duration, 1), self.dmg_power); - - f = self.dmg_last - dmg_remaining_next; - self.dmg_last = dmg_remaining_next; - - RadiusDamage (self, self.realowner, self.dmg * f, self.dmg_edge * f, self.dmg_radius, self.realowner, self.dmg_force * f, self.projectiledeathtype, world); - self.projectiledeathtype |= HITTYPE_BOUNCE; - //RadiusDamage (self, world, self.dmg * f, self.dmg_edge * f, self.dmg_radius, world, self.dmg_force * f, self.projectiledeathtype, world); - - if(dt < self.dmg_duration) - self.nextthink = time + 0.05; // soon - else - remove(self); -} - -void W_Hook_Explode2 (void) -{ - self.event_damage = func_null; - self.touch = func_null; - self.effects |= EF_NODRAW; - - self.think = W_Hook_ExplodeThink; - self.nextthink = time; - self.dmg = autocvar_g_balance_hook_secondary_damage; - self.dmg_edge = autocvar_g_balance_hook_secondary_edgedamage; - self.dmg_radius = autocvar_g_balance_hook_secondary_radius; - self.dmg_force = autocvar_g_balance_hook_secondary_force; - self.dmg_power = autocvar_g_balance_hook_secondary_power; - self.dmg_duration = autocvar_g_balance_hook_secondary_duration; - self.teleport_time = time; - self.dmg_last = 1; - self.movetype = MOVETYPE_NONE; -} - -void W_Hook_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) -{ - if (self.health <= 0) - return; - - if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions - return; // g_projectiles_damage says to halt - - self.health = self.health - damage; - - if (self.health <= 0) - W_PrepareExplosionByDamage(self.realowner, W_Hook_Explode2); -} - -void W_Hook_Touch2 (void) -{ - PROJECTILE_TOUCH; - self.use(); -} - -void W_Hook_Attack2() -{ - entity gren; - - W_DecreaseAmmo(ammo_cells, autocvar_g_balance_hook_secondary_ammo, FALSE); - W_SetupShot (self, FALSE, 4, "weapons/hookbomb_fire.wav", CH_WEAPON_A, autocvar_g_balance_hook_secondary_damage); - - gren = spawn (); - gren.owner = gren.realowner = self; - gren.classname = "hookbomb"; - gren.bot_dodge = TRUE; - gren.bot_dodgerating = autocvar_g_balance_hook_secondary_damage; - gren.movetype = MOVETYPE_TOSS; - PROJECTILE_MAKETRIGGER(gren); - gren.projectiledeathtype = WEP_HOOK | HITTYPE_SECONDARY; - setorigin(gren, w_shotorg); - setsize(gren, '0 0 0', '0 0 0'); - - gren.nextthink = time + autocvar_g_balance_hook_secondary_lifetime; - gren.think = adaptor_think2use_hittype_splash; - gren.use = W_Hook_Explode2; - gren.touch = W_Hook_Touch2; - - gren.takedamage = DAMAGE_YES; - gren.health = autocvar_g_balance_hook_secondary_health; - gren.damageforcescale = autocvar_g_balance_hook_secondary_damageforcescale; - gren.event_damage = W_Hook_Damage; - gren.damagedbycontents = TRUE; - gren.missile_flags = MIF_SPLASH | MIF_ARC; - - gren.velocity = '0 0 1' * autocvar_g_balance_hook_secondary_speed; - if(autocvar_g_projectiles_newton_style) - gren.velocity = gren.velocity + self.velocity; - - gren.gravity = autocvar_g_balance_hook_secondary_gravity; - //W_SetupProjectileVelocity(gren); // just falling down! - - gren.angles = '0 0 0'; - gren.flags = FL_PROJECTILE; - - CSQCProjectile(gren, TRUE, PROJECTILE_HOOKBOMB, TRUE); - - other = gren; MUTATOR_CALLHOOK(EditProjectile); -} - -void spawnfunc_weapon_hook (void) -{ - if(g_grappling_hook) // offhand hook - { - startitem_failed = TRUE; - remove(self); - return; - } - weapon_defaultspawnfunc(WEP_HOOK); -} - -float w_hook(float req) -{ - float hooked_time_max, hooked_fuel; - - if (req == WR_AIM) - { - // ... sorry ... - } - else if (req == WR_THINK) - { - if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK)) - { - if(!self.hook) - if (!(self.hook_state & HOOK_WAITING_FOR_RELEASE)) - if (!(self.hook_state & HOOK_FIRING)) - if (time > self.hook_refire) - if (weapon_prepareattack(0, -1)) - { - W_DecreaseAmmo(ammo_fuel, autocvar_g_balance_hook_primary_fuel, FALSE); - self.hook_state |= HOOK_FIRING; - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_hook_primary_animtime, w_ready); - } - } - - if (self.BUTTON_ATCK2) - { - if (weapon_prepareattack(1, autocvar_g_balance_hook_secondary_refire)) - { - W_Hook_Attack2(); - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_hook_secondary_animtime, w_ready); - } - } - - if(self.hook) - { - // if hooked, no bombs, and increase the timer - self.hook_refire = max(self.hook_refire, time + autocvar_g_balance_hook_primary_refire * W_WeaponRateFactor()); - - // hook also inhibits health regeneration, but only for 1 second - if (!(self.items & IT_UNLIMITED_WEAPON_AMMO)) - self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen); - } - - if(self.hook && self.hook.state == 1) - { - hooked_time_max = autocvar_g_balance_hook_primary_hooked_time_max; - if (hooked_time_max > 0) - { - if ( time > self.hook_time_hooked + hooked_time_max ) - self.hook_state |= HOOK_REMOVING; - } - - hooked_fuel = autocvar_g_balance_hook_primary_hooked_fuel; - if (hooked_fuel > 0) - { - if ( time > self.hook_time_fueldecrease ) - { - if (!(self.items & IT_UNLIMITED_WEAPON_AMMO)) - { - if ( self.ammo_fuel >= (time - self.hook_time_fueldecrease) * hooked_fuel ) - { - W_DecreaseAmmo(ammo_fuel, (time - self.hook_time_fueldecrease) * hooked_fuel, FALSE); - self.hook_time_fueldecrease = time; - // decrease next frame again - } - else - { - self.ammo_fuel = 0; - self.hook_state |= HOOK_REMOVING; - W_SwitchWeapon_Force(self, w_getbestweapon(self)); - } - } - } - } - } - else - { - self.hook_time_hooked = time; - self.hook_time_fueldecrease = time + autocvar_g_balance_hook_primary_hooked_time_free; - } - - if (self.BUTTON_CROUCH) - { - self.hook_state &= ~HOOK_PULLING; - if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK)) - self.hook_state &= ~HOOK_RELEASING; - else - self.hook_state |= HOOK_RELEASING; - } - else - { - self.hook_state |= HOOK_PULLING; - self.hook_state &= ~HOOK_RELEASING; - - if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK)) - { - // already fired - if(self.hook) - self.hook_state |= HOOK_WAITING_FOR_RELEASE; - } - else - { - self.hook_state |= HOOK_REMOVING; - self.hook_state &= ~HOOK_WAITING_FOR_RELEASE; - } - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/weapons/g_hookgun.md3"); - precache_model ("models/weapons/v_hookgun.md3"); - precache_model ("models/weapons/h_hookgun.iqm"); - precache_sound ("weapons/hook_impact.wav"); // done by g_hook.qc - precache_sound ("weapons/hook_fire.wav"); - precache_sound ("weapons/hookbomb_fire.wav"); - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_HOOK); - self.current_ammo = ammo_fuel; - self.hook_state &= ~HOOK_WAITING_FOR_RELEASE; - } - else if (req == WR_CHECKAMMO1) - { - if(self.hook) - return self.ammo_fuel > 0; - else - return self.ammo_fuel >= autocvar_g_balance_hook_primary_fuel; - } - else if (req == WR_CHECKAMMO2) - { - return self.ammo_cells >= autocvar_g_balance_hook_secondary_ammo; - } - else if (req == WR_RESETPLAYER) - { - self.hook_refire = time; - } - else if (req == WR_SUICIDEMESSAGE) - { - return FALSE; - } - else if (req == WR_KILLMESSAGE) - { - return WEAPON_HOOK_MURDER; - } - return TRUE; -} -#endif -#ifdef CSQC -float w_hook(float req) -{ - if(req == WR_IMPACTEFFECT) - { - vector org2; - org2 = w_org + w_backoff * 2; - pointparticles(particleeffectnum("hookbomb_explode"), org2, '0 0 0', 1); - if(!w_issilent) - sound(self, CH_SHOTS, "weapons/hookbomb_impact.wav", VOL_BASE, ATTEN_NORM); - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/hookbomb_impact.wav"); - } - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_laser.qc b/qcsrc/server/w_laser.qc deleted file mode 100644 index 0a0704638c..0000000000 --- a/qcsrc/server/w_laser.qc +++ /dev/null @@ -1,322 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ LASER, -/* function */ w_laser, -/* ammotype */ 0, -/* impulse */ 1, -/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH, -/* rating */ 0, -/* model */ "laser", -/* shortname */ "laser", -/* fullname */ _("Laser") -); -#else -#ifdef SVQC -void(float imp) W_SwitchWeapon; -void() W_LastWeapon; - -void W_Laser_Touch (void) -{ - PROJECTILE_TOUCH; - - self.event_damage = func_null; - if (self.dmg) - RadiusDamage (self, self.realowner, autocvar_g_balance_laser_secondary_damage, autocvar_g_balance_laser_secondary_edgedamage, autocvar_g_balance_laser_secondary_radius, world, autocvar_g_balance_laser_secondary_force, self.projectiledeathtype, other); - else - RadiusDamage (self, self.realowner, autocvar_g_balance_laser_primary_damage, autocvar_g_balance_laser_primary_edgedamage, autocvar_g_balance_laser_primary_radius, world, autocvar_g_balance_laser_primary_force, self.projectiledeathtype, other); - - remove (self); -} - -void W_Laser_Think() -{ - self.movetype = MOVETYPE_FLY; - self.think = SUB_Remove; - if (self.dmg) - self.nextthink = time + autocvar_g_balance_laser_secondary_lifetime; - else - self.nextthink = time + autocvar_g_balance_laser_primary_lifetime; - CSQCProjectile(self, TRUE, PROJECTILE_LASER, TRUE); -} - -void W_Laser_Attack (float issecondary) -{ - entity missile; - vector s_forward; - float a; - - a = autocvar_g_balance_laser_primary_shotangle; - s_forward = v_forward * cos(a * DEG2RAD) + v_up * sin(a * DEG2RAD); - - if(issecondary == 1) - W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_secondary_damage); - else - W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_primary_damage); - pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - missile = spawn (); - missile.owner = missile.realowner = self; - missile.classname = "laserbolt"; - missile.dmg = 0; - missile.bot_dodge = TRUE; - missile.bot_dodgerating = autocvar_g_balance_laser_primary_damage; - - PROJECTILE_MAKETRIGGER(missile); - missile.projectiledeathtype = WEP_LASER; - - setorigin (missile, w_shotorg); - setsize(missile, '0 0 0', '0 0 0'); - - W_SETUPPROJECTILEVELOCITY(missile, g_balance_laser_primary); - missile.angles = vectoangles (missile.velocity); - //missile.glow_color = 250; // 244, 250 - //missile.glow_size = 120; - missile.touch = W_Laser_Touch; - - missile.flags = FL_PROJECTILE; - missile.missile_flags = MIF_SPLASH; - - missile.think = W_Laser_Think; - missile.nextthink = time + autocvar_g_balance_laser_primary_delay; - - other = missile; MUTATOR_CALLHOOK(EditProjectile); - - if(time >= missile.nextthink) - { - entity oldself; - oldself = self; - self = missile; - self.think(); - self = oldself; - } -} - -.vector hook_start, hook_end; -float gauntletbeam_send(entity to, float sf) -{ - WriteByte(MSG_ENTITY, ENT_CLIENT_GAUNTLET); - sf = sf & 0x7F; - if(sound_allowed(MSG_BROADCAST, self.realowner)) - sf |= 0x80; - WriteByte(MSG_ENTITY, sf); - if(sf & 1) - { - WriteByte(MSG_ENTITY, num_for_edict(self.realowner)); - } - if(sf & 2) - { - WriteCoord(MSG_ENTITY, self.hook_start_x); - WriteCoord(MSG_ENTITY, self.hook_start_y); - WriteCoord(MSG_ENTITY, self.hook_start_z); - } - if(sf & 4) - { - WriteCoord(MSG_ENTITY, self.hook_end_x); - WriteCoord(MSG_ENTITY, self.hook_end_y); - WriteCoord(MSG_ENTITY, self.hook_end_z); - } - return TRUE; -} -.entity gauntletbeam; -.float prevgauntletfire; -entity lgbeam_owner_ent; -void gauntletbeam_think() -{ - float damage, myforce, myradius; - damage = autocvar_g_balance_laser_secondary_damage; - myforce = autocvar_g_balance_laser_secondary_force; - myradius = autocvar_g_balance_laser_secondary_radius; - - self.realowner.prevgauntletfire = time; - if (self.realowner.weaponentity.state != WS_INUSE || self != self.realowner.gauntletbeam || self.realowner.deadflag != DEAD_NO || !self.realowner.BUTTON_ATCK2) - { - remove(self); - return; - } - - self.nextthink = time; - - makevectors(self.realowner.v_angle); - - float dt; - dt = frametime; - - W_SetupShot_Range(self.realowner, TRUE, 0, "", 0, damage * dt, myradius); - if(!lgbeam_owner_ent) - { - lgbeam_owner_ent = spawn(); - lgbeam_owner_ent.classname = "lgbeam_owner_ent"; - } - WarpZone_traceline_antilag(lgbeam_owner_ent, w_shotorg, w_shotend, MOVE_NORMAL, lgbeam_owner_ent, ANTILAG_LATENCY(self.owner)); - - // apply the damage - if(trace_ent) - { - vector force; - force = w_shotdir * myforce; - if(accuracy_isgooddamage(self.owner, trace_ent)) - accuracy_add(self.owner, WEP_LASER, 0, damage * dt); - Damage (trace_ent, self.owner, self.owner, damage * dt, WEP_LASER | HITTYPE_SECONDARY, trace_endpos, force * dt); - } - - // draw effect - if(w_shotorg != self.hook_start) - { - self.SendFlags |= 2; - self.hook_start = w_shotorg; - } - if(w_shotend != self.hook_end) - { - self.SendFlags |= 4; - self.hook_end = w_shotend; - } -} - -// experimental gauntlet -void W_Laser_Attack2 () -{ - // only play fire sound if 0.5 sec has passed since player let go the fire button - if(time - self.prevgauntletfire > 0.5) - { - sound (self, CH_WEAPON_A, "weapons/gauntlet_fire.wav", VOL_BASE, ATTEN_NORM); - } - - entity beam, oldself; - - self.gauntletbeam = beam = spawn(); - beam.solid = SOLID_NOT; - beam.think = gauntletbeam_think; - beam.owner = self; - beam.movetype = MOVETYPE_NONE; - beam.shot_spread = 0; - beam.bot_dodge = TRUE; - beam.bot_dodgerating = autocvar_g_balance_laser_primary_damage; - Net_LinkEntity(beam, FALSE, 0, gauntletbeam_send); - - oldself = self; - self = beam; - self.think(); - self = oldself; -} - -void LaserInit() -{ - weapon_action(WEP_LASER, WR_PRECACHE); - gauntlet_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 1); - gauntlet_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 2); - gauntlet_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 3); - gauntlet_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 4); -} - -void spawnfunc_weapon_laser (void) -{ - weapon_defaultspawnfunc(WEP_LASER); -} - -float w_laser(float req) -{ - float r1; - float r2; - if (req == WR_AIM) - { - if(autocvar_g_balance_laser_secondary) - { - r1 = autocvar_g_balance_laser_primary_damage; - r2 = autocvar_g_balance_laser_secondary_damage; - if (random() * (r2 + r1) > r1) - self.BUTTON_ATCK2 = bot_aim(autocvar_g_balance_laser_secondary_speed, 0, autocvar_g_balance_laser_secondary_lifetime, FALSE); - else - self.BUTTON_ATCK = bot_aim(autocvar_g_balance_laser_primary_speed, 0, autocvar_g_balance_laser_primary_lifetime, FALSE); - } - else - self.BUTTON_ATCK = bot_aim(autocvar_g_balance_laser_primary_speed, 0, autocvar_g_balance_laser_primary_lifetime, FALSE); - } - else if (req == WR_THINK) - { - if(autocvar_g_balance_laser_reload_ammo && self.clip_load < 1) // forced reload - weapon_action(self.weapon, WR_RELOAD); - else if (self.BUTTON_ATCK) - { - if (weapon_prepareattack(0, autocvar_g_balance_laser_primary_refire)) - { - W_DecreaseAmmo(ammo_none, 1, TRUE); - - W_Laser_Attack(0); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_laser_primary_animtime, w_ready); - } - } - else if (self.BUTTON_ATCK2) - { - if(autocvar_g_balance_laser_secondary) - { - W_DecreaseAmmo(ammo_none, 1, TRUE); - - if (weapon_prepareattack(0, 0)) - { - W_Laser_Attack2(); - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_laser_secondary_animtime, w_ready); - } - } - else - { - if(self.switchweapon == WEP_LASER) // don't do this if already switching - W_LastWeapon(); - } - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/weapons/g_laser.md3"); - precache_model ("models/weapons/v_laser.md3"); - precache_model ("models/weapons/h_laser.iqm"); - precache_sound ("weapons/lasergun_fire.wav"); - precache_sound ("weapons/gauntlet_fire.wav"); - //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_LASER); - self.current_ammo = ammo_none; - } - else if (req == WR_CHECKAMMO1) - { - return TRUE; - } - else if (req == WR_CHECKAMMO2) - { - return TRUE; - } - else if (req == WR_RELOAD) - { - W_Reload(0, autocvar_g_balance_laser_reload_ammo, autocvar_g_balance_laser_reload_time, "weapons/reload.wav"); - } - else if (req == WR_SUICIDEMESSAGE) - { - return WEAPON_LASER_SUICIDE; - } - else if (req == WR_KILLMESSAGE) - { - return WEAPON_LASER_MURDER; - } - return TRUE; -} -#endif -#ifdef CSQC -float w_laser(float req) -{ - if(req == WR_IMPACTEFFECT) - { - vector org2; - org2 = w_org + w_backoff * 6; - pointparticles(particleeffectnum("laser_impact"), org2, w_backoff * 1000, 1); - if(!w_issilent) - sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTEN_NORM); - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/laserimpact.wav"); - } - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_laser.qh b/qcsrc/server/w_laser.qh deleted file mode 100644 index 0f2c137030..0000000000 --- a/qcsrc/server/w_laser.qh +++ /dev/null @@ -1,2 +0,0 @@ -void LaserInit(); -vector gauntlet_shotorigin[4]; diff --git a/qcsrc/server/w_minelayer.qc b/qcsrc/server/w_minelayer.qc deleted file mode 100644 index e91fb5fb6b..0000000000 --- a/qcsrc/server/w_minelayer.qc +++ /dev/null @@ -1,563 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ MINE_LAYER, -/* function */ w_minelayer, -/* ammotype */ IT_ROCKETS, -/* impulse */ 4, -/* flags */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH, -/* rating */ BOT_PICKUP_RATING_HIGH, -/* model */ "minelayer", -/* shortname */ "minelayer", -/* fullname */ _("Mine Layer") -); -#else -#ifdef SVQC -void W_Mine_Think (void); -.float minelayer_detonate, mine_explodeanyway; -.float mine_time; -.vector mine_orientation; - -void spawnfunc_weapon_minelayer (void) -{ - weapon_defaultspawnfunc(WEP_MINE_LAYER); -} - -void W_Mine_Stick (entity to) -{ - spamsound (self, CH_SHOTS, "weapons/mine_stick.wav", VOL_BASE, ATTEN_NORM); - - // in order for mines to face properly when sticking to the ground, they must be a server side entity rather than a csqc projectile - - entity newmine; - newmine = spawn(); - newmine.classname = self.classname; - - newmine.bot_dodge = self.bot_dodge; - newmine.bot_dodgerating = self.bot_dodgerating; - - newmine.owner = self.owner; - newmine.realowner = self.realowner; - setsize(newmine, '-4 -4 -4', '4 4 4'); - setorigin(newmine, self.origin); - setmodel(newmine, "models/mine.md3"); - newmine.angles = vectoangles(-trace_plane_normal); // face against the surface - - newmine.mine_orientation = -trace_plane_normal; - - newmine.takedamage = self.takedamage; - newmine.damageforcescale = self.damageforcescale; - newmine.health = self.health; - newmine.event_damage = self.event_damage; - newmine.spawnshieldtime = self.spawnshieldtime; - newmine.damagedbycontents = TRUE; - - newmine.movetype = MOVETYPE_NONE; // lock the mine in place - newmine.projectiledeathtype = self.projectiledeathtype; - - newmine.mine_time = self.mine_time; - - newmine.touch = func_null; - newmine.think = W_Mine_Think; - newmine.nextthink = time; - newmine.cnt = self.cnt; - newmine.flags = self.flags; - - remove(self); - self = newmine; - - if(to) - SetMovetypeFollow(self, to); -} - -void W_Mine_Explode () -{ - if(other.takedamage == DAMAGE_AIM) - if(IS_PLAYER(other)) - if(DIFF_TEAM(self.realowner, other)) - if(other.deadflag == DEAD_NO) - if(IsFlying(other)) - Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); - - self.event_damage = func_null; - self.takedamage = DAMAGE_NO; - - RadiusDamage (self, self.realowner, autocvar_g_balance_minelayer_damage, autocvar_g_balance_minelayer_edgedamage, autocvar_g_balance_minelayer_radius, world, autocvar_g_balance_minelayer_force, self.projectiledeathtype, other); - - if (self.realowner.weapon == WEP_MINE_LAYER) - { - entity oldself; - oldself = self; - self = self.realowner; - if (!weapon_action(WEP_MINE_LAYER, WR_CHECKAMMO1)) - { - self.cnt = WEP_MINE_LAYER; - ATTACK_FINISHED(self) = time; - self.switchweapon = w_getbestweapon(self); - } - self = oldself; - } - self.realowner.minelayer_mines -= 1; - remove (self); -} - -void W_Mine_DoRemoteExplode () -{ - self.event_damage = func_null; - self.takedamage = DAMAGE_NO; - - if(self.movetype == MOVETYPE_NONE || self.movetype == MOVETYPE_FOLLOW) - self.velocity = self.mine_orientation; // particle fx and decals need .velocity - - RadiusDamage (self, self.realowner, autocvar_g_balance_minelayer_remote_damage, autocvar_g_balance_minelayer_remote_edgedamage, autocvar_g_balance_minelayer_remote_radius, world, autocvar_g_balance_minelayer_remote_force, self.projectiledeathtype | HITTYPE_BOUNCE, world); - - if (self.realowner.weapon == WEP_MINE_LAYER) - { - entity oldself; - oldself = self; - self = self.realowner; - if (!weapon_action(WEP_MINE_LAYER, WR_CHECKAMMO1)) - { - self.cnt = WEP_MINE_LAYER; - ATTACK_FINISHED(self) = time; - self.switchweapon = w_getbestweapon(self); - } - self = oldself; - } - self.realowner.minelayer_mines -= 1; - remove (self); -} - -void W_Mine_RemoteExplode () -{ - if(self.realowner.deadflag == DEAD_NO) - if((self.spawnshieldtime >= 0) - ? (time >= self.spawnshieldtime) // timer - : (vlen(NearestPointOnBox(self.realowner, self.origin) - self.origin) > autocvar_g_balance_minelayer_remote_radius) // safety device - ) - { - W_Mine_DoRemoteExplode(); - } -} - -void W_Mine_ProximityExplode () -{ - // make sure no friend is in the mine's radius. If there is any, explosion is delayed until he's at a safe distance - if(autocvar_g_balance_minelayer_protection && self.mine_explodeanyway == 0) - { - entity head; - head = findradius(self.origin, autocvar_g_balance_minelayer_radius); - while(head) - { - if(head == self.realowner || SAME_TEAM(head, self.realowner)) - return; - head = head.chain; - } - } - - self.mine_time = 0; - W_Mine_Explode(); -} - -float W_Mine_Count(entity e) -{ - float minecount = 0; - entity mine; - for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.realowner == e) - minecount += 1; - - return minecount; -} - -void W_Mine_Think (void) -{ - entity head; - - self.nextthink = time; - - if(self.movetype == MOVETYPE_FOLLOW) - { - if(LostMovetypeFollow(self)) - { - UnsetMovetypeFollow(self); - self.movetype = MOVETYPE_NONE; - } - } - - // our lifetime has expired, it's time to die - mine_time just allows us to play a sound for this - // TODO: replace this mine_trigger.wav sound with a real countdown - if ((time > self.cnt) && (!self.mine_time)) - { - if(autocvar_g_balance_minelayer_lifetime_countdown > 0) - spamsound (self, CH_SHOTS, "weapons/mine_trigger.wav", VOL_BASE, ATTEN_NORM); - self.mine_time = time + autocvar_g_balance_minelayer_lifetime_countdown; - self.mine_explodeanyway = 1; // make the mine super aggressive -- Samual: Rather, make it not care if a team mate is near. - } - - // a player's mines shall explode if he disconnects or dies - // TODO: Do this on team change too -- Samual: But isn't a player killed when they switch teams? - if(!IS_PLAYER(self.realowner) || self.realowner.deadflag != DEAD_NO || self.realowner.frozen) - { - other = world; - self.projectiledeathtype |= HITTYPE_BOUNCE; - W_Mine_Explode(); - return; - } - - // set the mine for detonation when a foe gets close enough - head = findradius(self.origin, autocvar_g_balance_minelayer_proximityradius); - while(head) - { - if(IS_PLAYER(head) && head.deadflag == DEAD_NO && !head.frozen) - if(head != self.realowner && DIFF_TEAM(head, self.realowner)) // don't trigger for team mates - if(!self.mine_time) - { - spamsound (self, CH_SHOTS, "weapons/mine_trigger.wav", VOL_BASE, ATTEN_NORM); - self.mine_time = time + autocvar_g_balance_minelayer_time; - } - head = head.chain; - } - - // explode if it's time to - if(self.mine_time && time >= self.mine_time) - { - W_Mine_ProximityExplode(); - return; - } - - // remote detonation - if (self.realowner.weapon == WEP_MINE_LAYER) - if (self.realowner.deadflag == DEAD_NO) - if (self.minelayer_detonate) - W_Mine_RemoteExplode(); -} - -void W_Mine_Touch (void) -{ - if(self.movetype == MOVETYPE_NONE || self.movetype == MOVETYPE_FOLLOW) - return; // we're already a stuck mine, why do we get called? TODO does this even happen? - - if(WarpZone_Projectile_Touch()) - { - if(wasfreed(self)) - self.realowner.minelayer_mines -= 1; - return; - } - - if(other && IS_PLAYER(other) && other.deadflag == DEAD_NO) - { - // hit a player - // don't stick - } - else - { - W_Mine_Stick(other); - } -} - -void W_Mine_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) -{ - if (self.health <= 0) - return; - - float is_from_enemy = (inflictor.realowner != self.realowner); - - if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, (is_from_enemy ? 1 : -1))) - return; // g_projectiles_damage says to halt - - self.health = self.health - damage; - self.angles = vectoangles(self.velocity); - - if (self.health <= 0) - W_PrepareExplosionByDamage(attacker, W_Mine_Explode); -} - -void W_Mine_Attack (void) -{ - entity mine; - entity flash; - - // scan how many mines we placed, and return if we reached our limit - if(autocvar_g_balance_minelayer_limit) - { - if(self.minelayer_mines >= autocvar_g_balance_minelayer_limit) - { - // the refire delay keeps this message from being spammed - sprint(self, strcat("minelayer: You cannot place more than ^2", ftos(autocvar_g_balance_minelayer_limit), " ^7mines at a time\n") ); - play2(self, "weapons/unavailable.wav"); - return; - } - } - - W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_minelayer_ammo, autocvar_g_balance_minelayer_reload_ammo); - - W_SetupShot_ProjectileSize (self, '-4 -4 -4', '4 4 4', FALSE, 5, "weapons/mine_fire.wav", CH_WEAPON_A, autocvar_g_balance_minelayer_damage); - pointparticles(particleeffectnum("rocketlauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - mine = WarpZone_RefSys_SpawnSameRefSys(self); - mine.owner = mine.realowner = self; - if(autocvar_g_balance_minelayer_detonatedelay >= 0) - mine.spawnshieldtime = time + autocvar_g_balance_minelayer_detonatedelay; - else - mine.spawnshieldtime = -1; - mine.classname = "mine"; - mine.bot_dodge = TRUE; - mine.bot_dodgerating = autocvar_g_balance_minelayer_damage * 2; // * 2 because it can detonate inflight which makes it even more dangerous - - mine.takedamage = DAMAGE_YES; - mine.damageforcescale = autocvar_g_balance_minelayer_damageforcescale; - mine.health = autocvar_g_balance_minelayer_health; - mine.event_damage = W_Mine_Damage; - mine.damagedbycontents = TRUE; - - mine.movetype = MOVETYPE_TOSS; - PROJECTILE_MAKETRIGGER(mine); - mine.projectiledeathtype = WEP_MINE_LAYER; - setsize (mine, '-4 -4 -4', '4 4 4'); // give it some size so it can be shot - - setorigin (mine, w_shotorg - v_forward * 4); // move it back so it hits the wall at the right point - W_SetupProjectileVelocity(mine, autocvar_g_balance_minelayer_speed, 0); - mine.angles = vectoangles (mine.velocity); - - mine.touch = W_Mine_Touch; - mine.think = W_Mine_Think; - mine.nextthink = time; - mine.cnt = time + (autocvar_g_balance_minelayer_lifetime - autocvar_g_balance_minelayer_lifetime_countdown); - mine.flags = FL_PROJECTILE; - mine.missile_flags = MIF_SPLASH | MIF_ARC | MIF_PROXY; - - CSQCProjectile(mine, TRUE, PROJECTILE_MINE, TRUE); - - // muzzle flash for 1st person view - flash = spawn (); - setmodel (flash, "models/flash.md3"); // precision set below - SUB_SetFade (flash, time, 0.1); - flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; - W_AttachToShotorg(flash, '5 0 0'); - - // common properties - - other = mine; MUTATOR_CALLHOOK(EditProjectile); - - self.minelayer_mines = W_Mine_Count(self); -} - -float W_PlacedMines(float detonate) -{ - entity mine; - float minfound = 0; - - for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.realowner == self) - { - if(detonate) - { - if(!mine.minelayer_detonate) - { - mine.minelayer_detonate = TRUE; - minfound = 1; - } - } - else - minfound = 1; - } - return minfound; -} - -float w_minelayer(float req) -{ - entity mine; - float ammo_amount; - - if (req == WR_AIM) - { - // aim and decide to fire if appropriate - if(self.minelayer_mines >= autocvar_g_balance_minelayer_limit) - self.BUTTON_ATCK = FALSE; - else - self.BUTTON_ATCK = bot_aim(autocvar_g_balance_minelayer_speed, 0, autocvar_g_balance_minelayer_lifetime, FALSE); - if(skill >= 2) // skill 0 and 1 bots won't detonate mines! - { - // decide whether to detonate mines - entity targetlist, targ; - float edgedamage, coredamage, edgeradius, recipricoledgeradius, d; - float selfdamage, teamdamage, enemydamage; - edgedamage = autocvar_g_balance_minelayer_edgedamage; - coredamage = autocvar_g_balance_minelayer_damage; - edgeradius = autocvar_g_balance_minelayer_radius; - recipricoledgeradius = 1 / edgeradius; - selfdamage = 0; - teamdamage = 0; - enemydamage = 0; - targetlist = findchainfloat(bot_attack, TRUE); - mine = find(world, classname, "mine"); - while (mine) - { - if (mine.realowner != self) - { - mine = find(mine, classname, "mine"); - continue; - } - targ = targetlist; - while (targ) - { - d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - mine.origin); - d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000); - // count potential damage according to type of target - if (targ == self) - selfdamage = selfdamage + d; - else if (targ.team == self.team && teamplay) - teamdamage = teamdamage + d; - else if (bot_shouldattack(targ)) - enemydamage = enemydamage + d; - targ = targ.chain; - } - mine = find(mine, classname, "mine"); - } - float desirabledamage; - desirabledamage = enemydamage; - if (time > self.invincible_finished && time > self.spawnshieldtime) - desirabledamage = desirabledamage - selfdamage * autocvar_g_balance_selfdamagepercent; - if (teamplay && self.team) - desirabledamage = desirabledamage - teamdamage; - - mine = find(world, classname, "mine"); - while (mine) - { - if (mine.realowner != self) - { - mine = find(mine, classname, "mine"); - continue; - } - makevectors(mine.v_angle); - targ = targetlist; - if (skill > 9) // normal players only do this for the target they are tracking - { - targ = targetlist; - while (targ) - { - if ( - (v_forward * normalize(mine.origin - targ.origin)< 0.1) - && desirabledamage > 0.1*coredamage - )self.BUTTON_ATCK2 = TRUE; - targ = targ.chain; - } - }else{ - float distance; distance= bound(300,vlen(self.origin-self.enemy.origin),30000); - //As the distance gets larger, a correct detonation gets near imposible - //Bots are assumed to use the mine spawnfunc_light to see if the mine gets near a player - if(v_forward * normalize(mine.origin - self.enemy.origin)< 0.1) - if(IS_PLAYER(self.enemy)) - if(desirabledamage >= 0.1*coredamage) - if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1)) - self.BUTTON_ATCK2 = TRUE; - // dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n"); - } - - mine = find(mine, classname, "mine"); - } - // if we would be doing at X percent of the core damage, detonate it - // but don't fire a new shot at the same time! - if (desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events - self.BUTTON_ATCK2 = TRUE; - if ((skill > 6.5) && (selfdamage > self.health)) - self.BUTTON_ATCK2 = FALSE; - //if(self.BUTTON_ATCK2 == TRUE) - // dprint(ftos(desirabledamage),"\n"); - if (self.BUTTON_ATCK2 == TRUE) self.BUTTON_ATCK = FALSE; - } - } - else if (req == WR_THINK) - { - if(autocvar_g_balance_minelayer_reload_ammo && self.clip_load < autocvar_g_balance_minelayer_ammo) // forced reload - { - // not if we're holding the minelayer without enough ammo, but can detonate existing mines - if (!(W_PlacedMines(FALSE) && self.ammo_rockets < autocvar_g_balance_minelayer_ammo)) - weapon_action(self.weapon, WR_RELOAD); - } - else if (self.BUTTON_ATCK) - { - if(weapon_prepareattack(0, autocvar_g_balance_minelayer_refire)) - { - W_Mine_Attack(); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_minelayer_animtime, w_ready); - } - } - - if (self.BUTTON_ATCK2) - { - if(W_PlacedMines(TRUE)) - sound (self, CH_WEAPON_B, "weapons/mine_det.wav", VOL_BASE, ATTEN_NORM); - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/flash.md3"); - precache_model ("models/mine.md3"); - precache_model ("models/weapons/g_minelayer.md3"); - precache_model ("models/weapons/v_minelayer.md3"); - precache_model ("models/weapons/h_minelayer.iqm"); - precache_sound ("weapons/mine_det.wav"); - precache_sound ("weapons/mine_fire.wav"); - precache_sound ("weapons/mine_stick.wav"); - precache_sound ("weapons/mine_trigger.wav"); - //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_MINE_LAYER); - self.current_ammo = ammo_rockets; - } - else if (req == WR_CHECKAMMO1) - { - // don't switch while placing a mine - if (ATTACK_FINISHED(self) <= time || self.weapon != WEP_MINE_LAYER) - { - ammo_amount = self.ammo_rockets >= autocvar_g_balance_minelayer_ammo; - ammo_amount += self.(weapon_load[WEP_MINE_LAYER]) >= autocvar_g_balance_minelayer_ammo; - return ammo_amount; - } - } - else if (req == WR_CHECKAMMO2) - { - if (W_PlacedMines(FALSE)) - return TRUE; - else - return FALSE; - } - else if (req == WR_RESETPLAYER) - { - self.minelayer_mines = 0; - } - else if (req == WR_RELOAD) - { - W_Reload(autocvar_g_balance_minelayer_ammo, autocvar_g_balance_minelayer_reload_ammo, autocvar_g_balance_minelayer_reload_time, "weapons/reload.wav"); - } - else if (req == WR_SUICIDEMESSAGE) - { - return WEAPON_MINELAYER_SUICIDE; - } - else if (req == WR_KILLMESSAGE) - { - return WEAPON_MINELAYER_MURDER; - } - return TRUE; -} -#endif -#ifdef CSQC -float w_minelayer(float req) -{ - if(req == WR_IMPACTEFFECT) - { - vector org2; - org2 = w_org + w_backoff * 12; - pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1); - if(!w_issilent) - sound(self, CH_SHOTS, "weapons/mine_exp.wav", VOL_BASE, ATTEN_NORM); - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/mine_exp.wav"); - } - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_minstanex.qc b/qcsrc/server/w_minstanex.qc deleted file mode 100644 index becb2e8b7c..0000000000 --- a/qcsrc/server/w_minstanex.qc +++ /dev/null @@ -1,213 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ MINSTANEX, -/* function */ w_minstanex, -/* ammotype */ IT_CELLS, -/* impulse */ 7, -/* flags */ WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_FLAG_SUPERWEAPON | WEP_TYPE_HITSCAN, -/* rating */ BOT_PICKUP_RATING_HIGH, -/* model */ "minstanex", -/* shortname */ "minstanex", -/* fullname */ _("MinstaNex") -); -#else -#ifdef SVQC -.float minstanex_lasthit; -.float jump_interval; - -void W_MinstaNex_Attack (void) -{ - float flying; - flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last - - W_SetupShot (self, TRUE, 0, "weapons/minstanexfire.wav", CH_WEAPON_A, 10000); - - yoda = 0; - damage_goodhits = 0; - FireRailgunBullet (w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, 10000, 800, 0, 0, 0, 0, WEP_MINSTANEX); - - if(yoda && flying) - Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); - if(damage_goodhits && self.minstanex_lasthit) - { - Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_IMPRESSIVE); - damage_goodhits = 0; // only every second time - } - - self.minstanex_lasthit = damage_goodhits; - - pointparticles(particleeffectnum("nex_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - // teamcolor / hit beam effect - vector v; - v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); - switch(self.team) - { - case NUM_TEAM_1: // Red - if(damage_goodhits) - WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED_HIT"), w_shotorg, v); - else - WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED"), w_shotorg, v); - break; - case NUM_TEAM_2: // Blue - if(damage_goodhits) - WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE_HIT"), w_shotorg, v); - else - WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE"), w_shotorg, v); - break; - case NUM_TEAM_3: // Yellow - if(damage_goodhits) - WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW_HIT"), w_shotorg, v); - else - WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW"), w_shotorg, v); - break; - case NUM_TEAM_4: // Pink - if(damage_goodhits) - WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK_HIT"), w_shotorg, v); - else - WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK"), w_shotorg, v); - break; - default: - if(damage_goodhits) - WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3_HIT"), w_shotorg, v); - else - WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3"), w_shotorg, v); - break; - } - - W_DecreaseAmmo(ammo_cells, ((g_instagib) ? 1 : autocvar_g_balance_minstanex_ammo), autocvar_g_balance_minstanex_reload_ammo); -} - -void spawnfunc_weapon_minstanex (void); // defined in t_items.qc - -float w_minstanex(float req) -{ - float ammo_amount; - float minstanex_ammo; - - // now multiple WR_s use this - minstanex_ammo = ((g_instagib) ? 1 : autocvar_g_balance_minstanex_ammo); - - if (req == WR_AIM) - { - if(self.ammo_cells > 0) - self.BUTTON_ATCK = bot_aim(1000000, 0, 1, FALSE); - else - self.BUTTON_ATCK2 = bot_aim(autocvar_g_balance_laser_primary_speed, 0, autocvar_g_balance_laser_primary_lifetime, FALSE); - } - else if (req == WR_THINK) - { - // if the laser uses load, we also consider its ammo for reloading - if(autocvar_g_balance_minstanex_reload_ammo && autocvar_g_balance_minstanex_laser_ammo && self.clip_load < min(minstanex_ammo, autocvar_g_balance_minstanex_laser_ammo)) // forced reload - weapon_action(self.weapon, WR_RELOAD); - else if(autocvar_g_balance_minstanex_reload_ammo && self.clip_load < minstanex_ammo) // forced reload - weapon_action(self.weapon, WR_RELOAD); - else if (self.BUTTON_ATCK) - { - if (weapon_prepareattack(0, autocvar_g_balance_minstanex_refire)) - { - W_MinstaNex_Attack(); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_minstanex_animtime, w_ready); - } - } - else if (self.BUTTON_ATCK2) - { - if (self.jump_interval <= time) - if (weapon_prepareattack(1, -1)) - { - // handle refire manually, so that primary and secondary can be fired without conflictions (important for instagib) - self.jump_interval = time + autocvar_g_balance_minstanex_laser_refire * W_WeaponRateFactor(); - - // decrease ammo for the laser? - if(autocvar_g_balance_minstanex_laser_ammo) - W_DecreaseAmmo(ammo_cells, autocvar_g_balance_minstanex_laser_ammo, autocvar_g_balance_minstanex_reload_ammo); - - // ugly instagib hack to reuse the fire mode of the laser - float w; - w = self.weapon; - self.weapon = WEP_LASER; - W_Laser_Attack(2); - self.weapon = w; - - // now do normal refire - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_minstanex_laser_animtime, w_ready); - } - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/nexflash.md3"); - precache_model ("models/weapons/g_minstanex.md3"); - precache_model ("models/weapons/v_minstanex.md3"); - precache_model ("models/weapons/h_minstanex.iqm"); - precache_sound ("weapons/minstanexfire.wav"); - precache_sound ("weapons/nexwhoosh1.wav"); - precache_sound ("weapons/nexwhoosh2.wav"); - precache_sound ("weapons/nexwhoosh3.wav"); - //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else - w_laser(WR_PRECACHE); - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_MINSTANEX); - self.current_ammo = ammo_cells; - self.minstanex_lasthit = 0; - } - else if (req == WR_CHECKAMMO1) - { - ammo_amount = self.ammo_cells >= minstanex_ammo; - ammo_amount += self.(weapon_load[WEP_MINSTANEX]) >= minstanex_ammo; - return ammo_amount; - } - else if (req == WR_CHECKAMMO2) - { - if(!autocvar_g_balance_minstanex_laser_ammo) - return TRUE; - ammo_amount = self.ammo_cells >= autocvar_g_balance_minstanex_laser_ammo; - ammo_amount += self.(weapon_load[WEP_MINSTANEX]) >= autocvar_g_balance_minstanex_laser_ammo; - return ammo_amount; - } - else if (req == WR_RESETPLAYER) - { - self.minstanex_lasthit = 0; - } - else if (req == WR_RELOAD) - { - float used_ammo; - if(autocvar_g_balance_minstanex_laser_ammo) - used_ammo = min(minstanex_ammo, autocvar_g_balance_minstanex_laser_ammo); - else - used_ammo = minstanex_ammo; - - W_Reload(used_ammo, autocvar_g_balance_minstanex_reload_ammo, autocvar_g_balance_minstanex_reload_time, "weapons/reload.wav"); - } - else if (req == WR_SUICIDEMESSAGE) - { - return WEAPON_THINKING_WITH_PORTALS; - } - else if (req == WR_KILLMESSAGE) - { - return WEAPON_MINSTANEX_MURDER; - } - return TRUE; -} -#endif -#ifdef CSQC -float w_minstanex(float req) -{ - if(req == WR_IMPACTEFFECT) - { - vector org2; - org2 = w_org + w_backoff * 6; - pointparticles(particleeffectnum("nex_impact"), org2, '0 0 0', 1); - if(!w_issilent) - sound(self, CH_SHOTS, "weapons/neximpact.wav", VOL_BASE, ATTEN_NORM); - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/neximpact.wav"); - } - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_nex.qc b/qcsrc/server/w_nex.qc deleted file mode 100644 index 919e27e5ae..0000000000 --- a/qcsrc/server/w_nex.qc +++ /dev/null @@ -1,275 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ NEX, -/* function */ w_nex, -/* ammotype */ IT_CELLS, -/* impulse */ 7, -/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN, -/* rating */ BOT_PICKUP_RATING_HIGH, -/* model */ "nex", -/* shortname */ "nex", -/* fullname */ _("Nex") -); -#else -#ifdef SVQC - -void SendCSQCNexBeamParticle(float charge) { - vector v; - v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); - WriteByte(MSG_BROADCAST, SVC_TEMPENTITY); - WriteByte(MSG_BROADCAST, TE_CSQC_NEXGUNBEAMPARTICLE); - WriteCoord(MSG_BROADCAST, w_shotorg_x); - WriteCoord(MSG_BROADCAST, w_shotorg_y); - WriteCoord(MSG_BROADCAST, w_shotorg_z); - WriteCoord(MSG_BROADCAST, v_x); - WriteCoord(MSG_BROADCAST, v_y); - WriteCoord(MSG_BROADCAST, v_z); - WriteByte(MSG_BROADCAST, bound(0, 255 * charge, 255)); -} - -void W_Nex_Attack (float issecondary) -{ - float mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, myammo, charge; - if(issecondary) - { - mydmg = autocvar_g_balance_nex_secondary_damage; - myforce = autocvar_g_balance_nex_secondary_force; - mymindist = autocvar_g_balance_nex_secondary_damagefalloff_mindist; - mymaxdist = autocvar_g_balance_nex_secondary_damagefalloff_maxdist; - myhalflife = autocvar_g_balance_nex_secondary_damagefalloff_halflife; - myforcehalflife = autocvar_g_balance_nex_secondary_damagefalloff_forcehalflife; - myammo = autocvar_g_balance_nex_secondary_ammo; - } - else - { - mydmg = autocvar_g_balance_nex_primary_damage; - myforce = autocvar_g_balance_nex_primary_force; - mymindist = autocvar_g_balance_nex_primary_damagefalloff_mindist; - mymaxdist = autocvar_g_balance_nex_primary_damagefalloff_maxdist; - myhalflife = autocvar_g_balance_nex_primary_damagefalloff_halflife; - myforcehalflife = autocvar_g_balance_nex_primary_damagefalloff_forcehalflife; - myammo = autocvar_g_balance_nex_primary_ammo; - } - - float flying; - flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last - - if(autocvar_g_balance_nex_charge) - { - charge = autocvar_g_balance_nex_charge_mindmg / mydmg + (1 - autocvar_g_balance_nex_charge_mindmg / mydmg) * self.nex_charge; - self.nex_charge *= autocvar_g_balance_nex_charge_shot_multiplier; // do this AFTER setting mydmg/myforce - // O RLY? -- divVerent - // YA RLY -- FruitieX - } - else - charge = 1; - mydmg *= charge; - myforce *= charge; - - W_SetupShot (self, TRUE, 5, "weapons/nexfire.wav", CH_WEAPON_A, mydmg); - if(charge > autocvar_g_balance_nex_charge_animlimit && autocvar_g_balance_nex_charge_animlimit) // if the Nex is overcharged, we play an extra sound - { - sound (self, CH_WEAPON_B, "weapons/nexcharge.wav", VOL_BASE * (charge - 0.5 * autocvar_g_balance_nex_charge_animlimit) / (1 - 0.5 * autocvar_g_balance_nex_charge_animlimit), ATTEN_NORM); - } - - yoda = 0; - FireRailgunBullet (w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, WEP_NEX); - - if(yoda && flying) - Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); - - //beam and muzzle flash done on client - SendCSQCNexBeamParticle(charge); - - W_DecreaseAmmo(ammo_cells, myammo, autocvar_g_balance_nex_reload_ammo); -} - -void spawnfunc_weapon_nex (void); // defined in t_items.qc - -.float nex_chargepool_pauseregen_finished; -float w_nex(float req) -{ - float dt; - float ammo_amount; - if (req == WR_AIM) - { - if(bot_aim(1000000, 0, 1, FALSE)) - self.BUTTON_ATCK = TRUE; - else - { - if(autocvar_g_balance_nex_charge) - self.BUTTON_ATCK2 = TRUE; - } - } - else if (req == WR_THINK) - { - if(autocvar_g_balance_nex_charge && self.nex_charge < autocvar_g_balance_nex_charge_limit) - self.nex_charge = min(1, self.nex_charge + autocvar_g_balance_nex_charge_rate * frametime / W_TICSPERFRAME); - - if(autocvar_g_balance_nex_secondary_chargepool) - if(self.nex_chargepool_ammo < 1) - { - if(self.nex_chargepool_pauseregen_finished < time) - self.nex_chargepool_ammo = min(1, self.nex_chargepool_ammo + autocvar_g_balance_nex_secondary_chargepool_regen * frametime / W_TICSPERFRAME); - self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_nex_secondary_chargepool_pause_health_regen); - } - - if(autocvar_g_balance_nex_reload_ammo && self.clip_load < min(autocvar_g_balance_nex_primary_ammo, autocvar_g_balance_nex_secondary_ammo)) // forced reload - weapon_action(self.weapon, WR_RELOAD); - else - { - if (self.BUTTON_ATCK) - { - if (weapon_prepareattack(0, autocvar_g_balance_nex_primary_refire)) - { - W_Nex_Attack(0); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_nex_primary_animtime, w_ready); - } - } - if ((autocvar_g_balance_nex_secondary_charge && !autocvar_g_balance_nex_secondary) ? (self.BUTTON_ZOOM | self.BUTTON_ZOOMSCRIPT) : self.BUTTON_ATCK2) - { - if(autocvar_g_balance_nex_secondary_charge) - { - self.nex_charge_rottime = time + autocvar_g_balance_nex_charge_rot_pause; - dt = frametime / W_TICSPERFRAME; - - if(self.nex_charge < 1) - { - if(autocvar_g_balance_nex_secondary_chargepool) - { - if(autocvar_g_balance_nex_secondary_ammo) - { - // always deplete if secondary is held - self.nex_chargepool_ammo = max(0, self.nex_chargepool_ammo - autocvar_g_balance_nex_secondary_ammo * dt); - - dt = min(dt, (1 - self.nex_charge) / autocvar_g_balance_nex_secondary_charge_rate); - self.nex_chargepool_pauseregen_finished = time + autocvar_g_balance_nex_secondary_chargepool_pause_regen; - dt = min(dt, self.nex_chargepool_ammo); - dt = max(0, dt); - - self.nex_charge += dt * autocvar_g_balance_nex_secondary_charge_rate; - } - } - - else if(autocvar_g_balance_nex_secondary_ammo) - { - if(self.BUTTON_ATCK2) // only eat ammo when the button is pressed - { - dt = min(dt, (1 - self.nex_charge) / autocvar_g_balance_nex_secondary_charge_rate); - if (!(self.items & IT_UNLIMITED_WEAPON_AMMO)) - { - // if this weapon is reloadable, decrease its load. Else decrease the player's ammo - if(autocvar_g_balance_nex_reload_ammo) - { - dt = min(dt, (self.clip_load - autocvar_g_balance_nex_primary_ammo) / autocvar_g_balance_nex_secondary_ammo); - dt = max(0, dt); - if(dt > 0) - { - self.clip_load = max(autocvar_g_balance_nex_secondary_ammo, self.clip_load - autocvar_g_balance_nex_secondary_ammo * dt); - } - self.(weapon_load[WEP_NEX]) = self.clip_load; - } - else - { - dt = min(dt, (self.ammo_cells - autocvar_g_balance_nex_primary_ammo) / autocvar_g_balance_nex_secondary_ammo); - dt = max(0, dt); - if(dt > 0) - { - self.ammo_cells = max(autocvar_g_balance_nex_secondary_ammo, self.ammo_cells - autocvar_g_balance_nex_secondary_ammo * dt); - } - } - } - self.nex_charge += dt * autocvar_g_balance_nex_secondary_charge_rate; - } - } - - else - { - dt = min(dt, (1 - self.nex_charge) / autocvar_g_balance_nex_secondary_charge_rate); - self.nex_charge += dt * autocvar_g_balance_nex_secondary_charge_rate; - } - } - } - else if(autocvar_g_balance_nex_secondary) - { - if (weapon_prepareattack(0, autocvar_g_balance_nex_secondary_refire)) - { - W_Nex_Attack(1); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_nex_secondary_animtime, w_ready); - } - } - } - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/nexflash.md3"); - precache_model ("models/weapons/g_nex.md3"); - precache_model ("models/weapons/v_nex.md3"); - precache_model ("models/weapons/h_nex.iqm"); - precache_sound ("weapons/nexfire.wav"); - precache_sound ("weapons/nexcharge.wav"); - precache_sound ("weapons/nexwhoosh1.wav"); - precache_sound ("weapons/nexwhoosh2.wav"); - precache_sound ("weapons/nexwhoosh3.wav"); - //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_NEX); - self.current_ammo = ammo_cells; - } - else if (req == WR_CHECKAMMO1) - { - ammo_amount = self.ammo_cells >= autocvar_g_balance_nex_primary_ammo; - ammo_amount += (autocvar_g_balance_nex_reload_ammo && self.(weapon_load[WEP_NEX]) >= autocvar_g_balance_nex_primary_ammo); - return ammo_amount; - } - else if (req == WR_CHECKAMMO2) - { - if(autocvar_g_balance_nex_secondary) - { - // don't allow charging if we don't have enough ammo - ammo_amount = self.ammo_cells >= autocvar_g_balance_nex_secondary_ammo; - ammo_amount += self.(weapon_load[WEP_NEX]) >= autocvar_g_balance_nex_secondary_ammo; - return ammo_amount; - } - else - { - return FALSE; // zoom is not a fire mode - } - } - else if (req == WR_RELOAD) - { - W_Reload(min(autocvar_g_balance_nex_primary_ammo, autocvar_g_balance_nex_secondary_ammo), autocvar_g_balance_nex_reload_ammo, autocvar_g_balance_nex_reload_time, "weapons/reload.wav"); - } - else if (req == WR_SUICIDEMESSAGE) - { - return WEAPON_THINKING_WITH_PORTALS; - } - else if (req == WR_KILLMESSAGE) - { - return WEAPON_NEX_MURDER; - } - return TRUE; -} -#endif -#ifdef CSQC -float w_nex(float req) -{ - if(req == WR_IMPACTEFFECT) - { - vector org2; - org2 = w_org + w_backoff * 6; - pointparticles(particleeffectnum("nex_impact"), org2, '0 0 0', 1); - if(!w_issilent) - sound(self, CH_SHOTS, "weapons/neximpact.wav", VOL_BASE, ATTEN_NORM); - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/neximpact.wav"); - } - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_porto.qc b/qcsrc/server/w_porto.qc deleted file mode 100644 index 1eb0f4b0bc..0000000000 --- a/qcsrc/server/w_porto.qc +++ /dev/null @@ -1,391 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ PORTO, -/* function */ w_porto, -/* ammotype */ 0, -/* impulse */ 0, -/* flags */ WEP_TYPE_OTHER | WEP_FLAG_SUPERWEAPON, -/* rating */ 0, -/* model */ "porto" , -/* shortname */ "porto", -/* fullname */ _("Port-O-Launch") -); -#else -#ifdef SVQC -.entity porto_current; -.vector porto_v_angle; // holds "held" view angles -.float porto_v_angle_held; -.vector right_vector; - -void W_Porto_Success (void) -{ - if(self.realowner == world) - { - objerror("Cannot succeed successfully: no owner\n"); - return; - } - - self.realowner.porto_current = world; - remove(self); -} - -string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo); -void W_Porto_Fail (float failhard) -{ - if(self.realowner == world) - { - objerror("Cannot fail successfully: no owner\n"); - return; - } - - // no portals here! - if(self.cnt < 0) - { - Portal_ClearWithID(self.realowner, self.portal_id); - } - - self.realowner.porto_current = world; - - if(self.cnt < 0 && !failhard && self.realowner.playerid == self.playerid && self.realowner.deadflag == DEAD_NO && !(self.realowner.weapons & WEPSET_PORTO)) - { - setsize (self, '-16 -16 0', '16 16 32'); - setorigin(self, self.origin + trace_plane_normal); - if(move_out_of_solid(self)) - { - self.flags = FL_ITEM; - self.velocity = trigger_push_calculatevelocity(self.origin, self.realowner, 128); - tracetoss(self, self); - if(vlen(trace_endpos - self.realowner.origin) < 128) - { - W_ThrowNewWeapon(self.realowner, WEP_PORTO, 0, self.origin, self.velocity); - centerprint(self.realowner, "^1Portal deployment failed.\n\n^2Catch it to try again!"); - } - } - } - remove(self); -} - -void W_Porto_Remove (entity p) -{ - if(p.porto_current.realowner == p && p.porto_current.classname == "porto") - { - entity oldself; - oldself = self; - self = p.porto_current; - W_Porto_Fail(1); - self = oldself; - } -} - -void W_Porto_Think (void) -{ - trace_plane_normal = '0 0 0'; - if(self.realowner.playerid != self.playerid) - remove(self); - else - W_Porto_Fail(0); -} - -void W_Porto_Touch (void) -{ - vector norm; - - // do not use PROJECTILE_TOUCH here - // FIXME but DO handle warpzones! - - if(other.classname == "portal") - return; // handled by the portal - - norm = trace_plane_normal; - if(trace_ent.iscreature) - { - traceline(trace_ent.origin, trace_ent.origin + '0 0 2' * PL_MIN_z, MOVE_WORLDONLY, self); - if(trace_fraction >= 1) - return; - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP) - return; - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) - return; - } - - if(self.realowner.playerid != self.playerid) - { - sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM); - remove(self); - } - else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP) - { - spamsound(self, CH_SHOTS, "porto/bounce.wav", VOL_BASE, ATTEN_NORM); - // just reflect - self.right_vector = self.right_vector - 2 * trace_plane_normal * (self.right_vector * trace_plane_normal); - self.angles = vectoangles(self.velocity - 2 * trace_plane_normal * (self.velocity * trace_plane_normal)); - } - else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) - { - sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM); - W_Porto_Fail(0); - if(self.cnt < 0) - Portal_ClearAll_PortalsOnly(self.realowner); - } - else if(self.cnt == 0) - { - // in-portal only - if(Portal_SpawnInPortalAtTrace(self.realowner, self.right_vector, self.portal_id)) - { - sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM); - trace_plane_normal = norm; - centerprint(self.realowner, "^1In^7-portal created."); - W_Porto_Success(); - } - else - { - sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM); - trace_plane_normal = norm; - W_Porto_Fail(0); - } - } - else if(self.cnt == 1) - { - // out-portal only - if(Portal_SpawnOutPortalAtTrace(self.realowner, self.right_vector, self.portal_id)) - { - sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM); - trace_plane_normal = norm; - centerprint(self.realowner, "^4Out^7-portal created."); - W_Porto_Success(); - } - else - { - sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM); - trace_plane_normal = norm; - W_Porto_Fail(0); - } - } - else if(self.effects & EF_RED) - { - self.effects += EF_BLUE - EF_RED; - if(Portal_SpawnInPortalAtTrace(self.realowner, self.right_vector, self.portal_id)) - { - sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM); - trace_plane_normal = norm; - centerprint(self.realowner, "^1In^7-portal created."); - self.right_vector = self.right_vector - 2 * trace_plane_normal * (self.right_vector * norm); - self.angles = vectoangles(self.velocity - 2 * trace_plane_normal * (self.velocity * norm)); - CSQCProjectile(self, TRUE, PROJECTILE_PORTO_BLUE, TRUE); // change type - } - else - { - sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM); - trace_plane_normal = norm; - Portal_ClearAll_PortalsOnly(self.realowner); - W_Porto_Fail(0); - } - } - else - { - if(self.realowner.portal_in.portal_id == self.portal_id) - { - if(Portal_SpawnOutPortalAtTrace(self.realowner, self.right_vector, self.portal_id)) - { - sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM); - trace_plane_normal = norm; - centerprint(self.realowner, "^4Out^7-portal created."); - W_Porto_Success(); - } - else - { - sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM); - Portal_ClearAll_PortalsOnly(self.realowner); - W_Porto_Fail(0); - } - } - else - { - sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM); - Portal_ClearAll_PortalsOnly(self.realowner); - W_Porto_Fail(0); - } - } -} - -void W_Porto_Attack (float type) -{ - entity gren; - - W_SetupShot (self, FALSE, 4, "porto/fire.wav", CH_WEAPON_A, 0); - // always shoot from the eye - w_shotdir = v_forward; - w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward; - - //pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - gren = spawn (); - gren.cnt = type; - gren.owner = gren.realowner = self; - gren.playerid = self.playerid; - gren.classname = "porto"; - gren.bot_dodge = TRUE; - gren.bot_dodgerating = 200; - gren.movetype = MOVETYPE_BOUNCEMISSILE; - PROJECTILE_MAKETRIGGER(gren); - gren.effects = EF_RED; - gren.scale = 4; - setorigin(gren, w_shotorg); - setsize(gren, '0 0 0', '0 0 0'); - - if(type > 0) - gren.nextthink = time + autocvar_g_balance_porto_secondary_lifetime; - else - gren.nextthink = time + autocvar_g_balance_porto_primary_lifetime; - gren.think = W_Porto_Think; - gren.touch = W_Porto_Touch; - - if(type > 0) - { - if(self.items & IT_STRENGTH) - W_SetupProjectileVelocity(gren, autocvar_g_balance_porto_secondary_speed * autocvar_g_balance_powerup_strength_force, 0); - else - W_SetupProjectileVelocity(gren, autocvar_g_balance_porto_secondary_speed, 0); - } - else - { - if(self.items & IT_STRENGTH) - W_SetupProjectileVelocity(gren, autocvar_g_balance_porto_primary_speed * autocvar_g_balance_powerup_strength_force, 0); - else - W_SetupProjectileVelocity(gren, autocvar_g_balance_porto_primary_speed, 0); - } - - gren.angles = vectoangles (gren.velocity); - gren.flags = FL_PROJECTILE; - - gren.portal_id = time; - self.porto_current = gren; - gren.playerid = self.playerid; - fixedmakevectors(fixedvectoangles(gren.velocity)); - gren.right_vector = v_right; - - gren.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP; - - if(type > 0) - CSQCProjectile(gren, TRUE, PROJECTILE_PORTO_BLUE, TRUE); - else - CSQCProjectile(gren, TRUE, PROJECTILE_PORTO_RED, TRUE); - - other = gren; MUTATOR_CALLHOOK(EditProjectile); -} - -void spawnfunc_weapon_porto (void) -{ - weapon_defaultspawnfunc(WEP_PORTO); -} - -float w_nexball_weapon(float req); -float w_porto(float req) -{ - //vector v_angle_save; - - if (g_nexball) { return w_nexball_weapon(req); } - if (req == WR_AIM) - { - self.BUTTON_ATCK = FALSE; - self.BUTTON_ATCK2 = FALSE; - if(!autocvar_g_balance_porto_secondary) - if(bot_aim(autocvar_g_balance_porto_primary_speed, 0, autocvar_g_balance_grenadelauncher_primary_lifetime, FALSE)) - self.BUTTON_ATCK = TRUE; - } - else if (req == WR_THINK) - { - if(autocvar_g_balance_porto_secondary) - { - if (self.BUTTON_ATCK) - if (!self.porto_current) - if (!self.porto_forbidden) - if (weapon_prepareattack(0, autocvar_g_balance_porto_primary_refire)) - { - W_Porto_Attack(0); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_porto_primary_animtime, w_ready); - } - - if (self.BUTTON_ATCK2) - if (!self.porto_current) - if (!self.porto_forbidden) - if (weapon_prepareattack(1, autocvar_g_balance_porto_secondary_refire)) - { - W_Porto_Attack(1); - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_porto_secondary_animtime, w_ready); - } - } - else - { - if(self.porto_v_angle_held) - { - if(!self.BUTTON_ATCK2) - { - self.porto_v_angle_held = 0; - - ClientData_Touch(self); - } - } - else - { - if(self.BUTTON_ATCK2) - { - self.porto_v_angle = self.v_angle; - self.porto_v_angle_held = 1; - - ClientData_Touch(self); - } - } - if(self.porto_v_angle_held) - makevectors(self.porto_v_angle); // override the previously set angles - - if (self.BUTTON_ATCK) - if (!self.porto_current) - if (!self.porto_forbidden) - if (weapon_prepareattack(0, autocvar_g_balance_porto_primary_refire)) - { - W_Porto_Attack(-1); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_porto_primary_animtime, w_ready); - } - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/weapons/g_porto.md3"); - precache_model ("models/weapons/v_porto.md3"); - precache_model ("models/weapons/h_porto.iqm"); - precache_model ("models/portal.md3"); - precache_sound ("porto/bounce.wav"); - precache_sound ("porto/create.wav"); - precache_sound ("porto/expire.wav"); - precache_sound ("porto/explode.wav"); - precache_sound ("porto/fire.wav"); - precache_sound ("porto/unsupported.wav"); - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_PORTO); - self.current_ammo = ammo_none; - } - else if (req == WR_RESETPLAYER) - { - self.porto_current = world; - } - return TRUE; -} -#endif -#ifdef CSQC -float w_porto(float req) -{ - if(req == WR_IMPACTEFFECT) - { - print("Since when does Porto send DamageInfo?\n"); - } - else if(req == WR_PRECACHE) - { - // nothing to do - } - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_rifle.qc b/qcsrc/server/w_rifle.qc deleted file mode 100644 index 0e3a5c0b02..0000000000 --- a/qcsrc/server/w_rifle.qc +++ /dev/null @@ -1,264 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ RIFLE, -/* function */ w_rifle, -/* ammotype */ IT_NAILS, -/* impulse */ 7, -/* flags */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN, -/* rating */ BOT_PICKUP_RATING_MID, -/* model */ "campingrifle", -/* shortname */ "rifle", -/* fullname */ _("Rifle") -); -#else -#ifdef SVQC - -.float rifle_accumulator; - -void W_Rifle_FireBullet(float pSpread, float pDamage, float pForce, float pSolidPenetration, float pAmmo, float deathtype, float pTracer, float pShots, string pSound) -{ - float i; - - W_DecreaseAmmo(ammo_nails, pAmmo, autocvar_g_balance_rifle_reload_ammo); - - W_SetupShot (self, TRUE, 2, pSound, CH_WEAPON_A, pDamage * pShots); - - pointparticles(particleeffectnum("rifle_muzzleflash"), w_shotorg, w_shotdir * 2000, 1); - - if(self.BUTTON_ZOOM | self.BUTTON_ZOOMSCRIPT) // if zoomed, shoot from the eye - { - w_shotdir = v_forward; - w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward; - } - - for(i = 0; i < pShots; ++i) - fireBullet(w_shotorg, w_shotdir, pSpread, pSolidPenetration, pDamage, pForce, deathtype, (pTracer ? EF_RED : EF_BLUE)); - - if (autocvar_g_casings >= 2) - 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, self); -} - -void W_Rifle_Attack() -{ - W_Rifle_FireBullet(autocvar_g_balance_rifle_primary_spread, autocvar_g_balance_rifle_primary_damage, autocvar_g_balance_rifle_primary_force, autocvar_g_balance_rifle_primary_solidpenetration, autocvar_g_balance_rifle_primary_ammo, WEP_RIFLE, autocvar_g_balance_rifle_primary_tracer, autocvar_g_balance_rifle_primary_shots, "weapons/campingrifle_fire.wav"); -} - -void W_Rifle_Attack2() -{ - W_Rifle_FireBullet(autocvar_g_balance_rifle_secondary_spread, autocvar_g_balance_rifle_secondary_damage, autocvar_g_balance_rifle_secondary_force, autocvar_g_balance_rifle_secondary_solidpenetration, autocvar_g_balance_rifle_secondary_ammo, WEP_RIFLE | HITTYPE_SECONDARY, autocvar_g_balance_rifle_secondary_tracer, autocvar_g_balance_rifle_secondary_shots, "weapons/campingrifle_fire2.wav"); -} - -void spawnfunc_weapon_rifle (void) -{ - weapon_defaultspawnfunc(WEP_RIFLE); -} - -// compatibility alias -void spawnfunc_weapon_campingrifle (void) -{ - spawnfunc_weapon_rifle(); -} -void spawnfunc_weapon_sniperrifle (void) -{ - spawnfunc_weapon_rifle(); -} - -.void(void) rifle_bullethail_attackfunc; -.float rifle_bullethail_frame; -.float rifle_bullethail_animtime; -.float rifle_bullethail_refire; -void W_Rifle_BulletHail_Continue() -{ - float r, sw, af; - - sw = self.switchweapon; // make it not detect weapon changes as reason to abort firing - af = ATTACK_FINISHED(self); - self.switchweapon = self.weapon; - ATTACK_FINISHED(self) = time; - print(ftos(self.ammo_nails), "\n"); - r = weapon_prepareattack(self.rifle_bullethail_frame == WFRAME_FIRE2, self.rifle_bullethail_refire); - if(self.switchweapon == self.weapon) - self.switchweapon = sw; - if(r) - { - self.rifle_bullethail_attackfunc(); - weapon_thinkf(self.rifle_bullethail_frame, self.rifle_bullethail_animtime, W_Rifle_BulletHail_Continue); - print("thinkf set\n"); - } - else - { - ATTACK_FINISHED(self) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time - print("out of ammo... ", ftos(self.weaponentity.state), "\n"); - } -} - -void W_Rifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire) -{ - // if we get here, we have at least one bullet to fire - AttackFunc(); - if(mode) - { - // continue hail - self.rifle_bullethail_attackfunc = AttackFunc; - self.rifle_bullethail_frame = fr; - self.rifle_bullethail_animtime = animtime; - self.rifle_bullethail_refire = refire; - weapon_thinkf(fr, animtime, W_Rifle_BulletHail_Continue); - } - else - { - // just one shot - weapon_thinkf(fr, animtime, w_ready); - } -} - -.float bot_secondary_riflemooth; -float w_rifle(float req) -{ - float ammo_amount; - - if (req == WR_AIM) - { - self.BUTTON_ATCK=FALSE; - self.BUTTON_ATCK2=FALSE; - if(vlen(self.origin-self.enemy.origin) > 1000) - self.bot_secondary_riflemooth = 0; - if(self.bot_secondary_riflemooth == 0) - { - if(bot_aim(1000000, 0, 0.001, FALSE)) - { - self.BUTTON_ATCK = TRUE; - if(random() < 0.01) self.bot_secondary_riflemooth = 1; - } - } - else - { - if(bot_aim(1000000, 0, 0.001, FALSE)) - { - self.BUTTON_ATCK2 = TRUE; - if(random() < 0.03) self.bot_secondary_riflemooth = 0; - } - } - } - else if (req == WR_THINK) - { - if(autocvar_g_balance_rifle_reload_ammo && self.clip_load < min(autocvar_g_balance_rifle_primary_ammo, autocvar_g_balance_rifle_secondary_ammo)) // forced reload - weapon_action(self.weapon, WR_RELOAD); - else - { - self.rifle_accumulator = bound(time - autocvar_g_balance_rifle_bursttime, self.rifle_accumulator, time); - if (self.BUTTON_ATCK) - if (weapon_prepareattack_check(0, autocvar_g_balance_rifle_primary_refire)) - if (time >= self.rifle_accumulator + autocvar_g_balance_rifle_primary_burstcost) - { - weapon_prepareattack_do(0, autocvar_g_balance_rifle_primary_refire); - W_Rifle_BulletHail(autocvar_g_balance_rifle_primary_bullethail, W_Rifle_Attack, WFRAME_FIRE1, autocvar_g_balance_rifle_primary_animtime, autocvar_g_balance_rifle_primary_refire); - self.rifle_accumulator += autocvar_g_balance_rifle_primary_burstcost; - } - if (self.BUTTON_ATCK2) - { - if (autocvar_g_balance_rifle_secondary) - { - if(autocvar_g_balance_rifle_secondary_reload) - weapon_action(self.weapon, WR_RELOAD); - else - { - if (weapon_prepareattack_check(1, autocvar_g_balance_rifle_secondary_refire)) - if (time >= self.rifle_accumulator + autocvar_g_balance_rifle_secondary_burstcost) - { - weapon_prepareattack_do(1, autocvar_g_balance_rifle_secondary_refire); - W_Rifle_BulletHail(autocvar_g_balance_rifle_secondary_bullethail, W_Rifle_Attack2, WFRAME_FIRE2, autocvar_g_balance_rifle_secondary_animtime, autocvar_g_balance_rifle_primary_refire); - self.rifle_accumulator += autocvar_g_balance_rifle_secondary_burstcost; - } - } - } - } - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/weapons/g_campingrifle.md3"); - precache_model ("models/weapons/v_campingrifle.md3"); - precache_model ("models/weapons/h_campingrifle.iqm"); - precache_sound ("weapons/campingrifle_fire.wav"); - precache_sound ("weapons/campingrifle_fire2.wav"); - //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_RIFLE); - self.current_ammo = ammo_nails; - } - else if (req == WR_CHECKAMMO1) - { - ammo_amount = self.ammo_nails >= autocvar_g_balance_rifle_primary_ammo; - ammo_amount += self.(weapon_load[WEP_RIFLE]) >= autocvar_g_balance_rifle_primary_ammo; - return ammo_amount; - } - else if (req == WR_CHECKAMMO2) - { - ammo_amount = self.ammo_nails >= autocvar_g_balance_rifle_secondary_ammo; - ammo_amount += self.(weapon_load[WEP_RIFLE]) >= autocvar_g_balance_rifle_secondary_ammo; - return ammo_amount; - } - else if (req == WR_RESETPLAYER) - { - self.rifle_accumulator = time - autocvar_g_balance_rifle_bursttime; - } - else if (req == WR_RELOAD) - { - W_Reload(min(autocvar_g_balance_rifle_primary_ammo, autocvar_g_balance_rifle_secondary_ammo), autocvar_g_balance_rifle_reload_ammo, autocvar_g_balance_rifle_reload_time, "weapons/reload.wav"); - } - else if (req == WR_SUICIDEMESSAGE) - { - return WEAPON_THINKING_WITH_PORTALS; - } - else if (req == WR_KILLMESSAGE) - { - if(w_deathtype & HITTYPE_SECONDARY) - { - if(w_deathtype & HITTYPE_BOUNCE) - return WEAPON_RIFLE_MURDER_HAIL_PIERCING; - else - return WEAPON_RIFLE_MURDER_HAIL; - } - else - { - if(w_deathtype & HITTYPE_BOUNCE) - return WEAPON_RIFLE_MURDER_PIERCING; - else - return WEAPON_RIFLE_MURDER; - } - } - return TRUE; -} -#endif -#ifdef CSQC -float w_rifle(float req) -{ - if(req == WR_IMPACTEFFECT) - { - vector org2; - org2 = w_org + w_backoff * 2; - pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1); - if(!w_issilent) - { - if(w_random < 0.2) - sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTEN_NORM); - else if(w_random < 0.4) - sound(self, CH_SHOTS, "weapons/ric2.wav", VOL_BASE, ATTEN_NORM); - else if(w_random < 0.5) - sound(self, CH_SHOTS, "weapons/ric3.wav", VOL_BASE, ATTEN_NORM); - } - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/ric1.wav"); - precache_sound("weapons/ric2.wav"); - precache_sound("weapons/ric3.wav"); - } - - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_rocketlauncher.qc b/qcsrc/server/w_rocketlauncher.qc deleted file mode 100644 index 6cd8929f6f..0000000000 --- a/qcsrc/server/w_rocketlauncher.qc +++ /dev/null @@ -1,491 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ ROCKET_LAUNCHER, -/* function */ w_rlauncher, -/* ammotype */ IT_ROCKETS, -/* impulse */ 9, -/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH, -/* rating */ BOT_PICKUP_RATING_HIGH, -/* model */ "rl", -/* shortname */ "rocketlauncher", -/* fullname */ _("Rocket Launcher") -); -#else -#ifdef SVQC -.float rl_release; -.float rl_detonate_later; - -void W_Rocket_Unregister() -{ - if(self.realowner && self.realowner.lastrocket == self) - { - self.realowner.lastrocket = world; - // self.realowner.rl_release = 1; - } -} - -void W_Rocket_Explode () -{ - W_Rocket_Unregister(); - - if(other.takedamage == DAMAGE_AIM) - if(IS_PLAYER(other)) - if(DIFF_TEAM(self.realowner, other)) - if(other.deadflag == DEAD_NO) - if(IsFlying(other)) - Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); - - self.event_damage = func_null; - self.takedamage = DAMAGE_NO; - - RadiusDamage (self, self.realowner, autocvar_g_balance_rocketlauncher_damage, autocvar_g_balance_rocketlauncher_edgedamage, autocvar_g_balance_rocketlauncher_radius, world, autocvar_g_balance_rocketlauncher_force, self.projectiledeathtype, other); - - if (self.realowner.weapon == WEP_ROCKET_LAUNCHER) - { - if(self.realowner.ammo_rockets < autocvar_g_balance_rocketlauncher_ammo) - { - self.realowner.cnt = WEP_ROCKET_LAUNCHER; - ATTACK_FINISHED(self.realowner) = time; - self.realowner.switchweapon = w_getbestweapon(self.realowner); - } - } - remove (self); -} - -void W_Rocket_DoRemoteExplode () -{ - W_Rocket_Unregister(); - - self.event_damage = func_null; - self.takedamage = DAMAGE_NO; - - RadiusDamage (self, self.realowner, autocvar_g_balance_rocketlauncher_remote_damage, autocvar_g_balance_rocketlauncher_remote_edgedamage, autocvar_g_balance_rocketlauncher_remote_radius, world, autocvar_g_balance_rocketlauncher_remote_force, self.projectiledeathtype | HITTYPE_BOUNCE, world); - - if (self.realowner.weapon == WEP_ROCKET_LAUNCHER) - { - if(self.realowner.ammo_rockets < autocvar_g_balance_rocketlauncher_ammo) - { - self.realowner.cnt = WEP_ROCKET_LAUNCHER; - ATTACK_FINISHED(self.realowner) = time; - self.realowner.switchweapon = w_getbestweapon(self.realowner); - } - } - remove (self); -} - -void W_Rocket_RemoteExplode() -{ - if(self.realowner.deadflag == DEAD_NO) - if(self.realowner.lastrocket) - { - if((self.spawnshieldtime >= 0) - ? (time >= self.spawnshieldtime) // timer - : (vlen(NearestPointOnBox(self.realowner, self.origin) - self.origin) > autocvar_g_balance_rocketlauncher_remote_radius) // safety device - ) - { - W_Rocket_DoRemoteExplode(); - } - } -} - -vector rocket_steerto(vector thisdir, vector goaldir, float maxturn_cos) -{ - if(thisdir * goaldir > maxturn_cos) - return goaldir; - if(thisdir * goaldir < -0.9998) // less than 1 degree and opposite - return thisdir; // refuse to guide (better than letting a numerical error happen) - float f, m2; - vector v; - // solve: - // g = normalize(thisdir + goaldir * X) - // thisdir * g = maxturn - // - // gg = thisdir + goaldir * X - // (thisdir * gg)^2 = maxturn^2 * (gg * gg) - // - // (1 + (thisdir * goaldir) * X)^2 = maxturn^2 * (1 + X*X + 2 * X * thisdir * goaldir) - f = thisdir * goaldir; - // (1 + f * X)^2 = maxturn^2 * (1 + X*X + 2 * X * f) - // 0 = (m^2 - f^2) * x^2 + (2 * f * (m^2 - 1)) * x + (m^2 - 1) - m2 = maxturn_cos * maxturn_cos; - v = solve_quadratic(m2 - f * f, 2 * f * (m2 - 1), m2 - 1); - return normalize(thisdir + goaldir * v_y); // the larger solution! -} -// assume thisdir == -goaldir: -// f == -1 -// v = solve_qadratic(m2 - 1, -2 * (m2 - 1), m2 - 1) -// (m2 - 1) x^2 - 2 * (m2 - 1) * x + (m2 - 1) = 0 -// x^2 - 2 * x + 1 = 0 -// (x - 1)^2 = 0 -// x = 1 -// normalize(thisdir + goaldir) -// normalize(0) - -void W_Rocket_Think (void) -{ - vector desireddir, olddir, newdir, desiredorigin, goal; -#if 0 - float cosminang, cosmaxang, cosang; -#endif - float velspeed, f; - self.nextthink = time; - if (time > self.cnt) - { - other = world; - self.projectiledeathtype |= HITTYPE_BOUNCE; - W_Rocket_Explode (); - return; - } - - // accelerate - makevectors(self.angles_x * '-1 0 0' + self.angles_y * '0 1 0'); - velspeed = autocvar_g_balance_rocketlauncher_speed * g_weaponspeedfactor - (self.velocity * v_forward); - if (velspeed > 0) - self.velocity = self.velocity + v_forward * min(autocvar_g_balance_rocketlauncher_speedaccel * g_weaponspeedfactor * frametime, velspeed); - - // laser guided, or remote detonation - if (self.realowner.weapon == WEP_ROCKET_LAUNCHER) - { - if(self == self.realowner.lastrocket) - if (!self.realowner.rl_release) - if (!self.BUTTON_ATCK2) - if(autocvar_g_balance_rocketlauncher_guiderate) - if(time > self.pushltime) - if(self.realowner.deadflag == DEAD_NO) - { - f = autocvar_g_balance_rocketlauncher_guideratedelay; - if(f) - f = bound(0, (time - self.pushltime) / f, 1); - else - f = 1; - - velspeed = vlen(self.velocity); - - makevectors(self.realowner.v_angle); - desireddir = WarpZone_RefSys_TransformVelocity(self.realowner, self, v_forward); - desiredorigin = WarpZone_RefSys_TransformOrigin(self.realowner, self, self.realowner.origin + self.realowner.view_ofs); - olddir = normalize(self.velocity); - - // now it gets tricky... we want to move like some curve to approximate the target direction - // but we are limiting the rate at which we can turn! - goal = desiredorigin + ((self.origin - desiredorigin) * desireddir + autocvar_g_balance_rocketlauncher_guidegoal) * desireddir; - newdir = rocket_steerto(olddir, normalize(goal - self.origin), cos(autocvar_g_balance_rocketlauncher_guiderate * f * frametime * DEG2RAD)); - - self.velocity = newdir * velspeed; - self.angles = vectoangles(self.velocity); - - if(!self.count) - { - pointparticles(particleeffectnum("rocket_guide"), self.origin, self.velocity, 1); - // TODO add a better sound here - sound (self.realowner, CH_WEAPON_B, "weapons/rocket_mode.wav", VOL_BASE, ATTEN_NORM); - self.count = 1; - } - } - - if(self.rl_detonate_later) - W_Rocket_RemoteExplode(); - } - - if(self.csqcprojectile_clientanimate == 0) - UpdateCSQCProjectile(self); -} - -void W_Rocket_Touch (void) -{ - if(WarpZone_Projectile_Touch()) - { - if(wasfreed(self)) - W_Rocket_Unregister(); - return; - } - W_Rocket_Unregister(); - W_Rocket_Explode (); -} - -void W_Rocket_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) -{ - if (self.health <= 0) - return; - - if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions - return; // g_projectiles_damage says to halt - - self.health = self.health - damage; - self.angles = vectoangles(self.velocity); - - if (self.health <= 0) - W_PrepareExplosionByDamage(attacker, W_Rocket_Explode); -} - -void W_Rocket_Attack (void) -{ - entity missile; - entity flash; - - W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_rocketlauncher_ammo, autocvar_g_balance_rocketlauncher_reload_ammo); - - W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', FALSE, 5, "weapons/rocket_fire.wav", CH_WEAPON_A, autocvar_g_balance_rocketlauncher_damage); - pointparticles(particleeffectnum("rocketlauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - missile = WarpZone_RefSys_SpawnSameRefSys(self); - missile.owner = missile.realowner = self; - self.lastrocket = missile; - if(autocvar_g_balance_rocketlauncher_detonatedelay >= 0) - missile.spawnshieldtime = time + autocvar_g_balance_rocketlauncher_detonatedelay; - else - missile.spawnshieldtime = -1; - missile.pushltime = time + autocvar_g_balance_rocketlauncher_guidedelay; - missile.classname = "rocket"; - missile.bot_dodge = TRUE; - missile.bot_dodgerating = autocvar_g_balance_rocketlauncher_damage * 2; // * 2 because it can be detonated inflight which makes it even more dangerous - - missile.takedamage = DAMAGE_YES; - missile.damageforcescale = autocvar_g_balance_rocketlauncher_damageforcescale; - missile.health = autocvar_g_balance_rocketlauncher_health; - missile.event_damage = W_Rocket_Damage; - missile.damagedbycontents = TRUE; - - missile.movetype = MOVETYPE_FLY; - PROJECTILE_MAKETRIGGER(missile); - missile.projectiledeathtype = WEP_ROCKET_LAUNCHER; - 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_SetupProjectileVelocity(missile, autocvar_g_balance_rocketlauncher_speedstart, 0); - missile.angles = vectoangles (missile.velocity); - - missile.touch = W_Rocket_Touch; - missile.think = W_Rocket_Think; - missile.nextthink = time; - missile.cnt = time + autocvar_g_balance_rocketlauncher_lifetime; - missile.flags = FL_PROJECTILE; - missile.missile_flags = MIF_SPLASH; - - CSQCProjectile(missile, autocvar_g_balance_rocketlauncher_guiderate == 0 && autocvar_g_balance_rocketlauncher_speedaccel == 0, PROJECTILE_ROCKET, FALSE); // because of fly sound - - // muzzle flash for 1st person view - flash = spawn (); - setmodel (flash, "models/flash.md3"); // precision set below - SUB_SetFade (flash, time, 0.1); - flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; - W_AttachToShotorg(flash, '5 0 0'); - - // common properties - other = missile; MUTATOR_CALLHOOK(EditProjectile); -} - -void spawnfunc_weapon_rocketlauncher (void); // defined in t_items.qc - -float w_rlauncher(float req) -{ - entity rock; - float rockfound; - float ammo_amount; - - if (req == WR_AIM) - { - // aim and decide to fire if appropriate - self.BUTTON_ATCK = bot_aim(autocvar_g_balance_rocketlauncher_speed, 0, autocvar_g_balance_rocketlauncher_lifetime, FALSE); - if(skill >= 2) // skill 0 and 1 bots won't detonate rockets! - { - // decide whether to detonate rockets - entity missile, targetlist, targ; - float edgedamage, coredamage, edgeradius, recipricoledgeradius, d; - float selfdamage, teamdamage, enemydamage; - edgedamage = autocvar_g_balance_rocketlauncher_edgedamage; - coredamage = autocvar_g_balance_rocketlauncher_damage; - edgeradius = autocvar_g_balance_rocketlauncher_radius; - recipricoledgeradius = 1 / edgeradius; - selfdamage = 0; - teamdamage = 0; - enemydamage = 0; - targetlist = findchainfloat(bot_attack, TRUE); - missile = find(world, classname, "rocket"); - while (missile) - { - if (missile.realowner != self) - { - missile = find(missile, classname, "rocket"); - continue; - } - targ = targetlist; - while (targ) - { - d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - missile.origin); - d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000); - // count potential damage according to type of target - if (targ == self) - selfdamage = selfdamage + d; - else if (targ.team == self.team && teamplay) - teamdamage = teamdamage + d; - else if (bot_shouldattack(targ)) - enemydamage = enemydamage + d; - targ = targ.chain; - } - missile = find(missile, classname, "rocket"); - } - float desirabledamage; - desirabledamage = enemydamage; - if (time > self.invincible_finished && time > self.spawnshieldtime) - desirabledamage = desirabledamage - selfdamage * autocvar_g_balance_selfdamagepercent; - if (teamplay && self.team) - desirabledamage = desirabledamage - teamdamage; - - missile = find(world, classname, "rocket"); - while (missile) - { - if (missile.realowner != self) - { - missile = find(missile, classname, "rocket"); - continue; - } - makevectors(missile.v_angle); - targ = targetlist; - if (skill > 9) // normal players only do this for the target they are tracking - { - targ = targetlist; - while (targ) - { - if ( - (v_forward * normalize(missile.origin - targ.origin)< 0.1) - && desirabledamage > 0.1*coredamage - )self.BUTTON_ATCK2 = TRUE; - targ = targ.chain; - } - }else{ - float distance; distance= bound(300,vlen(self.origin-self.enemy.origin),30000); - //As the distance gets larger, a correct detonation gets near imposible - //Bots are assumed to use the rocket spawnfunc_light to see if the rocket gets near a player - if(v_forward * normalize(missile.origin - self.enemy.origin)< 0.1) - if(IS_PLAYER(self.enemy)) - if(desirabledamage >= 0.1*coredamage) - if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1)) - self.BUTTON_ATCK2 = TRUE; - // dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n"); - } - - missile = find(missile, classname, "rocket"); - } - // if we would be doing at X percent of the core damage, detonate it - // but don't fire a new shot at the same time! - if (desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events - self.BUTTON_ATCK2 = TRUE; - if ((skill > 6.5) && (selfdamage > self.health)) - self.BUTTON_ATCK2 = FALSE; - //if(self.BUTTON_ATCK2 == TRUE) - // dprint(ftos(desirabledamage),"\n"); - if (self.BUTTON_ATCK2 == TRUE) self.BUTTON_ATCK = FALSE; - } - } - else if (req == WR_THINK) - { - if(autocvar_g_balance_rocketlauncher_reload_ammo && self.clip_load < autocvar_g_balance_rocketlauncher_ammo) // forced reload - weapon_action(self.weapon, WR_RELOAD); - else - { - if (self.BUTTON_ATCK) - { - if(self.rl_release || autocvar_g_balance_rocketlauncher_guidestop) - if(weapon_prepareattack(0, autocvar_g_balance_rocketlauncher_refire)) - { - W_Rocket_Attack(); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_rocketlauncher_animtime, w_ready); - self.rl_release = 0; - } - } - else - self.rl_release = 1; - - if (self.BUTTON_ATCK2) - { - rockfound = 0; - for(rock = world; (rock = find(rock, classname, "rocket")); ) if(rock.realowner == self) - { - if(!rock.rl_detonate_later) - { - rock.rl_detonate_later = TRUE; - rockfound = 1; - } - } - if(rockfound) - sound (self, CH_WEAPON_B, "weapons/rocket_det.wav", VOL_BASE, ATTEN_NORM); - } - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/flash.md3"); - precache_model ("models/weapons/g_rl.md3"); - precache_model ("models/weapons/v_rl.md3"); - precache_model ("models/weapons/h_rl.iqm"); - precache_sound ("weapons/rocket_det.wav"); - precache_sound ("weapons/rocket_fire.wav"); - precache_sound ("weapons/rocket_mode.wav"); - //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_ROCKET_LAUNCHER); - self.current_ammo = ammo_rockets; - self.rl_release = 1; - } - else if (req == WR_CHECKAMMO1) - { - // don't switch while guiding a missile - if (ATTACK_FINISHED(self) <= time || self.weapon != WEP_ROCKET_LAUNCHER) - { - ammo_amount = FALSE; - if(autocvar_g_balance_rocketlauncher_reload_ammo) - { - if(self.ammo_rockets < autocvar_g_balance_rocketlauncher_ammo && self.(weapon_load[WEP_ROCKET_LAUNCHER]) < autocvar_g_balance_rocketlauncher_ammo) - ammo_amount = TRUE; - } - else if(self.ammo_rockets < autocvar_g_balance_rocketlauncher_ammo) - ammo_amount = TRUE; - return !ammo_amount; - } - } - else if (req == WR_CHECKAMMO2) - return FALSE; - else if (req == WR_RESETPLAYER) - { - self.rl_release = 0; - } - else if (req == WR_RELOAD) - { - W_Reload(autocvar_g_balance_rocketlauncher_ammo, autocvar_g_balance_rocketlauncher_reload_ammo, autocvar_g_balance_rocketlauncher_reload_time, "weapons/reload.wav"); - } - else if (req == WR_SUICIDEMESSAGE) - { - return WEAPON_ROCKETLAUNCHER_SUICIDE; - } - else if (req == WR_KILLMESSAGE) - { - if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH)) - return WEAPON_ROCKETLAUNCHER_MURDER_SPLASH; - else - return WEAPON_ROCKETLAUNCHER_MURDER_DIRECT; - } - return TRUE; -} -#endif -#ifdef CSQC -float w_rlauncher(float req) -{ - if(req == WR_IMPACTEFFECT) - { - vector org2; - org2 = w_org + w_backoff * 12; - pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1); - if(!w_issilent) - sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM); - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/rocket_impact.wav"); - } - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_seeker.qc b/qcsrc/server/w_seeker.qc deleted file mode 100644 index c870f0868e..0000000000 --- a/qcsrc/server/w_seeker.qc +++ /dev/null @@ -1,712 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ SEEKER, -/* function */ w_seeker, -/* ammotype */ IT_ROCKETS, -/* impulse */ 8, -/* flags */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH, -/* rating */ BOT_PICKUP_RATING_MID, -/* model */ "seeker", -/* shortname */ "seeker", -/* fullname */ _("T.A.G. Seeker") -); -#else -#ifdef SVQC -//.float proxytime; = autoswitch -//.float tl; = wait -.entity tag_target, wps_tag_tracker; -.float tag_time; - -// ============================ -// Begin: Missile functions, these are general functions to be manipulated by other code -// ============================ -void Seeker_Missile_Explode () -{ - self.event_damage = func_null; - RadiusDamage (self, self.realowner, autocvar_g_balance_seeker_missile_damage, autocvar_g_balance_seeker_missile_edgedamage, autocvar_g_balance_seeker_missile_radius, world, autocvar_g_balance_seeker_missile_force, self.projectiledeathtype, other); - - remove (self); -} - -void Seeker_Missile_Touch() -{ - PROJECTILE_TOUCH; - - Seeker_Missile_Explode(); -} - -void Seeker_Missile_Think() -{ - entity e; - vector desireddir, olddir, newdir, eorg; - float turnrate; - float dist; - float spd; - - if (time > self.cnt) - { - self.projectiledeathtype |= HITTYPE_SPLASH; - Seeker_Missile_Explode(); - } - - spd = vlen(self.velocity); - spd = bound( - spd - autocvar_g_balance_seeker_missile_decel * frametime, - autocvar_g_balance_seeker_missile_speed_max, - spd + autocvar_g_balance_seeker_missile_accel * frametime - ); - - if (self.enemy != world) - if (self.enemy.takedamage != DAMAGE_AIM || self.enemy.deadflag != DEAD_NO) - self.enemy = world; - - if (self.enemy != world) - { - e = self.enemy; - eorg = 0.5 * (e.absmin + e.absmax); - turnrate = autocvar_g_balance_seeker_missile_turnrate; // how fast to turn - desireddir = normalize(eorg - self.origin); - olddir = normalize(self.velocity); // get my current direction - dist = vlen(eorg - self.origin); - - // Do evasive maneuvers for world objects? ( this should be a cpu hog. :P ) - if (autocvar_g_balance_seeker_missile_smart && (dist > autocvar_g_balance_seeker_missile_smart_mindist)) - { - // Is it a better idea (shorter distance) to trace to the target itself? - if ( vlen(self.origin + olddir * self.wait) < dist) - traceline(self.origin, self.origin + olddir * self.wait, FALSE, self); - else - traceline(self.origin, eorg, FALSE, self); - - // Setup adaptive tracelength - self.wait = bound(autocvar_g_balance_seeker_missile_smart_trace_min, vlen(self.origin - trace_endpos), self.wait = autocvar_g_balance_seeker_missile_smart_trace_max); - - // Calc how important it is that we turn and add this to the desierd (enemy) dir. - desireddir = normalize(((trace_plane_normal * (1 - trace_fraction)) + (desireddir * trace_fraction)) * 0.5); - } - - newdir = normalize(olddir + desireddir * turnrate); // take the average of the 2 directions; not the best method but simple & easy - self.velocity = newdir * spd; // make me fly in the new direction at my flight speed - } - else - dist = 0; - - // Proxy - if (autocvar_g_balance_seeker_missile_proxy) - { - if ( dist <= autocvar_g_balance_seeker_missile_proxy_maxrange) - { - if (self.autoswitch == 0) - { - self.autoswitch = time + autocvar_g_balance_seeker_missile_proxy_delay; - } - else - { - if (self.autoswitch <= time) - { - Seeker_Missile_Explode(); - self.autoswitch = 0; - } - } - } - else - { - if (self.autoswitch != 0) - self.autoswitch = 0; - } - } - /////////////// - - if (self.enemy.deadflag != DEAD_NO) - { - self.enemy = world; - self.cnt = time + 1 + (random() * 4); - self.nextthink = self.cnt; - return; - } - - //self.angles = vectoangles(self.velocity); // turn model in the new flight direction - self.nextthink = time;// + 0.05; // csqc projectiles - UpdateCSQCProjectile(self); -} - - - -void Seeker_Missile_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) -{ - if (self.health <= 0) - return; - - if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions - return; // g_projectiles_damage says to halt - - if (self.realowner == attacker) - self.health = self.health - (damage * 0.25); - else - self.health = self.health - damage; - - if (self.health <= 0) - W_PrepareExplosionByDamage(attacker, Seeker_Missile_Explode); -} - -/* -void Seeker_Missile_Animate() -{ - self.frame = self.frame +1; - self.nextthink = time + 0.05; - - if (self.enemy != world) - if (self.enemy.takedamage != DAMAGE_AIM || self.enemy.deadflag != DEAD_NO) - self.enemy = world; - - if(self.frame == 5) - { - self.think = Seeker_Missile_Think; - self.nextthink = time;// + cvar("g_balance_seeker_missile_activate_delay"); // cant dealy with csqc projectiles - - if (autocvar_g_balance_seeker_missile_proxy) - self.movetype = MOVETYPE_BOUNCEMISSILE; - else - self.movetype = MOVETYPE_FLYMISSILE; - } - - UpdateCSQCProjectile(self); -} -*/ - -void Seeker_Fire_Missile(vector f_diff, entity m_target) -{ - entity missile; - - W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_seeker_missile_ammo, autocvar_g_balance_seeker_reload_ammo); - - makevectors(self.v_angle); - W_SetupShot_ProjectileSize (self, '-2 -2 -2', '2 2 2', FALSE, 2, "weapons/seeker_fire.wav", CH_WEAPON_A, 0); - w_shotorg += f_diff; - pointparticles(particleeffectnum("seeker_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - //self.detornator = FALSE; - - missile = spawn(); - missile.owner = missile.realowner = self; - missile.classname = "seeker_missile"; - missile.bot_dodge = TRUE; - missile.bot_dodgerating = autocvar_g_balance_seeker_missile_damage; - - missile.think = Seeker_Missile_Think; - missile.touch = Seeker_Missile_Touch; - missile.event_damage = Seeker_Missile_Damage; - missile.nextthink = time;// + 0.2;// + cvar("g_balance_seeker_missile_activate_delay"); - missile.cnt = time + autocvar_g_balance_seeker_missile_lifetime; - missile.enemy = m_target; - missile.solid = SOLID_BBOX; - missile.scale = 2; - missile.takedamage = DAMAGE_YES; - missile.health = autocvar_g_balance_seeker_missile_health; - missile.damageforcescale = autocvar_g_balance_seeker_missile_damageforcescale; - missile.damagedbycontents = TRUE; - //missile.think = Seeker_Missile_Animate; // csqc projectiles. - - if (missile.enemy != world) - missile.projectiledeathtype = WEP_SEEKER | HITTYPE_SECONDARY; - else - missile.projectiledeathtype = WEP_SEEKER; - - - setorigin (missile, w_shotorg); - setsize (missile, '-4 -4 -4', '4 4 4'); - missile.movetype = MOVETYPE_FLYMISSILE; - missile.flags = FL_PROJECTILE; - missile.missile_flags = MIF_SPLASH | MIF_GUIDED_TAG; - - W_SETUPPROJECTILEVELOCITY_UP(missile, g_balance_seeker_missile); - - missile.angles = vectoangles (missile.velocity); - - CSQCProjectile(missile, FALSE, PROJECTILE_SEEKER, TRUE); - - other = missile; MUTATOR_CALLHOOK(EditProjectile); -} - -// ============================ -// Begin: FLAC, close range attack meant for defeating rockets which are coming at you. -// ============================ -void Seeker_Flac_Explode () -{ - self.event_damage = func_null; - - RadiusDamage (self, self.realowner, autocvar_g_balance_seeker_flac_damage, autocvar_g_balance_seeker_flac_edgedamage, autocvar_g_balance_seeker_flac_radius, world, autocvar_g_balance_seeker_flac_force, self.projectiledeathtype, other); - - remove (self); -} - -void Seeker_Flac_Touch() -{ - PROJECTILE_TOUCH; - - Seeker_Flac_Explode(); -} - -void Seeker_Fire_Flac() -{ - entity missile; - vector f_diff; - float c; - - W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_seeker_flac_ammo, autocvar_g_balance_seeker_reload_ammo); - - c = mod(self.bulletcounter, 4); - switch(c) - { - case 0: - f_diff = '-1.25 -3.75 0'; - break; - case 1: - f_diff = '+1.25 -3.75 0'; - break; - case 2: - f_diff = '-1.25 +3.75 0'; - break; - case 3: - default: - f_diff = '+1.25 +3.75 0'; - break; - } - W_SetupShot_ProjectileSize (self, '-2 -2 -2', '2 2 2', FALSE, 2, "weapons/flac_fire.wav", CH_WEAPON_A, autocvar_g_balance_seeker_flac_damage); - w_shotorg += f_diff; - - pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - missile = spawn (); - missile.owner = missile.realowner = self; - missile.classname = "missile"; - missile.bot_dodge = TRUE; - missile.bot_dodgerating = autocvar_g_balance_seeker_flac_damage; - missile.touch = Seeker_Flac_Explode; - missile.use = Seeker_Flac_Explode; - missile.think = adaptor_think2use_hittype_splash; - missile.nextthink = time + autocvar_g_balance_seeker_flac_lifetime + autocvar_g_balance_seeker_flac_lifetime_rand; - missile.solid = SOLID_BBOX; - missile.movetype = MOVETYPE_FLY; - missile.projectiledeathtype = WEP_SEEKER; - missile.projectiledeathtype = WEP_SEEKER | HITTYPE_SECONDARY; - missile.flags = FL_PROJECTILE; - missile.missile_flags = MIF_SPLASH; - - // csqc projectiles - //missile.angles = vectoangles (missile.velocity); - //missile.scale = 0.4; // BUG: the model is too big - - setorigin (missile, w_shotorg); - setsize (missile, '-2 -2 -2', '2 2 2'); - - W_SETUPPROJECTILEVELOCITY_UP(missile, g_balance_seeker_flac); - CSQCProjectile(missile, TRUE, PROJECTILE_FLAC, TRUE); - - other = missile; MUTATOR_CALLHOOK(EditProjectile); -} - -// ============================ -// Begin: Tag and rocket controllers -// ============================ -entity Seeker_Tagged_Info(entity isowner, entity istarget) -{ - entity tag; - for(tag = world; (tag = find(tag, classname, "tag_tracker")); ) - if ((tag.realowner == isowner) && (tag.tag_target == istarget)) - return tag; - - return world; -} - -void Seeker_Attack() -{ - entity tracker, closest_target; - - closest_target = world; - for(tracker = world; (tracker = find(tracker, classname, "tag_tracker")); ) if (tracker.realowner == self) - { - if (closest_target) - { - if (vlen(self.origin - tracker.tag_target.origin) < vlen(self.origin - closest_target.origin)) - closest_target = tracker.tag_target; - } - else - closest_target = tracker.tag_target; - } - - traceline(self.origin + self.view_ofs, closest_target.origin, MOVE_NOMONSTERS, self); - if ((!closest_target) || ((trace_fraction < 1) && (trace_ent != closest_target))) - closest_target = world; - - Seeker_Fire_Missile('0 0 0', closest_target); -} - -void Seeker_Vollycontroller_Think() // TODO: Merge this with Seeker_Attack -{ - float c; - entity oldself,oldenemy; - self.cnt = self.cnt - 1; - - if((!(self.realowner.items & IT_UNLIMITED_AMMO) && self.realowner.ammo_rockets < autocvar_g_balance_seeker_missile_ammo) || (self.cnt <= -1) || (self.realowner.deadflag != DEAD_NO) || (self.realowner.switchweapon != WEP_SEEKER)) - { - remove(self); - return; - } - - self.nextthink = time + autocvar_g_balance_seeker_missile_delay * W_WeaponRateFactor(); - - oldself = self; - self = self.realowner; - - oldenemy = self.enemy; - self.enemy = oldself.enemy; - - c = mod(self.cnt, 4); - switch(c) - { - case 0: - Seeker_Fire_Missile('-1.25 -3.75 0', self.enemy); - break; - case 1: - Seeker_Fire_Missile('+1.25 -3.75 0', self.enemy); - break; - case 2: - Seeker_Fire_Missile('-1.25 +3.75 0', self.enemy); - break; - case 3: - default: - Seeker_Fire_Missile('+1.25 +3.75 0', self.enemy); - break; - } - - self.enemy = oldenemy; - self = oldself; -} - -void Seeker_Tracker_Think() -{ - // commit suicide if: You die OR target dies OR you switch away from the seeker OR commit suicide if lifetime is up - if ((self.realowner.deadflag != DEAD_NO) || (self.tag_target.deadflag != DEAD_NO) || (self.realowner.switchweapon != WEP_SEEKER) - || (time > self.tag_time + autocvar_g_balance_seeker_tag_tracker_lifetime)) - { - if (self) - { - WaypointSprite_Kill(self.tag_target.wps_tag_tracker); - remove(self); - } - return; - } - - // Update the think method information - self.nextthink = time; -} - -// ============================ -// Begin: Tag projectile -// ============================ -void Seeker_Tag_Explode () -{ - //if(other==self.realowner) - // return; - Damage_DamageInfo(self.origin, 0, 0, 0, self.velocity, WEP_SEEKER | HITTYPE_BOUNCE, other.species, self); - - remove (self); -} - -void Seeker_Tag_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force) -{ - if (self.health <= 0) - return; - self.health = self.health - damage; - if (self.health <= 0) - Seeker_Tag_Explode(); -} - -void Seeker_Tag_Touch() -{ - vector dir; - vector org2; - entity e; - - PROJECTILE_TOUCH; - - dir = normalize (self.realowner.origin - self.origin); - org2 = findbetterlocation (self.origin, 8); - - te_knightspike(org2); - - self.event_damage = func_null; - Damage_DamageInfo(self.origin, 0, 0, 0, self.velocity, WEP_SEEKER | HITTYPE_BOUNCE | HITTYPE_SECONDARY, other.species, self); - - if (other.takedamage == DAMAGE_AIM && other.deadflag == DEAD_NO) - { - // check to see if this person is already tagged by me - entity tag = Seeker_Tagged_Info(self.realowner, other); - - if (tag != world) - { - if (other.wps_tag_tracker && (autocvar_g_balance_seeker_type == 1)) // don't attach another waypointsprite without killing the old one first - WaypointSprite_Kill(other.wps_tag_tracker); - - tag.tag_time = time; - } - else - { - //sprint(self.realowner, strcat("You just tagged ^2", other.netname, "^7 with a tracking device!\n")); - e = spawn(); - e.cnt = autocvar_g_balance_seeker_missile_count; - e.classname = "tag_tracker"; - e.owner = self.owner; - e.realowner = self.realowner; - - if (autocvar_g_balance_seeker_type == 1) - { - e.tag_target = other; - e.tag_time = time; - e.think = Seeker_Tracker_Think; - } - else - { - e.enemy = other; - e.think = Seeker_Vollycontroller_Think; - } - - e.nextthink = time; - } - - if (autocvar_g_balance_seeker_type == 1) - { - WaypointSprite_Spawn("tagged-target", autocvar_g_balance_seeker_tag_tracker_lifetime, 0, other, '0 0 64', self.realowner, 0, other, wps_tag_tracker, TRUE, RADARICON_TAGGED, '0.5 1 0'); - WaypointSprite_UpdateRule(other.wps_tag_tracker, 0, SPRITERULE_DEFAULT); - } - } - - remove(self); - return; -} - -void Seeker_Fire_Tag() -{ - entity missile; - W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_seeker_tag_ammo, autocvar_g_balance_seeker_reload_ammo); - - W_SetupShot_ProjectileSize (self, '-2 -2 -2', '2 2 2', FALSE, 2, "weapons/tag_fire.wav", CH_WEAPON_A, autocvar_g_balance_seeker_missile_damage * autocvar_g_balance_seeker_missile_count); - - missile = spawn(); - missile.owner = missile.realowner = self; - missile.classname = "seeker_tag"; - missile.bot_dodge = TRUE; - missile.bot_dodgerating = 50; - missile.touch = Seeker_Tag_Touch; - missile.think = SUB_Remove; - missile.nextthink = time + autocvar_g_balance_seeker_tag_lifetime; - missile.movetype = MOVETYPE_FLY; - missile.solid = SOLID_BBOX; - - missile.takedamage = DAMAGE_YES; - missile.event_damage = Seeker_Tag_Damage; - missile.health = autocvar_g_balance_seeker_tag_health; - missile.damageforcescale = autocvar_g_balance_seeker_tag_damageforcescale; - - setorigin (missile, w_shotorg); - setsize (missile, '-2 -2 -2', '2 2 2'); - - missile.flags = FL_PROJECTILE; - //missile.missile_flags = MIF_..?; - - missile.movetype = MOVETYPE_FLY; - W_SETUPPROJECTILEVELOCITY(missile, g_balance_seeker_tag); - missile.angles = vectoangles (missile.velocity); - - CSQCProjectile(missile, TRUE, PROJECTILE_TAG, FALSE); // has sound - - other = missile; MUTATOR_CALLHOOK(EditProjectile); -} - -// ============================ -// Begin: Genereal weapon functions -// ============================ -void spawnfunc_weapon_seeker (void) -{ - weapon_defaultspawnfunc(WEP_SEEKER); -} - -float w_seeker(float req) -{ - float ammo_amount; - - if (req == WR_AIM) - { - if (autocvar_g_balance_seeker_type == 1) - if (Seeker_Tagged_Info(self, self.enemy) != world) - self.BUTTON_ATCK = bot_aim(autocvar_g_balance_seeker_missile_speed_max, 0, autocvar_g_balance_seeker_missile_lifetime, FALSE); - else - self.BUTTON_ATCK2 = bot_aim(autocvar_g_balance_seeker_tag_speed, 0, autocvar_g_balance_seeker_tag_lifetime, FALSE); - else - self.BUTTON_ATCK = bot_aim(autocvar_g_balance_seeker_tag_speed, 0, autocvar_g_balance_seeker_tag_lifetime, FALSE); - } - else if (req == WR_THINK) - { - if(autocvar_g_balance_seeker_reload_ammo && self.clip_load < min(autocvar_g_balance_seeker_missile_ammo, autocvar_g_balance_seeker_tag_ammo)) // forced reload - weapon_action(self.weapon, WR_RELOAD); - - else if (self.BUTTON_ATCK) - { - if (autocvar_g_balance_seeker_type == 1) - { - if (weapon_prepareattack(0, autocvar_g_balance_seeker_missile_refire)) - { - Seeker_Attack(); - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_seeker_missile_animtime, w_ready); - } - } - else - { - if (weapon_prepareattack(0, autocvar_g_balance_seeker_tag_refire)) - { - Seeker_Fire_Tag(); - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_seeker_tag_animtime, w_ready); - } - } - } - - else if (self.BUTTON_ATCK2) - { - if (autocvar_g_balance_seeker_type == 1) - { - if (weapon_prepareattack(0, autocvar_g_balance_seeker_tag_refire)) - { - Seeker_Fire_Tag(); - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_seeker_tag_animtime, w_ready); - } - } - else - { - if (weapon_prepareattack(0, autocvar_g_balance_seeker_flac_refire)) - { - Seeker_Fire_Flac(); - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_seeker_flac_animtime, w_ready); - } - } - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/weapons/g_seeker.md3"); - precache_model ("models/weapons/v_seeker.md3"); - precache_model ("models/weapons/h_seeker.iqm"); - precache_sound ("weapons/tag_fire.wav"); - precache_sound ("weapons/flac_fire.wav"); - precache_sound ("weapons/seeker_fire.wav"); - //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_SEEKER); - self.current_ammo = ammo_rockets; - } - else if (req == WR_CHECKAMMO1) - { - if (autocvar_g_balance_seeker_type == 1) - { - ammo_amount = self.ammo_rockets >= autocvar_g_balance_seeker_missile_ammo; - ammo_amount += self.(weapon_load[WEP_SEEKER]) >= autocvar_g_balance_seeker_missile_ammo; - } - else - { - ammo_amount = self.ammo_rockets >= autocvar_g_balance_seeker_tag_ammo; - ammo_amount += self.(weapon_load[WEP_SEEKER]) >= autocvar_g_balance_seeker_tag_ammo; - } - - return ammo_amount; - } - else if (req == WR_CHECKAMMO2) - { - if (autocvar_g_balance_seeker_type == 1) - { - ammo_amount = self.ammo_rockets >= autocvar_g_balance_seeker_tag_ammo; - ammo_amount += self.(weapon_load[WEP_SEEKER]) >= autocvar_g_balance_seeker_tag_ammo; - } - else - { - ammo_amount = self.ammo_rockets >= autocvar_g_balance_seeker_flac_ammo; - ammo_amount += self.(weapon_load[WEP_SEEKER]) >= autocvar_g_balance_seeker_flac_ammo; - } - - return ammo_amount; - } - else if (req == WR_RELOAD) - { - W_Reload(min(autocvar_g_balance_seeker_missile_ammo, autocvar_g_balance_seeker_tag_ammo), autocvar_g_balance_seeker_reload_ammo, autocvar_g_balance_seeker_reload_time, "weapons/reload.wav"); - } - else if (req == WR_SUICIDEMESSAGE) - { - return WEAPON_SEEKER_SUICIDE; - } - else if (req == WR_KILLMESSAGE) - { - if(w_deathtype & HITTYPE_SECONDARY) - return WEAPON_SEEKER_MURDER_TAG; - else - return WEAPON_SEEKER_MURDER_SPRAY; - } - return TRUE; -} -#endif -#ifdef CSQC -float w_seeker(float req) -{ - if(req == WR_IMPACTEFFECT) - { - vector org2; - org2 = w_org + w_backoff * 6; - if(w_deathtype & HITTYPE_BOUNCE) - { - if(w_deathtype & HITTYPE_SECONDARY) - { - if(!w_issilent) - sound(self, CH_SHOTS, "weapons/tag_impact.wav", 1, ATTEN_NORM); - } - else - { - pointparticles(particleeffectnum("hagar_explode"), org2, '0 0 0', 1); - if(!w_issilent) - { - if (w_random<0.15) - sound(self, CH_SHOTS, "weapons/tagexp1.wav", 1, ATTEN_NORM); - else if (w_random<0.7) - sound(self, CH_SHOTS, "weapons/tagexp2.wav", 1, ATTEN_NORM); - else - sound(self, CH_SHOTS, "weapons/tagexp3.wav", 1, ATTEN_NORM); - } - } - } - else - { - pointparticles(particleeffectnum("hagar_explode"), org2, '0 0 0', 1); - if(!w_issilent) - { - if (w_random<0.15) - sound(self, CH_SHOTS, "weapons/seekerexp1.wav", 1, ATTEN_NORM); - else if (w_random<0.7) - sound(self, CH_SHOTS, "weapons/seekerexp2.wav", 1, ATTEN_NORM); - else - sound(self, CH_SHOTS, "weapons/seekerexp3.wav", 1, ATTEN_NORM); - } - } - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/seekerexp1.wav"); - precache_sound("weapons/seekerexp2.wav"); - precache_sound("weapons/seekerexp3.wav"); - precache_sound("weapons/tagexp1.wav"); - precache_sound("weapons/tagexp2.wav"); - precache_sound("weapons/tagexp3.wav"); - precache_sound("weapons/tag_impact.wav"); - } - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_shotgun.qc b/qcsrc/server/w_shotgun.qc deleted file mode 100644 index 173f8c1a52..0000000000 --- a/qcsrc/server/w_shotgun.qc +++ /dev/null @@ -1,287 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ SHOTGUN, -/* function */ w_shotgun, -/* ammotype */ IT_SHELLS, -/* impulse */ 2, -/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN, -/* rating */ BOT_PICKUP_RATING_LOW, -/* model */ "shotgun", -/* shortname */ "shotgun", -/* fullname */ _("Shotgun") -); -#else -#ifdef SVQC - -void W_Shotgun_Attack (void) -{ - float sc; - float ammoamount; - float bullets; - float d; - float f; - float spread; - float solidpenetration; - entity flash; - - ammoamount = autocvar_g_balance_shotgun_primary_ammo; - bullets = autocvar_g_balance_shotgun_primary_bullets; - d = autocvar_g_balance_shotgun_primary_damage; - f = autocvar_g_balance_shotgun_primary_force; - spread = autocvar_g_balance_shotgun_primary_spread; - solidpenetration = autocvar_g_balance_shotgun_primary_solidpenetration; - - W_DecreaseAmmo(ammo_shells, ammoamount, autocvar_g_balance_shotgun_reload_ammo); - - W_SetupShot (self, TRUE, 5, "weapons/shotgun_fire.wav", CH_WEAPON_A, d * bullets); - for (sc = 0;sc < bullets;sc = sc + 1) - fireBullet(w_shotorg, w_shotdir, spread, solidpenetration, d, f, WEP_SHOTGUN, 0); - - pointparticles(particleeffectnum("shotgun_muzzleflash"), w_shotorg, w_shotdir * 1000, autocvar_g_balance_shotgun_primary_ammo); - - // casing code - if (autocvar_g_casings >= 1) - for (sc = 0;sc < ammoamount;sc = sc + 1) - SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 30) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 1, self); - - // muzzle flash for 1st person view - flash = spawn(); - setmodel(flash, "models/uziflash.md3"); // precision set below - flash.think = SUB_Remove; - flash.nextthink = time + 0.06; - flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; - W_AttachToShotorg(flash, '5 0 0'); -} - -.float swing_prev; -.entity swing_alreadyhit; -void shotgun_meleethink (void) -{ - // declarations - float i, f, swing, swing_factor, swing_damage, meleetime, is_player, is_monster; - entity target_victim; - vector targpos; - - if(!self.cnt) // set start time of melee - { - self.cnt = time; - W_PlayStrengthSound(self.realowner); - } - - makevectors(self.realowner.v_angle); // update values for v_* vectors - - // calculate swing percentage based on time - meleetime = autocvar_g_balance_shotgun_secondary_melee_time * W_WeaponRateFactor(); - swing = bound(0, (self.cnt + meleetime - time) / meleetime, 10); - f = ((1 - swing) * autocvar_g_balance_shotgun_secondary_melee_traces); - - // check to see if we can still continue, otherwise give up now - if((self.realowner.deadflag != DEAD_NO) && autocvar_g_balance_shotgun_secondary_melee_no_doubleslap) - { - remove(self); - return; - } - - // if okay, perform the traces needed for this frame - for(i=self.swing_prev; i < f; ++i) - { - swing_factor = ((1 - (i / autocvar_g_balance_shotgun_secondary_melee_traces)) * 2 - 1); - - targpos = (self.realowner.origin + self.realowner.view_ofs - + (v_forward * autocvar_g_balance_shotgun_secondary_melee_range) - + (v_up * swing_factor * autocvar_g_balance_shotgun_secondary_melee_swing_up) - + (v_right * swing_factor * autocvar_g_balance_shotgun_secondary_melee_swing_side)); - - WarpZone_traceline_antilag(self, self.realowner.origin + self.realowner.view_ofs, targpos, FALSE, self, ANTILAG_LATENCY(self.realowner)); - - // draw lightning beams for debugging - //te_lightning2(world, targpos, self.realowner.origin + self.realowner.view_ofs + v_forward * 5 - v_up * 5); - //te_customflash(targpos, 40, 2, '1 1 1'); - - is_player = (IS_PLAYER(trace_ent) || trace_ent.classname == "body"); - is_monster = (trace_ent.flags & FL_MONSTER); - - if((trace_fraction < 1) // if trace is good, apply the damage and remove self - && (trace_ent.takedamage == DAMAGE_AIM) - && (trace_ent != self.swing_alreadyhit) - && ((is_player || is_monster) || autocvar_g_balance_shotgun_secondary_melee_nonplayerdamage)) - { - target_victim = trace_ent; // so it persists through other calls - - if(is_player || is_monster) // this allows us to be able to nerf the non-player damage done in e.g. assault or onslaught. - swing_damage = (autocvar_g_balance_shotgun_secondary_damage * min(1, swing_factor + 1)); - else - swing_damage = (autocvar_g_balance_shotgun_secondary_melee_nonplayerdamage * min(1, swing_factor + 1)); - - //print(strcat(self.realowner.netname, " hitting ", target_victim.netname, " with ", strcat(ftos(swing_damage), " damage (factor: ", ftos(swing_factor), ") at "), ftos(time), " seconds.\n")); - - Damage(target_victim, self.realowner, self.realowner, - swing_damage, WEP_SHOTGUN | HITTYPE_SECONDARY, - self.realowner.origin + self.realowner.view_ofs, - v_forward * autocvar_g_balance_shotgun_secondary_force); - - if(accuracy_isgooddamage(self.realowner, target_victim)) { accuracy_add(self.realowner, WEP_SHOTGUN, 0, swing_damage); } - - // draw large red flash for debugging - //te_customflash(targpos, 200, 2, '15 0 0'); - - if(autocvar_g_balance_shotgun_secondary_melee_multihit) // allow multiple hits with one swing, but not against the same player twice. - { - self.swing_alreadyhit = target_victim; - continue; // move along to next trace - } - else - { - remove(self); - return; - } - } - } - - if(time >= self.cnt + meleetime) - { - // melee is finished - remove(self); - return; - } - else - { - // set up next frame - self.swing_prev = i; - self.nextthink = time; - } -} - -void W_Shotgun_Attack2 (void) -{ - sound (self, CH_WEAPON_A, "weapons/shotgun_melee.wav", VOL_BASE, ATTEN_NORM); - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_shotgun_secondary_animtime, w_ready); - - entity meleetemp; - meleetemp = spawn(); - meleetemp.realowner = self; - meleetemp.think = shotgun_meleethink; - meleetemp.nextthink = time + autocvar_g_balance_shotgun_secondary_melee_delay * W_WeaponRateFactor(); - W_SetupShot_Range(self, TRUE, 0, "", 0, autocvar_g_balance_shotgun_secondary_damage, autocvar_g_balance_shotgun_secondary_melee_range); -} - -void spawnfunc_weapon_shotgun(); // defined in t_items.qc - -.float shotgun_primarytime; - -float w_shotgun(float req) -{ - float ammo_amount; - if (req == WR_AIM) - if(vlen(self.origin-self.enemy.origin) <= autocvar_g_balance_shotgun_secondary_melee_range) - self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE); - else - self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE); - - else if (req == WR_THINK) - { - if(autocvar_g_balance_shotgun_reload_ammo && self.clip_load < autocvar_g_balance_shotgun_primary_ammo) // forced reload - { - // don't force reload an empty shotgun if its melee attack is active - if (!(autocvar_g_balance_shotgun_secondary && self.ammo_shells < autocvar_g_balance_shotgun_primary_ammo)) - weapon_action(self.weapon, WR_RELOAD); - } - else - { - if (self.BUTTON_ATCK) - { - if (time >= self.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary - { - if(weapon_prepareattack(0, autocvar_g_balance_shotgun_primary_animtime)) - { - W_Shotgun_Attack(); - self.shotgun_primarytime = time + autocvar_g_balance_shotgun_primary_refire * W_WeaponRateFactor(); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_shotgun_primary_animtime, w_ready); - } - } - } - } - if (self.clip_load >= 0) // we are not currently reloading - if (!self.crouch) // no crouchmelee please - if (self.BUTTON_ATCK2 && autocvar_g_balance_shotgun_secondary) - if (weapon_prepareattack(1, autocvar_g_balance_shotgun_secondary_refire)) - { - // attempt forcing playback of the anim by switching to another anim (that we never play) here... - weapon_thinkf(WFRAME_FIRE1, 0, W_Shotgun_Attack2); - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/uziflash.md3"); - precache_model ("models/weapons/g_shotgun.md3"); - precache_model ("models/weapons/v_shotgun.md3"); - precache_model ("models/weapons/h_shotgun.iqm"); - precache_sound ("misc/itempickup.wav"); - precache_sound ("weapons/shotgun_fire.wav"); - precache_sound ("weapons/shotgun_melee.wav"); - //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_SHOTGUN); - self.current_ammo = ammo_shells; - } - else if (req == WR_CHECKAMMO1) - { - ammo_amount = self.ammo_shells >= autocvar_g_balance_shotgun_primary_ammo; - ammo_amount += self.(weapon_load[WEP_SHOTGUN]) >= autocvar_g_balance_shotgun_primary_ammo; - return ammo_amount; - } - else if (req == WR_CHECKAMMO2) - { - // melee attack is always available - return TRUE; - } - else if (req == WR_RELOAD) - { - W_Reload(autocvar_g_balance_shotgun_primary_ammo, autocvar_g_balance_shotgun_reload_ammo, autocvar_g_balance_shotgun_reload_time, "weapons/reload.wav"); - } - else if (req == WR_SUICIDEMESSAGE) - { - return WEAPON_THINKING_WITH_PORTALS; - } - else if (req == WR_KILLMESSAGE) - { - if(w_deathtype & HITTYPE_SECONDARY) - return WEAPON_SHOTGUN_MURDER_SLAP; - else - return WEAPON_SHOTGUN_MURDER; - } - return TRUE; -} -#endif -#ifdef CSQC -.float prevric; -float w_shotgun(float req) -{ - if(req == WR_IMPACTEFFECT) - { - vector org2; - org2 = w_org + w_backoff * 2; - pointparticles(particleeffectnum("shotgun_impact"), org2, w_backoff * 1000, 1); - if(!w_issilent && time - self.prevric > 0.25) - { - if(w_random < 0.0165) - sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTEN_NORM); - else if(w_random < 0.033) - sound(self, CH_SHOTS, "weapons/ric2.wav", VOL_BASE, ATTEN_NORM); - else if(w_random < 0.05) - sound(self, CH_SHOTS, "weapons/ric3.wav", VOL_BASE, ATTEN_NORM); - self.prevric = time; - } - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/ric1.wav"); - precache_sound("weapons/ric2.wav"); - precache_sound("weapons/ric3.wav"); - } - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_tuba.qc b/qcsrc/server/w_tuba.qc deleted file mode 100644 index 2e081095f1..0000000000 --- a/qcsrc/server/w_tuba.qc +++ /dev/null @@ -1,466 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ TUBA, -/* function */ w_tuba, -/* ammotype */ 0, -/* impulse */ 1, -/* flags */ WEP_FLAG_HIDDEN | WEP_TYPE_SPLASH, -/* rating */ BOT_PICKUP_RATING_MID, -/* model */ "tuba", -/* shortname */ "tuba", -/* xgettext:no-c-format */ -/* fullname */ _("@!#%'n Tuba") -); -#else -#ifdef SVQC -//#define TUBA_NOTE(n) strcat("weapons/tuba_note", ftos(n), ".wav") -.entity tuba_note; -.float tuba_smoketime; -.float tuba_instrument; - -#define MAX_TUBANOTES 32 -.float tuba_lastnotes_last; -.float tuba_lastnotes_cnt; // over -.vector tuba_lastnotes[MAX_TUBANOTES]; - -float W_Tuba_HasPlayed(entity pl, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo) -{ - float i, j, mmin, mmax, nolength; - float n = tokenize_console(melody); - if(n > pl.tuba_lastnotes_cnt) - return FALSE; - float pitchshift = 0; - - if(instrument >= 0) - if(pl.tuba_instrument != instrument) - return FALSE; - - // verify notes... - nolength = FALSE; - for(i = 0; i < n; ++i) - { - vector v = pl.(tuba_lastnotes[mod(pl.tuba_lastnotes_last - i + MAX_TUBANOTES, MAX_TUBANOTES)]); - float ai = stof(argv(n - i - 1)); - float np = floor(ai); - if(ai == np) - nolength = TRUE; - // n counts the last played notes BACKWARDS - // _x is start - // _y is end - // _z is note pitch - if(ignorepitch && i == 0) - { - pitchshift = np - v_z; - } - else - { - if(v_z + pitchshift != np) - return FALSE; - } - } - - // now we know the right NOTES were played - if(!nolength) - { - // verify rhythm... - float ti = 0; - if(maxtempo > 0) - mmin = 240 / maxtempo; // 60 = "0.25 means 1 sec", at 120 0.5 means 1 sec, at 240 1 means 1 sec - else - mmin = 0; - if(mintempo > 0) - mmax = 240 / mintempo; // 60 = "0.25 means 1 sec", at 120 0.5 means 1 sec, at 240 1 means 1 sec - else - mmax = 240; // you won't try THAT hard... (tempo 1) - //printf("initial tempo rules: %f %f\n", mmin, mmax); - - for(i = 0; i < n; ++i) - { - vector vi = pl.(tuba_lastnotes[mod(pl.tuba_lastnotes_last - i + MAX_TUBANOTES, MAX_TUBANOTES)]); - float ai = stof(argv(n - i - 1)); - ti -= 1 / (ai - floor(ai)); - float tj = ti; - for(j = i+1; j < n; ++j) - { - vector vj = pl.(tuba_lastnotes[mod(pl.tuba_lastnotes_last - j + MAX_TUBANOTES, MAX_TUBANOTES)]); - float aj = stof(argv(n - j - 1)); - tj -= (aj - floor(aj)); - - // note i should be at m*ti+b - // note j should be at m*tj+b - // so: - // we have a LINE l, so that - // vi_x <= l(ti) <= vi_y - // vj_x <= l(tj) <= vj_y - // what is m? - - // vi_x <= vi_y <= vj_x <= vj_y - // ti <= tj - //printf("first note: %f to %f, should be %f\n", vi_x, vi_y, ti); - //printf("second note: %f to %f, should be %f\n", vj_x, vj_y, tj); - //printf("m1 = %f\n", (vi_x - vj_y) / (ti - tj)); - //printf("m2 = %f\n", (vi_y - vj_x) / (ti - tj)); - mmin = max(mmin, (vi_x - vj_y) / (ti - tj)); // lower bound - mmax = min(mmax, (vi_y - vj_x) / (ti - tj)); // upper bound - } - } - - if(mmin > mmax) // rhythm fail - return FALSE; - } - - pl.tuba_lastnotes_cnt = 0; - - return TRUE; -} - -void W_Tuba_NoteOff() -{ - // we have a note: - // on: self.spawnshieldtime - // off: time - // note: self.cnt - if(self.owner.tuba_note == self) - { - self.owner.tuba_lastnotes_last = mod(self.owner.tuba_lastnotes_last + 1, MAX_TUBANOTES); - self.owner.(tuba_lastnotes[self.owner.tuba_lastnotes_last]) = eX * self.spawnshieldtime + eY * time + eZ * self.cnt; - self.owner.tuba_note = world; - self.owner.tuba_lastnotes_cnt = bound(0, self.owner.tuba_lastnotes_cnt + 1, MAX_TUBANOTES); - - string s; - s = trigger_magicear_processmessage_forallears(self.owner, 0, world, string_null); - if(s != "") - { - // simulate a server message - switch(self.tuba_instrument) - { - default: - case 0: // Tuba - bprint(strcat("\{1}\{13}* ^3", self.owner.netname, "^3 played on the @!#%'n Tuba: ^7", s, "\n")); - break; - case 1: - bprint(strcat("\{1}\{13}* ^3", self.owner.netname, "^3 played on the @!#%'n Accordeon: ^7", s, "\n")); - break; - case 2: - bprint(strcat("\{1}\{13}* ^3", self.owner.netname, "^3 played on the @!#%'n Klein Bottle: ^7", s, "\n")); - break; - } - } - } - remove(self); -} - -float Tuba_GetNote(entity pl, float hittype) -{ - float note; - float movestate; - movestate = 5; - if(pl.movement_x < 0) movestate -= 3; - if(pl.movement_x > 0) movestate += 3; - if(pl.movement_y < 0) movestate -= 1; - if(pl.movement_y > 0) movestate += 1; -#ifdef GMQCC - note = 0; -#endif - switch(movestate) - { - // layout: originally I wanted - // eb e e#=f - // B c d - // Gb G G# - // but then you only use forward and right key. So to make things more - // interesting, I swapped B with e#. Har har har... - // eb e B - // f=e# c d - // Gb G G# - case 1: note = -6; break; // Gb - case 2: note = -5; break; // G - case 3: note = -4; break; // G# - case 4: note = +5; break; // e# - default: - case 5: note = 0; break; // c - case 6: note = +2; break; // d - case 7: note = +3; break; // eb - case 8: note = +4; break; // e - case 9: note = -1; break; // B - } - if(pl.BUTTON_CROUCH) - note -= 12; - if(pl.BUTTON_JUMP) - note += 12; - if(hittype & HITTYPE_SECONDARY) - note += 7; - - // we support two kinds of tubas, those tuned in Eb and those tuned in C - // kind of tuba currently is player slot number, or team number if in - // teamplay - // that way, holes in the range of notes are "plugged" - if(teamplay) - { - if(pl.team == NUM_TEAM_2 || pl.team == NUM_TEAM_4) - note += 3; - } - else - { - if(pl.clientcolors & 1) - note += 3; - } - - // total range of notes: - // 0 - // *** ** **** - // *** ** **** - // *** ** **** - // *** ** **** - // *** ********************* **** - // -18.........................+12 - // *** ********************* **** - // -18............................+15 - // with jump: ... +24 - // ... +27 - return note; -} - -float W_Tuba_NoteSendEntity(entity to, float sf) -{ - float f; - - msg_entity = to; - if(!sound_allowed(MSG_ONE, self.realowner)) - return FALSE; - - WriteByte(MSG_ENTITY, ENT_CLIENT_TUBANOTE); - WriteByte(MSG_ENTITY, sf); - if(sf & 1) - { - WriteChar(MSG_ENTITY, self.cnt); - f = 0; - if(self.realowner != to) - f |= 1; - f |= 2 * self.tuba_instrument; - WriteByte(MSG_ENTITY, f); - } - if(sf & 2) - { - WriteCoord(MSG_ENTITY, self.origin_x); - WriteCoord(MSG_ENTITY, self.origin_y); - WriteCoord(MSG_ENTITY, self.origin_z); - } - return TRUE; -} - -void W_Tuba_NoteThink() -{ - float dist_mult; - float vol0, vol1; - vector dir0, dir1; - vector v; - entity e; - if(time > self.teleport_time) - { - W_Tuba_NoteOff(); - return; - } - self.nextthink = time; - dist_mult = autocvar_g_balance_tuba_attenuation / autocvar_snd_soundradius; - FOR_EACH_REALCLIENT(e) - if(e != self.realowner) - { - v = self.origin - (e.origin + e.view_ofs); - vol0 = max(0, 1 - vlen(v) * dist_mult); - dir0 = normalize(v); - v = self.realowner.origin - (e.origin + e.view_ofs); - vol1 = max(0, 1 - vlen(v) * dist_mult); - dir1 = normalize(v); - if(fabs(vol0 - vol1) > 0.005) // 0.5 percent change in volume - { - setorigin(self, self.realowner.origin); - self.SendFlags |= 2; - break; - } - if(dir0 * dir1 < 0.9994) // 2 degrees change in angle - { - setorigin(self, self.realowner.origin); - self.SendFlags |= 2; - break; - } - } -} - -void W_Tuba_NoteOn(float hittype) -{ - vector o; - float n; - - W_SetupShot(self, FALSE, 2, "", 0, autocvar_g_balance_tuba_damage); - - n = Tuba_GetNote(self, hittype); - - hittype = 0; - if(self.tuba_instrument & 1) - hittype |= HITTYPE_SECONDARY; - if(self.tuba_instrument & 2) - hittype |= HITTYPE_BOUNCE; - - if(self.tuba_note) - { - if(self.tuba_note.cnt != n || self.tuba_note.tuba_instrument != self.tuba_instrument) - { - entity oldself = self; - self = self.tuba_note; - W_Tuba_NoteOff(); - self = oldself; - } - } - - if (!self.tuba_note) - { - self.tuba_note = spawn(); - self.tuba_note.owner = self.tuba_note.realowner = self; - self.tuba_note.cnt = n; - self.tuba_note.tuba_instrument = self.tuba_instrument; - self.tuba_note.think = W_Tuba_NoteThink; - self.tuba_note.nextthink = time; - self.tuba_note.spawnshieldtime = time; - Net_LinkEntity(self.tuba_note, FALSE, 0, W_Tuba_NoteSendEntity); - } - - self.tuba_note.teleport_time = time + autocvar_g_balance_tuba_refire * 2 * W_WeaponRateFactor(); // so it can get prolonged safely - - //sound(self, c, TUBA_NOTE(n), bound(0, VOL_BASE * cvar("g_balance_tuba_volume"), 1), autocvar_g_balance_tuba_attenuation); - RadiusDamage(self, self, autocvar_g_balance_tuba_damage, autocvar_g_balance_tuba_edgedamage, autocvar_g_balance_tuba_radius, world, autocvar_g_balance_tuba_force, hittype | WEP_TUBA, world); - - o = gettaginfo(self.exteriorweaponentity, 0); - if(time > self.tuba_smoketime) - { - pointparticles(particleeffectnum("smoke_ring"), o + v_up * 45 + v_right * -6 + v_forward * 8, v_up * 100, 1); - self.tuba_smoketime = time + 0.25; - } -} - -void spawnfunc_weapon_tuba (void) -{ - weapon_defaultspawnfunc(WEP_TUBA); -} - -float w_tuba(float req) -{ - if (req == WR_AIM) - { - // bots cannot play the Tuba well yet - // I think they should start with the recorder first - if(vlen(self.origin - self.enemy.origin) < autocvar_g_balance_tuba_radius) - { - if(random() > 0.5) - self.BUTTON_ATCK = 1; - else - self.BUTTON_ATCK2 = 1; - } - } - else if (req == WR_THINK) - { - if (self.BUTTON_ATCK) - if (weapon_prepareattack(0, autocvar_g_balance_tuba_refire)) - { - W_Tuba_NoteOn(0); - //weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_tuba_animtime, w_ready); - weapon_thinkf(WFRAME_IDLE, autocvar_g_balance_tuba_animtime, w_ready); - } - if (self.BUTTON_ATCK2) - if (weapon_prepareattack(1, autocvar_g_balance_tuba_refire)) - { - W_Tuba_NoteOn(HITTYPE_SECONDARY); - //weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_tuba_animtime, w_ready); - weapon_thinkf(WFRAME_IDLE, autocvar_g_balance_tuba_animtime, w_ready); - } - if(self.tuba_note) - { - if(!self.BUTTON_ATCK && !self.BUTTON_ATCK2) - { - entity oldself = self; - self = self.tuba_note; - W_Tuba_NoteOff(); - self = oldself; - } - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/weapons/g_tuba.md3"); - precache_model ("models/weapons/v_tuba.md3"); - precache_model ("models/weapons/h_tuba.iqm"); - precache_model ("models/weapons/v_akordeon.md3"); - precache_model ("models/weapons/h_akordeon.iqm"); - precache_model ("models/weapons/v_kleinbottle.md3"); - precache_model ("models/weapons/h_kleinbottle.iqm"); - - //float i; - //for(i = -18; i <= +27; ++i) - // precache_sound(TUBA_NOTE(i)); - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_TUBA); - self.current_ammo = ammo_none; - self.tuba_instrument = 0; - } - else if (req == WR_RELOAD) - { - // switch to alternate instruments :) - if(self.weaponentity.state == WS_READY) - { - switch(self.tuba_instrument) - { - case 0: - self.tuba_instrument = 1; - self.weaponname = "akordeon"; - break; - case 1: - self.tuba_instrument = 2; - self.weaponname = "kleinbottle"; - break; - case 2: - self.tuba_instrument = 0; - self.weaponname = "tuba"; - break; - } - W_SetupShot(self, FALSE, 0, "", 0, 0); - pointparticles(particleeffectnum("teleport"), w_shotorg, '0 0 0', 1); - self.weaponentity.state = WS_INUSE; - weapon_thinkf(WFRAME_RELOAD, 0.5, w_ready); - } - } - else if (req == WR_CHECKAMMO1) - return TRUE; // TODO use fuel? - else if (req == WR_CHECKAMMO2) - return TRUE; // TODO use fuel? - else if (req == WR_SUICIDEMESSAGE) - { - if(w_deathtype & HITTYPE_BOUNCE) - return WEAPON_KLEINBOTTLE_SUICIDE; - else if(w_deathtype & HITTYPE_SECONDARY) - return WEAPON_ACCORDEON_SUICIDE; - else - return WEAPON_TUBA_SUICIDE; - } - else if (req == WR_KILLMESSAGE) - { - if(w_deathtype & HITTYPE_BOUNCE) - return WEAPON_KLEINBOTTLE_MURDER; - else if(w_deathtype & HITTYPE_SECONDARY) - return WEAPON_ACCORDEON_MURDER; - else - return WEAPON_TUBA_MURDER; - } - return TRUE; -} -#endif -#ifdef CSQC -float w_tuba(float req) -{ - // nothing to do here; particles of tuba are handled differently - - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/w_uzi.qc b/qcsrc/server/w_uzi.qc deleted file mode 100644 index 4fb6047430..0000000000 --- a/qcsrc/server/w_uzi.qc +++ /dev/null @@ -1,337 +0,0 @@ -#ifdef REGISTER_WEAPON -REGISTER_WEAPON( -/* WEP_##id */ UZI, -/* function */ w_uzi, -/* ammotype */ IT_NAILS, -/* impulse */ 3, -/* flags */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN, -/* rating */ BOT_PICKUP_RATING_MID, -/* model */ "uzi", -/* shortname */ "uzi", -/* fullname */ _("Machine Gun") -); -#else -#ifdef SVQC - -// leilei's fancy muzzleflash stuff -void UZI_Flash_Go() -{ - self.frame = self.frame + 2; - self.scale = self.scale * 0.5; - self.alpha = self.alpha - 0.25; - self.nextthink = time + 0.05; - - if (self.alpha <= 0) - { - self.think = SUB_Remove; - self.nextthink = time; - self.realowner.muzzle_flash = world; - return; - } - -} - -void UziFlash() -{ - if (self.muzzle_flash == world) - self.muzzle_flash = spawn(); - - // muzzle flash for 1st person view - setmodel(self.muzzle_flash, "models/uziflash.md3"); // precision set below - - self.muzzle_flash.scale = 0.75; - self.muzzle_flash.think = UZI_Flash_Go; - self.muzzle_flash.nextthink = time + 0.02; - self.muzzle_flash.frame = 2; - self.muzzle_flash.alpha = 0.75; - self.muzzle_flash.angles_z = random() * 180; - self.muzzle_flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; - self.muzzle_flash.owner = self.muzzle_flash.realowner = self; -} - -void W_UZI_Attack (float deathtype) -{ - W_SetupShot (self, TRUE, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, ((self.misc_bulletcounter == 1) ? autocvar_g_balance_uzi_first_damage : autocvar_g_balance_uzi_sustained_damage)); - if (!autocvar_g_norecoil) - { - self.punchangle_x = random () - 0.5; - self.punchangle_y = random () - 0.5; - } - - // this attack_finished just enforces a cooldown at the end of a burst - ATTACK_FINISHED(self) = time + autocvar_g_balance_uzi_first_refire * W_WeaponRateFactor(); - - if (self.misc_bulletcounter == 1) - fireBullet(w_shotorg, w_shotdir, autocvar_g_balance_uzi_first_spread, autocvar_g_balance_uzi_solidpenetration, autocvar_g_balance_uzi_first_damage, autocvar_g_balance_uzi_first_force, deathtype, 0); - else - fireBullet(w_shotorg, w_shotdir, autocvar_g_balance_uzi_sustained_spread, autocvar_g_balance_uzi_solidpenetration, autocvar_g_balance_uzi_sustained_damage, autocvar_g_balance_uzi_sustained_force, deathtype, 0); - - pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - UziFlash(); - W_AttachToShotorg(self.muzzle_flash, '5 0 0'); - - // casing code - if (autocvar_g_casings >= 2) - 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, self); - - if (self.misc_bulletcounter == 1) - W_DecreaseAmmo(ammo_nails, autocvar_g_balance_uzi_first_ammo, autocvar_g_balance_uzi_reload_ammo); - else - W_DecreaseAmmo(ammo_nails, autocvar_g_balance_uzi_sustained_ammo, autocvar_g_balance_uzi_reload_ammo); -} - -// weapon frames -void uzi_fire1_02() -{ - if(self.weapon != self.switchweapon) // abort immediately if switching - { - w_ready(); - return; - } - if (self.BUTTON_ATCK) - { - if (!weapon_action(self.weapon, WR_CHECKAMMO2)) - if (!(self.items & IT_UNLIMITED_WEAPON_AMMO)) - { - W_SwitchWeapon_Force(self, w_getbestweapon(self)); - w_ready(); - return; - } - self.misc_bulletcounter = self.misc_bulletcounter + 1; - W_UZI_Attack(WEP_UZI); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_uzi_sustained_refire, uzi_fire1_02); - } - else - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_uzi_sustained_refire, w_ready); -} - - -void uzi_mode1_fire_auto() -{ - float uzi_spread; - - if (!self.BUTTON_ATCK) - { - w_ready(); - return; - } - - if (!weapon_action(self.weapon, WR_CHECKAMMO1)) - if (!(self.items & IT_UNLIMITED_WEAPON_AMMO)) - { - W_SwitchWeapon_Force(self, w_getbestweapon(self)); - w_ready(); - return; - } - - W_DecreaseAmmo(ammo_nails, autocvar_g_balance_uzi_sustained_ammo, autocvar_g_balance_uzi_reload_ammo); - - W_SetupShot (self, TRUE, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, autocvar_g_balance_uzi_sustained_damage); - if (!autocvar_g_norecoil) - { - self.punchangle_x = random () - 0.5; - self.punchangle_y = random () - 0.5; - } - - uzi_spread = bound(autocvar_g_balance_uzi_spread_min, autocvar_g_balance_uzi_spread_min + (autocvar_g_balance_uzi_spread_add * self.misc_bulletcounter), autocvar_g_balance_uzi_spread_max); - fireBullet(w_shotorg, w_shotdir, uzi_spread, autocvar_g_balance_uzi_solidpenetration, autocvar_g_balance_uzi_sustained_damage, autocvar_g_balance_uzi_sustained_force, WEP_UZI, 0); - - self.misc_bulletcounter = self.misc_bulletcounter + 1; - - pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - UziFlash(); - W_AttachToShotorg(self.muzzle_flash, '5 0 0'); - - if (autocvar_g_casings >= 2) // casing code - 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, self); - - ATTACK_FINISHED(self) = time + autocvar_g_balance_uzi_first_refire * W_WeaponRateFactor(); - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_uzi_sustained_refire, uzi_mode1_fire_auto); -} - -void uzi_mode1_fire_burst() -{ - W_SetupShot (self, TRUE, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, autocvar_g_balance_uzi_sustained_damage); - if (!autocvar_g_norecoil) - { - self.punchangle_x = random () - 0.5; - self.punchangle_y = random () - 0.5; - } - - fireBullet(w_shotorg, w_shotdir, autocvar_g_balance_uzi_burst_spread, autocvar_g_balance_uzi_solidpenetration, autocvar_g_balance_uzi_sustained_damage, autocvar_g_balance_uzi_sustained_force, WEP_UZI, 0); - - pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1); - - UziFlash(); - W_AttachToShotorg(self.muzzle_flash, '5 0 0'); - - if (autocvar_g_casings >= 2) // casing code - 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, self); - - self.misc_bulletcounter = self.misc_bulletcounter + 1; - if (self.misc_bulletcounter == 0) - { - ATTACK_FINISHED(self) = time + autocvar_g_balance_uzi_burst_refire2 * W_WeaponRateFactor(); - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_uzi_burst_animtime, w_ready); - } - else - { - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_uzi_burst_refire, uzi_mode1_fire_burst); - } - -} - -void spawnfunc_weapon_machinegun(); // defined in t_items.qc - -float w_uzi(float req) -{ - float ammo_amount; - if (req == WR_AIM) - if(vlen(self.origin-self.enemy.origin) < 3000 - bound(0, skill, 10) * 200) - self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE); - else - { - self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE); - } - else if (req == WR_THINK) - { - if(autocvar_g_balance_uzi_reload_ammo && self.clip_load < min(max(autocvar_g_balance_uzi_sustained_ammo, autocvar_g_balance_uzi_first_ammo), autocvar_g_balance_uzi_burst_ammo)) // forced reload - weapon_action(self.weapon, WR_RELOAD); - else if(autocvar_g_balance_uzi_mode == 1) - { - if (self.BUTTON_ATCK) - if (weapon_prepareattack(0, 0)) - { - self.misc_bulletcounter = 0; - uzi_mode1_fire_auto(); - } - - if(self.BUTTON_ATCK2) - if(weapon_prepareattack(1, 0)) - { - if (!weapon_action(self.weapon, WR_CHECKAMMO2)) - if (!(self.items & IT_UNLIMITED_WEAPON_AMMO)) - { - W_SwitchWeapon_Force(self, w_getbestweapon(self)); - w_ready(); - return FALSE; - } - - W_DecreaseAmmo(ammo_nails, autocvar_g_balance_uzi_burst_ammo, autocvar_g_balance_uzi_reload_ammo); - - self.misc_bulletcounter = autocvar_g_balance_uzi_burst * -1; - uzi_mode1_fire_burst(); - } - } - else - { - - if (self.BUTTON_ATCK) - if (weapon_prepareattack(0, 0)) - { - self.misc_bulletcounter = 1; - W_UZI_Attack(WEP_UZI); // sets attack_finished - weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_uzi_sustained_refire, uzi_fire1_02); - } - - if (self.BUTTON_ATCK2 && autocvar_g_balance_uzi_first) - if (weapon_prepareattack(1, 0)) - { - self.misc_bulletcounter = 1; - W_UZI_Attack(WEP_UZI | HITTYPE_SECONDARY); // sets attack_finished - weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_uzi_first_refire, w_ready); - } - } - } - else if (req == WR_PRECACHE) - { - precache_model ("models/uziflash.md3"); - precache_model ("models/weapons/g_uzi.md3"); - precache_model ("models/weapons/v_uzi.md3"); - precache_model ("models/weapons/h_uzi.iqm"); - precache_sound ("weapons/uzi_fire.wav"); - //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else - } - else if (req == WR_SETUP) - { - weapon_setup(WEP_UZI); - self.current_ammo = ammo_nails; - } - else if (req == WR_CHECKAMMO1) - { - if(autocvar_g_balance_uzi_mode == 1) - ammo_amount = self.ammo_nails >= autocvar_g_balance_uzi_sustained_ammo; - else - ammo_amount = self.ammo_nails >= autocvar_g_balance_uzi_first_ammo; - - if(autocvar_g_balance_uzi_reload_ammo) - { - if(autocvar_g_balance_uzi_mode == 1) - ammo_amount += self.(weapon_load[WEP_UZI]) >= autocvar_g_balance_uzi_sustained_ammo; - else - ammo_amount += self.(weapon_load[WEP_UZI]) >= autocvar_g_balance_uzi_first_ammo; - } - return ammo_amount; - } - else if (req == WR_CHECKAMMO2) - { - if(autocvar_g_balance_uzi_mode == 1) - ammo_amount = self.ammo_nails >= autocvar_g_balance_uzi_burst_ammo; - else - ammo_amount = self.ammo_nails >= autocvar_g_balance_uzi_first_ammo; - - if(autocvar_g_balance_uzi_reload_ammo) - { - if(autocvar_g_balance_uzi_mode == 1) - ammo_amount += self.(weapon_load[WEP_UZI]) >= autocvar_g_balance_uzi_burst_ammo; - else - ammo_amount += self.(weapon_load[WEP_UZI]) >= autocvar_g_balance_uzi_first_ammo; - } - return ammo_amount; - } - else if (req == WR_RELOAD) - { - W_Reload(min(max(autocvar_g_balance_uzi_sustained_ammo, autocvar_g_balance_uzi_first_ammo), autocvar_g_balance_uzi_burst_ammo), autocvar_g_balance_uzi_reload_ammo, autocvar_g_balance_uzi_reload_time, "weapons/reload.wav"); - } - else if (req == WR_SUICIDEMESSAGE) - { - return WEAPON_THINKING_WITH_PORTALS; - } - else if (req == WR_KILLMESSAGE) - { - if(w_deathtype & HITTYPE_SECONDARY) - return WEAPON_UZI_MURDER_SNIPE; - else - return WEAPON_UZI_MURDER_SPRAY; - } - return TRUE; -} -#endif -#ifdef CSQC -float w_uzi(float req) -{ - if(req == WR_IMPACTEFFECT) - { - vector org2; - org2 = w_org + w_backoff * 2; - pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1); - if(!w_issilent) - if(w_random < 0.05) - sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTEN_NORM); - else if(w_random < 0.1) - sound(self, CH_SHOTS, "weapons/ric2.wav", VOL_BASE, ATTEN_NORM); - else if(w_random < 0.2) - sound(self, CH_SHOTS, "weapons/ric3.wav", VOL_BASE, ATTEN_NORM); - } - else if(req == WR_PRECACHE) - { - precache_sound("weapons/ric1.wav"); - precache_sound("weapons/ric2.wav"); - precache_sound("weapons/ric3.wav"); - } - return TRUE; -} -#endif -#endif diff --git a/qcsrc/server/weapons/accuracy.qc b/qcsrc/server/weapons/accuracy.qc new file mode 100644 index 0000000000..09d42332ae --- /dev/null +++ b/qcsrc/server/weapons/accuracy.qc @@ -0,0 +1,124 @@ +float accuracy_byte(float n, float d) +{ + //printf("accuracy: %d / %d\n", n, d); + if(n <= 0) + return 0; + if(n > d) + return 255; + return 1 + rint(n * 100.0 / d); +} + +float accuracy_send(entity to, float sf) +{ + float w, f; + entity a; + WriteByte(MSG_ENTITY, ENT_CLIENT_ACCURACY); + + a = self.owner; + if(IS_SPEC(a)) + a = a.enemy; + a = a.accuracy; + + if(to != a.owner) + if (!(self.owner.cvar_cl_accuracy_data_share && autocvar_sv_accuracy_data_share)) + sf = 0; + // note: zero sendflags can never be sent... so we can use that to say that we send no accuracy! + WriteInt24_t(MSG_ENTITY, sf); + if(sf == 0) + return TRUE; + // note: we know that client and server agree about SendFlags... + for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w) + { + if(sf & f) + WriteByte(MSG_ENTITY, accuracy_byte(self.(accuracy_hit[w]), self.(accuracy_fired[w]))); + if(f == 0x800000) + f = 1; + else + f *= 2; + } + return TRUE; +} + +// init/free +void accuracy_init(entity e) +{ + e.accuracy = spawn(); + e.accuracy.owner = e; + e.accuracy.classname = "accuracy"; + e.accuracy.drawonlytoclient = e; + Net_LinkEntity(e.accuracy, FALSE, 0, accuracy_send); +} + +void accuracy_free(entity e) +{ + remove(e.accuracy); +} + +// force a resend of a player's accuracy stats +void accuracy_resend(entity e) +{ + e.accuracy.SendFlags = 0xFFFFFF; +} + +// update accuracy stats +.float hit_time; +.float fired_time; + +void accuracy_add(entity e, float w, float fired, float hit) +{ + entity a; + float b; + if(IS_INDEPENDENT_PLAYER(e)) + return; + a = e.accuracy; + if(!a || !(hit || fired)) + return; + w -= WEP_FIRST; + b = accuracy_byte(a.(accuracy_hit[w]), a.(accuracy_fired[w])); + if(hit) + a.(accuracy_hit[w]) += hit; + if(fired) + a.(accuracy_fired[w]) += fired; + + if(hit && a.hit_time != time) // only run this once per frame + { + a.(accuracy_cnt_hit[w]) += 1; + a.hit_time = time; + } + + if(fired && a.fired_time != time) // only run this once per frame + { + a.(accuracy_cnt_fired[w]) += 1; + a.fired_time = time; + } + + if(b == accuracy_byte(a.(accuracy_hit[w]), a.(accuracy_fired[w]))) + return; + w = pow(2, mod(w, 24)); + a.SendFlags |= w; + FOR_EACH_CLIENT(a) + if(IS_SPEC(a)) + if(a.enemy == e) + a.SendFlags |= w; +} + +float accuracy_isgooddamage(entity attacker, entity targ) +{ + frag_attacker = attacker; + frag_target = targ; + float mutator_check = MUTATOR_CALLHOOK(AccuracyTargetValid); + + if(!warmup_stage) + if(targ.deadflag == DEAD_NO) + if(mutator_check == MUT_ACCADD_INVALID || (mutator_check == MUT_ACCADD_VALID && IS_CLIENT(targ))) + if(DIFF_TEAM(attacker, targ)) + return TRUE; + return FALSE; +} + +float accuracy_canbegooddamage(entity attacker) +{ + if(!warmup_stage) + return TRUE; + return FALSE; +} diff --git a/qcsrc/server/weapons/accuracy.qh b/qcsrc/server/weapons/accuracy.qh new file mode 100644 index 0000000000..4cd43ee8ad --- /dev/null +++ b/qcsrc/server/weapons/accuracy.qh @@ -0,0 +1,25 @@ +.float cvar_cl_accuracy_data_share; +.float cvar_cl_accuracy_data_receive; + +.entity accuracy; +.float accuracy_frags[WEP_MAXCOUNT]; + +.float accuracy_hit[WEP_MAXCOUNT]; +.float accuracy_fired[WEP_MAXCOUNT]; +.float accuracy_cnt_hit[WEP_MAXCOUNT]; +.float accuracy_cnt_fired[WEP_MAXCOUNT]; + + +// init/free +void accuracy_init(entity e); +void accuracy_free(entity e); + +// force a resend of a player's accuracy stats +void accuracy_resend(entity e); + +// update accuracy stats +void accuracy_add(entity e, float w, float fired, float hit); + +// helper +float accuracy_isgooddamage(entity attacker, entity targ); +float accuracy_canbegooddamage(entity attacker); diff --git a/qcsrc/server/weapons/common.qc b/qcsrc/server/weapons/common.qc new file mode 100644 index 0000000000..6e54cb0601 --- /dev/null +++ b/qcsrc/server/weapons/common.qc @@ -0,0 +1,89 @@ + +void W_GiveWeapon (entity e, float wep) +{ + entity oldself; + + if (!wep) + return; + + e.weapons |= WepSet_FromWeapon(wep); + + oldself = self; + self = e; + + if(IS_PLAYER(other)) + { Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_WEAPON_GOT, wep); } + + self = oldself; +} + +void W_PlayStrengthSound(entity player) // void W_PlayStrengthSound +{ + if((player.items & IT_STRENGTH) + && ((time > player.prevstrengthsound + autocvar_sv_strengthsound_antispam_time) // prevent insane sound spam + || (time > player.prevstrengthsoundattempt + autocvar_sv_strengthsound_antispam_refire_threshold))) + { + sound(player, CH_TRIGGER, "weapons/strength_fire.wav", VOL_BASE, ATTEN_NORM); + player.prevstrengthsound = time; + } + player.prevstrengthsoundattempt = time; +} + +float W_CheckProjectileDamage(entity inflictor, entity projowner, float deathtype, float exception) +{ + float is_from_contents = (deathtype == DEATH_SLIME || deathtype == DEATH_LAVA); + float is_from_owner = (inflictor == projowner); + float is_from_exception = (exception != -1); + + //dprint(strcat("W_CheckProjectileDamage: from_contents ", ftos(is_from_contents), " : from_owner ", ftos(is_from_owner), " : exception ", strcat(ftos(is_from_exception), " (", ftos(exception), "). \n"))); + + if(autocvar_g_projectiles_damage <= -2) + { + return FALSE; // no damage to projectiles at all, not even with the exceptions + } + else if(autocvar_g_projectiles_damage == -1) + { + if(is_from_exception) + return (exception); // if exception is detected, allow it to override + else + return FALSE; // otherwise, no other damage is allowed + } + else if(autocvar_g_projectiles_damage == 0) + { + if(is_from_exception) + return (exception); // if exception is detected, allow it to override + else if(!is_from_contents) + return FALSE; // otherwise, only allow damage from contents + } + else if(autocvar_g_projectiles_damage == 1) + { + if(is_from_exception) + return (exception); // if exception is detected, allow it to override + else if(!(is_from_contents || is_from_owner)) + return FALSE; // otherwise, only allow self damage and damage from contents + } + else if(autocvar_g_projectiles_damage == 2) // allow any damage, but override for exceptions + { + if(is_from_exception) + return (exception); // if exception is detected, allow it to override + } + + return TRUE; // if none of these return, then allow damage anyway. +} + +void W_PrepareExplosionByDamage(entity attacker, void() explode) +{ + self.takedamage = DAMAGE_NO; + self.event_damage = func_null; + + if(IS_CLIENT(attacker) && !autocvar_g_projectiles_keep_owner) + { + self.owner = attacker; + self.realowner = attacker; + } + + // do not explode NOW but in the NEXT FRAME! + // because recursive calls to RadiusDamage are not allowed + self.nextthink = time; + self.think = explode; +} diff --git a/qcsrc/server/weapons/common.qh b/qcsrc/server/weapons/common.qh new file mode 100644 index 0000000000..8f9454ed54 --- /dev/null +++ b/qcsrc/server/weapons/common.qh @@ -0,0 +1,7 @@ + +void W_GiveWeapon (entity e, float wep); +.float prevstrengthsound; +.float prevstrengthsoundattempt; +void W_PlayStrengthSound(entity player); +float W_CheckProjectileDamage(entity inflictor, entity projowner, float deathtype, float exception); +void W_PrepareExplosionByDamage(entity attacker, void() explode); diff --git a/qcsrc/server/weapons/csqcprojectile.qc b/qcsrc/server/weapons/csqcprojectile.qc new file mode 100644 index 0000000000..3dd93c0581 --- /dev/null +++ b/qcsrc/server/weapons/csqcprojectile.qc @@ -0,0 +1,114 @@ +.float csqcprojectile_type; + +float CSQCProjectile_SendEntity(entity to, float sf) +{ + float ft, fr; + + // note: flag 0x08 = no trail please (teleport bit) + sf = sf & 0x0F; + + if(self.csqcprojectile_clientanimate) + sf |= 0x80; // client animated, not interpolated + + if(self.flags & FL_ONGROUND) + sf |= 0x40; + + ft = fr = 0; + if(self.fade_time != 0 || self.fade_rate != 0) + { + ft = (self.fade_time - time) / sys_frametime; + fr = (1 / self.fade_rate) / sys_frametime; + if(ft <= 255 && fr <= 255 && fr >= 1) + sf |= 0x20; + } + + if(self.gravity != 0) + sf |= 0x10; + + WriteByte(MSG_ENTITY, ENT_CLIENT_PROJECTILE); + WriteByte(MSG_ENTITY, sf); + + if(sf & 1) + { + WriteCoord(MSG_ENTITY, self.origin_x); + WriteCoord(MSG_ENTITY, self.origin_y); + WriteCoord(MSG_ENTITY, self.origin_z); + + if(sf & 0x80) + { + WriteCoord(MSG_ENTITY, self.velocity_x); + WriteCoord(MSG_ENTITY, self.velocity_y); + WriteCoord(MSG_ENTITY, self.velocity_z); + if(sf & 0x10) + WriteCoord(MSG_ENTITY, self.gravity); + } + + if(sf & 0x20) + { + WriteByte(MSG_ENTITY, ft); + WriteByte(MSG_ENTITY, fr); + } + + WriteByte(MSG_ENTITY, self.realowner.team); + } + + if(sf & 2) + WriteByte(MSG_ENTITY, self.csqcprojectile_type); // TODO maybe put this into sf? + + return 1; +} + +.vector csqcprojectile_oldorigin; +void CSQCProjectile_Check(entity e) +{ + if(e.csqcprojectile_clientanimate) + if(e.flags & FL_ONGROUND) + if(e.origin != e.csqcprojectile_oldorigin) + UpdateCSQCProjectile(e); + e.csqcprojectile_oldorigin = e.origin; +} + +void CSQCProjectile(entity e, float clientanimate, float type, float docull) +{ + Net_LinkEntity(e, docull, 0, CSQCProjectile_SendEntity); + + e.csqcprojectile_clientanimate = clientanimate; + + if(e.movetype == MOVETYPE_TOSS || e.movetype == MOVETYPE_BOUNCE) + { + if(e.gravity == 0) + e.gravity = 1; + } + else + e.gravity = 0; + + if(!sound_allowed(MSG_BROADCAST, e)) + type |= 0x80; + e.csqcprojectile_type = type; +} + +void UpdateCSQCProjectile(entity e) +{ + if(e.SendEntity == CSQCProjectile_SendEntity) + { + // send new origin data + e.SendFlags |= 0x01; + } +// FIXME HACK + else if(e.SendEntity == ItemSend) + { + ItemUpdate(e); + } +// END HACK +} + +void UpdateCSQCProjectileAfterTeleport(entity e) +{ + if(e.SendEntity == CSQCProjectile_SendEntity) + { + // send new origin data + e.SendFlags |= 0x01; + // mark as teleported + e.SendFlags |= 0x08; + } +} diff --git a/qcsrc/server/weapons/csqcprojectile.qh b/qcsrc/server/weapons/csqcprojectile.qh new file mode 100644 index 0000000000..e00c0988b9 --- /dev/null +++ b/qcsrc/server/weapons/csqcprojectile.qh @@ -0,0 +1,6 @@ +.float csqcprojectile_clientanimate; + +void CSQCProjectile(entity e, float clientanimate, float type, float docull); +void UpdateCSQCProjectile(entity e); +void UpdateCSQCProjectileAfterTeleport(entity e); +void CSQCProjectile_Check(entity e); diff --git a/qcsrc/server/weapons/hitplot.qc b/qcsrc/server/weapons/hitplot.qc new file mode 100644 index 0000000000..685741dabc --- /dev/null +++ b/qcsrc/server/weapons/hitplot.qc @@ -0,0 +1,91 @@ +vector W_HitPlotUnnormalizedUntransform(vector screenforward, vector screenright, vector screenup, vector v) +{ + vector ret; + ret_x = screenright * v; + ret_y = screenup * v; + ret_z = screenforward * v; + return ret; +} + +vector W_HitPlotNormalizedUntransform(vector org, entity targ, vector screenforward, vector screenright, vector screenup, vector v) +{ + float i, j, k; + vector mi, ma, thisv, myv, ret; + + myv = W_HitPlotUnnormalizedUntransform(screenforward, screenright, screenup, org); + + // x = 0..1 relative to hitbox; y = 0..1 relative to hitbox; z = distance + + mi = ma = targ.origin + 0.5 * (targ.mins + targ.maxs); + for(i = 0; i < 2; ++i) for(j = 0; j < 2; ++j) for(k = 0; k < 2; ++k) + { + thisv = targ.origin; + if(i) thisv_x += targ.maxs_x; else thisv_x += targ.mins_x; + if(j) thisv_y += targ.maxs_y; else thisv_y += targ.mins_y; + if(k) thisv_z += targ.maxs_z; else thisv_z += targ.mins_z; + thisv = W_HitPlotUnnormalizedUntransform(screenforward, screenright, screenup, thisv); + if(i || j || k) + { + if(mi_x > thisv_x) mi_x = thisv_x; if(ma_x < thisv_x) ma_x = thisv_x; + if(mi_y > thisv_y) mi_y = thisv_y; if(ma_y < thisv_y) ma_y = thisv_y; + //if(mi_z > thisv_z) mi_z = thisv_z; if(ma_z < thisv_z) ma_y = thisv_z; + } + else + { + // first run + mi = ma = thisv; + } + } + + thisv = W_HitPlotUnnormalizedUntransform(screenforward, screenright, screenup, v); + ret_x = (thisv_x - mi_x) / (ma_x - mi_x); + ret_y = (thisv_y - mi_y) / (ma_y - mi_y); + ret_z = thisv_z - myv_z; + return ret; +} + +void W_HitPlotAnalysis(entity player, vector screenforward, vector screenright, vector screenup) +{ + vector hitplot; + vector org; + float lag; + + if(player.hitplotfh >= 0) + { + lag = ANTILAG_LATENCY(player); + if(lag < 0.001) + lag = 0; + if(!IS_REAL_CLIENT(player)) + lag = 0; // only antilag for clients + + org = player.origin + player.view_ofs; + traceline_antilag_force(player, org, org + screenforward * MAX_SHOT_DISTANCE, MOVE_NORMAL, player, lag); + if(IS_CLIENT(trace_ent) || (trace_ent.flags & FL_MONSTER)) + { + antilag_takeback(trace_ent, time - lag); + hitplot = W_HitPlotNormalizedUntransform(org, trace_ent, screenforward, screenright, screenup, trace_endpos); + antilag_restore(trace_ent); + fputs(player.hitplotfh, strcat(ftos(hitplot_x), " ", ftos(hitplot_y), " ", ftos(hitplot_z), " ", ftos(player.switchweapon), "\n")); + //print(strcat(ftos(hitplot_x), " ", ftos(hitplot_y), " ", ftos(hitplot_z), "\n")); + } + } +} + +void W_HitPlotOpen(entity player) +{ + if(autocvar_g_hitplots || strstrofs(strcat(" ", autocvar_g_hitplots_individuals, " "), strcat(" ", player.netaddress, " "), 0) >= 0) + { + player.hitplotfh = fopen(strcat("hits-", matchid, "-", player.netaddress, "-", ftos(player.playerid), ".plot"), FILE_WRITE); + fputs(player.hitplotfh, strcat("#name ", player.netname, "\n")); + } + else { player.hitplotfh = -1; } +} + +void W_HitPlotClose(entity player) +{ + if(player.hitplotfh >= 0) + { + fclose(player.hitplotfh); + player.hitplotfh = -1; + } +} diff --git a/qcsrc/server/weapons/hitplot.qh b/qcsrc/server/weapons/hitplot.qh new file mode 100644 index 0000000000..9bc8ff3039 --- /dev/null +++ b/qcsrc/server/weapons/hitplot.qh @@ -0,0 +1,5 @@ +.float hitplotfh; + +void W_HitPlotAnalysis(entity player, vector screenforward, vector screenright, vector screenup); +void W_HitPlotOpen(entity player); +void W_HitPlotClose(entity player); diff --git a/qcsrc/server/weapons/selection.qc b/qcsrc/server/weapons/selection.qc new file mode 100644 index 0000000000..12ffbb9b49 --- /dev/null +++ b/qcsrc/server/weapons/selection.qc @@ -0,0 +1,304 @@ +// switch between weapons +void Send_WeaponComplain(entity e, float wpn, float type) +{ + msg_entity = e; + WriteByte(MSG_ONE, SVC_TEMPENTITY); + WriteByte(MSG_ONE, TE_CSQC_WEAPONCOMPLAIN); + WriteByte(MSG_ONE, wpn); + WriteByte(MSG_ONE, type); +} + +float client_hasweapon(entity cl, float wpn, float andammo, float complain) +{ + float f; + entity oldself; + + if(time < self.hasweapon_complain_spam) + complain = 0; + + if(wpn == WEP_HOOK && !g_grappling_hook && autocvar_g_nades && !((cl.weapons | weaponsInMap) & WepSet_FromWeapon(wpn))) + complain = 0; + + if(complain) + self.hasweapon_complain_spam = time + 0.2; + + if (wpn < WEP_FIRST || wpn > WEP_LAST) + { + if (complain) + sprint(self, "Invalid weapon\n"); + return FALSE; + } + if (cl.weapons & WepSet_FromWeapon(wpn)) + { + if (andammo) + { + if(cl.items & IT_UNLIMITED_WEAPON_AMMO) + { + f = 1; + } + else + { + oldself = self; + self = cl; + f = WEP_ACTION(wpn, WR_CHECKAMMO1); + f = f + WEP_ACTION(wpn, WR_CHECKAMMO2); + + // always allow selecting the Mine Layer if we placed mines, so that we can detonate them + entity mine; + if(wpn == WEP_MINE_LAYER) + for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self) + f = 1; + + self = oldself; + } + if (!f) + { + if (complain) + if(IS_REAL_CLIENT(cl)) + { + play2(cl, "weapons/unavailable.wav"); + Send_WeaponComplain (cl, wpn, 0); + } + return FALSE; + } + } + return TRUE; + } + if (complain) + { + // DRESK - 3/16/07 + // Report Proper Weapon Status / Modified Weapon Ownership Message + if (weaponsInMap & WepSet_FromWeapon(wpn)) + { + Send_WeaponComplain(cl, wpn, 1); + + if(autocvar_g_showweaponspawns) + { + entity e; + + for(e = world; (e = findfloat(e, weapon, wpn)); ) + { + if(e.classname == "droppedweapon" && autocvar_g_showweaponspawns < 2) + continue; + if(!(e.flags & FL_ITEM)) + continue; + WaypointSprite_Spawn( + (get_weaponinfo(wpn)).wpmodel, + 1, 0, + world, e.origin + ('0 0 1' * e.maxs_z) * 1.2, + self, 0, + world, enemy, + 0, + RADARICON_NONE, '0 0 0' + ); + } + } + } + else + { + Send_WeaponComplain (cl, wpn, 2); + } + + play2(cl, "weapons/unavailable.wav"); + } + return FALSE; +} + +float W_GetCycleWeapon(entity pl, string weaponorder, float dir, float imp, float complain, float skipmissing) +{ + // We cannot tokenize in this function, as GiveItems calls this + // function. Thus we must use car/cdr. + float weaponwant, first_valid, prev_valid, switchtonext, switchtolast, c; + string rest; + WepSet wepset = '0 0 0'; + switchtonext = switchtolast = 0; + first_valid = prev_valid = 0; + float weaponcur; + entity wep; + + if(skipmissing || pl.selectweapon == 0) + weaponcur = pl.switchweapon; + else + weaponcur = pl.selectweapon; + + if(dir == 0) + switchtonext = 1; + + c = 0; + + rest = weaponorder; + while(rest != "") + { + weaponwant = stof(car(rest)); rest = cdr(rest); + wep = get_weaponinfo(weaponwant); + wepset = WepSet_FromWeapon(weaponwant); + if(imp >= 0) + if(wep.impulse != imp) + continue; + + float i, have_other = FALSE; + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + { + if(i != weaponwant) + if((get_weaponinfo(i)).impulse == imp || imp < 0) + if((pl.weapons & WepSet_FromWeapon(i)) || (weaponsInMap & WepSet_FromWeapon(i))) + have_other = TRUE; + } + + // skip weapons we don't own that aren't normal and aren't in the map + if(!(pl.weapons & wepset)) + if(!(weaponsInMap & wepset)) + if((wep.spawnflags & WEP_FLAG_MUTATORBLOCKED) || have_other) + continue; + + ++c; + + if(!skipmissing || client_hasweapon(pl, weaponwant, TRUE, FALSE)) + { + if(switchtonext) + return weaponwant; + if(!first_valid) + first_valid = weaponwant; + if(weaponwant == weaponcur) + { + if(dir >= 0) + switchtonext = 1; + else if(prev_valid) + return prev_valid; + else + switchtolast = 1; + } + prev_valid = weaponwant; + } + } + if(first_valid) + { + if(switchtolast) + return prev_valid; + else + return first_valid; + } + // complain (but only for one weapon on the button that has been pressed) + if(complain) + { + self.weaponcomplainindex += 1; + c = mod(self.weaponcomplainindex, c) + 1; + rest = weaponorder; + while(rest != "") + { + weaponwant = stof(car(rest)); rest = cdr(rest); + wep = get_weaponinfo(weaponwant); + wepset = WepSet_FromWeapon(weaponwant); + if(imp >= 0) + if(wep.impulse != imp) + continue; + + float i, have_other = FALSE; + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + { + if(i != weaponwant) + if((get_weaponinfo(i)).impulse == imp || imp < 0) + if((pl.weapons & WepSet_FromWeapon(i)) || (weaponsInMap & WepSet_FromWeapon(i))) + have_other = TRUE; + } + + // skip weapons we don't own that aren't normal and aren't in the map + if(!(pl.weapons & wepset)) + if(!(weaponsInMap & wepset)) + if((wep.spawnflags & WEP_FLAG_MUTATORBLOCKED) || have_other) + continue; + + --c; + if(c == 0) + { + client_hasweapon(pl, weaponwant, TRUE, TRUE); + break; + } + } + } + return 0; +} + +void W_SwitchWeapon_Force(entity e, float w) +{ + e.cnt = e.switchweapon; + e.switchweapon = w; + e.selectweapon = w; +} + +// perform weapon to attack (weaponstate and attack_finished check is here) +void W_SwitchToOtherWeapon(entity pl) +{ + // hack to ensure it switches to an OTHER weapon (in case the other fire mode still has ammo, we want that anyway) + float w, ww; + w = pl.weapon; + if(pl.weapons & WepSet_FromWeapon(w)) + { + pl.weapons &= ~WepSet_FromWeapon(w); + ww = w_getbestweapon(pl); + pl.weapons |= WepSet_FromWeapon(w); + } + else + ww = w_getbestweapon(pl); + if(ww) + W_SwitchWeapon_Force(pl, ww); +} + +void W_SwitchWeapon(float imp) +{ + if (self.switchweapon != imp) + { + if (client_hasweapon(self, imp, TRUE, TRUE)) + W_SwitchWeapon_Force(self, imp); + else + self.selectweapon = imp; // update selectweapon ANYWAY + } + else if(!forbidWeaponUse()) { WEP_ACTION(self.weapon, WR_RELOAD); } +} + +void W_CycleWeapon(string weaponorder, float dir) +{ + float w; + w = W_GetCycleWeapon(self, weaponorder, dir, -1, 1, TRUE); + if(w > 0) + W_SwitchWeapon(w); +} + +void W_NextWeaponOnImpulse(float imp) +{ + float w; + w = W_GetCycleWeapon(self, self.cvar_cl_weaponpriority, +1, imp, 1, (self.cvar_cl_weaponimpulsemode == 0)); + if(w > 0) + W_SwitchWeapon(w); +} + +// next weapon +void W_NextWeapon(float list) +{ + if(list == 0) + W_CycleWeapon(weaponorder_byid, -1); + else if(list == 1) + W_CycleWeapon(self.weaponorder_byimpulse, -1); + else if(list == 2) + W_CycleWeapon(self.cvar_cl_weaponpriority, -1); +} + +// prev weapon +void W_PreviousWeapon(float list) +{ + if(list == 0) + W_CycleWeapon(weaponorder_byid, +1); + else if(list == 1) + W_CycleWeapon(self.weaponorder_byimpulse, +1); + else if(list == 2) + W_CycleWeapon(self.cvar_cl_weaponpriority, +1); +} + +// previously used if exists and has ammo, (second) best otherwise +void W_LastWeapon(void) +{ + if(client_hasweapon(self, self.cnt, TRUE, FALSE)) + W_SwitchWeapon(self.cnt); + else + W_SwitchToOtherWeapon(self); +} diff --git a/qcsrc/server/weapons/selection.qh b/qcsrc/server/weapons/selection.qh new file mode 100644 index 0000000000..8e2937f0ca --- /dev/null +++ b/qcsrc/server/weapons/selection.qh @@ -0,0 +1,29 @@ +// switch between weapons +void Send_WeaponComplain(entity e, float wpn, float type); + +.float hasweapon_complain_spam; +float client_hasweapon(entity cl, float wpn, float andammo, float complain); + +.float weaponcomplainindex; +float W_GetCycleWeapon(entity pl, string weaponorder, float dir, float imp, float complain, float skipmissing); + +#define w_getbestweapon(ent) W_GetCycleWeapon(ent, ent.cvar_cl_weaponpriority, 0, -1, FALSE, TRUE) + +void W_SwitchWeapon_Force(entity e, float w); + +// perform weapon to attack (weaponstate and attack_finished check is here) +void W_SwitchToOtherWeapon(entity pl); +void W_SwitchWeapon(float imp); + +void W_CycleWeapon(string weaponorder, float dir); + +void W_NextWeaponOnImpulse(float imp); + +// next weapon +void W_NextWeapon(float list); + +// prev weapon +void W_PreviousWeapon(float list); + +// previously used if exists and has ammo, (second) best otherwise +void W_LastWeapon(void); diff --git a/qcsrc/server/weapons/spawning.qc b/qcsrc/server/weapons/spawning.qc new file mode 100644 index 0000000000..04dd3f6f23 --- /dev/null +++ b/qcsrc/server/weapons/spawning.qc @@ -0,0 +1,181 @@ +string W_Apply_Weaponreplace(string in) +{ + float n = tokenize_console(in); + string out = "", s, replacement; + float i, j; + entity e; + for(i = 0; i < n; ++i) + { + replacement = ""; + s = argv(i); + + for(j = WEP_FIRST; j <= WEP_LAST; ++j) + { + e = get_weaponinfo(j); + if(e.netname == s) + { + replacement = e.weaponreplace; + } + } + + if(replacement == "") + out = strcat(out, " ", s); + else if(replacement != "0") + out = strcat(out, " ", replacement); + } + return substring(out, 1, -1); +} + +void weapon_defaultspawnfunc(float wpn) +{ + entity e; + float t; + string s; + entity oldself; + float i, j; + float f; + + if(self.classname != "droppedweapon" && self.classname != "replacedweapon") + { + e = get_weaponinfo(wpn); + + if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) + { + objerror("Attempted to spawn a mutator-blocked weapon rejected"); + startitem_failed = TRUE; + return; + } + + s = W_Apply_Weaponreplace(e.netname); + ret_string = s; + other = e; + MUTATOR_CALLHOOK(SetWeaponreplace); + s = ret_string; + if(s == "") + { + remove(self); + startitem_failed = TRUE; + return; + } + t = tokenize_console(s); + if(t >= 2) + { + self.team = --internalteam; + oldself = self; + for(i = 1; i < t; ++i) + { + s = argv(i); + for(j = WEP_FIRST; j <= WEP_LAST; ++j) + { + e = get_weaponinfo(j); + if(e.netname == s) + { + self = spawn(); + copyentity(oldself, self); + self.classname = "replacedweapon"; + weapon_defaultspawnfunc(j); + break; + } + } + if(j > WEP_LAST) + { + print("The weapon replace list for ", oldself.classname, " contains an unknown weapon ", s, ". Skipped.\n"); + } + } + self = oldself; + } + if(t >= 1) // always the case! + { + s = argv(0); + wpn = 0; + for(j = WEP_FIRST; j <= WEP_LAST; ++j) + { + e = get_weaponinfo(j); + if(e.netname == s) + { + wpn = j; + break; + } + } + if(j > WEP_LAST) + { + print("The weapon replace list for ", self.classname, " contains an unknown weapon ", s, ". Skipped.\n"); + } + } + if(wpn == 0) + { + remove(self); + startitem_failed = TRUE; + return; + } + } + + e = get_weaponinfo(wpn); + + if(!self.respawntime) + { + if(e.weapons & WEPSET_SUPERWEAPONS) + { + self.respawntime = g_pickup_respawntime_superweapon; + self.respawntimejitter = g_pickup_respawntimejitter_superweapon; + } + else + { + self.respawntime = g_pickup_respawntime_weapon; + self.respawntimejitter = g_pickup_respawntimejitter_weapon; + } + } + + if(e.weapons & WEPSET_SUPERWEAPONS) + if(!self.superweapons_finished) + self.superweapons_finished = autocvar_g_balance_superweapons_time; + + // if we don't already have ammo, give us some ammo + if(!self.(e.ammo_field)) + { + switch(e.ammo_field) + { + case ammo_shells: self.ammo_shells = cvar("g_pickup_shells_weapon"); break; + case ammo_nails: self.ammo_nails = cvar("g_pickup_nails_weapon"); break; + case ammo_rockets: self.ammo_rockets = cvar("g_pickup_rockets_weapon"); break; + case ammo_cells: self.ammo_cells = cvar("g_pickup_cells_weapon"); break; + case ammo_plasma: self.ammo_plasma = cvar("g_pickup_plasma_weapon"); break; + case ammo_fuel: self.ammo_fuel = cvar("g_pickup_fuel_weapon"); break; + } + } + + #if 0 // WEAPONTODO + if(e.items) + { + for(i = 0, j = 1; i < 24; ++i, j *= 2) + { + if(e.items & j) + { + ammotype = Item_CounterField(j); + if(!self.ammotype) + self.ammotype = cvar(strcat("g_pickup_", Item_CounterFieldName(j), "_weapon")); + } + } + } + #endif + + // pickup anyway + if(g_pickup_weapons_anyway) + self.pickup_anyway = TRUE; + + f = FL_WEAPON; + + // no weapon-stay on superweapons + if(e.weapons & WEPSET_SUPERWEAPONS) + f |= FL_NO_WEAPON_STAY; + + // weapon stay isn't supported for teamed weapons + if(self.team) + f |= FL_NO_WEAPON_STAY; + + StartItem(e.model, "weapons/weaponpickup.wav", self.respawntime, self.respawntimejitter, e.message, 0, e.weapon, f, weapon_pickupevalfunc, e.bot_pickupbasevalue); + #if 0 // WEAPONTODO + if (self.modelindex) // don't precache if self was removed + WEP_ACTION(e.weapon, WR_INIT); + #endif +} diff --git a/qcsrc/server/weapons/spawning.qh b/qcsrc/server/weapons/spawning.qh new file mode 100644 index 0000000000..c6939e53af --- /dev/null +++ b/qcsrc/server/weapons/spawning.qh @@ -0,0 +1,3 @@ +string W_Apply_Weaponreplace(string in); + +void weapon_defaultspawnfunc(float wpn); diff --git a/qcsrc/server/weapons/throwing.qc b/qcsrc/server/weapons/throwing.qc new file mode 100644 index 0000000000..552f035578 --- /dev/null +++ b/qcsrc/server/weapons/throwing.qc @@ -0,0 +1,189 @@ +void thrown_wep_think() +{ + self.nextthink = time; + if(self.oldorigin != self.origin) + { + self.SendFlags |= ISF_LOCATION; + self.oldorigin = self.origin; + } + self.owner = world; + float timeleft = self.savenextthink - time; + if(timeleft > 1) + SUB_SetFade(self, self.savenextthink - 1, 1); + else if(timeleft > 0) + SUB_SetFade(self, time, timeleft); + else + SUB_VanishOrRemove(self); +} + +// returns amount of ammo used as string, or -1 for failure, or 0 for no ammo count +string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo) +{ + entity oldself, wep; + float thisammo, i; + string s; + var .float ammotype = (get_weaponinfo(wpn)).ammo_field; + + wep = spawn(); + + setorigin(wep, org); + wep.classname = "droppedweapon"; + wep.velocity = velo; + wep.owner = wep.enemy = own; + wep.flags |= FL_TOSSED; + wep.colormap = own.colormap; + + W_DropEvent(WR_DROP,own,wpn,wep); + + if(WepSet_FromWeapon(wpn) & WEPSET_SUPERWEAPONS) + { + if(own.items & IT_UNLIMITED_SUPERWEAPONS) + { + wep.superweapons_finished = time + autocvar_g_balance_superweapons_time; + } + else + { + float superweapons = 1; + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + if(WepSet_FromWeapon(i) & WEPSET_SUPERWEAPONS) + if(own.weapons & WepSet_FromWeapon(i)) + ++superweapons; + if(superweapons <= 1) + { + wep.superweapons_finished = own.superweapons_finished; + own.superweapons_finished = 0; + } + else + { + float timeleft = own.superweapons_finished - time; + float weptimeleft = timeleft / superweapons; + wep.superweapons_finished = time + weptimeleft; + own.superweapons_finished -= weptimeleft; + } + } + } + + oldself = self; + self = wep; + weapon_defaultspawnfunc(wpn); + self = oldself; + if(startitem_failed) + return string_null; + wep.glowmod = own.weaponentity_glowmod; + wep.think = thrown_wep_think; + wep.savenextthink = wep.nextthink; + wep.nextthink = min(wep.nextthink, time + 0.5); + wep.pickup_anyway = TRUE; // these are ALWAYS pickable + + //wa = W_AmmoItemCode(wpn); + if(ammotype == ammo_none) + { + return ""; + } + else + { + s = ""; + + if(doreduce && g_weapon_stay == 2) + { + // if our weapon is loaded, give its load back to the player + if(self.(weapon_load[self.weapon]) > 0) + { + own.ammotype += self.(weapon_load[self.weapon]); + self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading + } + + wep.ammotype = 0; + } + else if(doreduce) + { + // if our weapon is loaded, give its load back to the player + if(self.(weapon_load[self.weapon]) > 0) + { + own.ammotype += self.(weapon_load[self.weapon]); + self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading + } + + thisammo = min(own.ammotype, wep.ammotype); + wep.ammotype = thisammo; + own.ammotype -= thisammo; + + switch(ammotype) + { + case ammo_shells: s = sprintf("%s and %d shells", s, thisammo); break; + case ammo_nails: s = sprintf("%s and %d nails", s, thisammo); break; + case ammo_rockets: s = sprintf("%s and %d rockets", s, thisammo); break; + case ammo_cells: s = sprintf("%s and %d cells", s, thisammo); break; + case ammo_plasma: s = sprintf("%s and %d plasma", s, thisammo); break; + case ammo_fuel: s = sprintf("%s and %d fuel", s, thisammo); break; + } + + s = substring(s, 5, -1); + } + return s; + } +} + +float W_IsWeaponThrowable(float w) +{ + if (!autocvar_g_pickup_items) + return 0; + if (g_weaponarena) + return 0; + if (g_cts) + return 0; + if (g_nexball && w == WEP_MORTAR) + return 0; + if(w == 0) + return 0; + + #if 0 + if(start_weapons & WepSet_FromWeapon(w)) + { + // start weapons that take no ammo can't be dropped (this prevents dropping the laser, as long as it continues to use no ammo) + if(start_items & IT_UNLIMITED_WEAPON_AMMO) + return 0; + if((get_weaponinfo(w)).ammo_field == ammo_none) + return 0; + } + return 1; + #else + return (get_weaponinfo(w)).weaponthrowable; + #endif +} + +// toss current weapon +void W_ThrowWeapon(vector velo, vector delta, float doreduce) +{ + float w; + string a; + + w = self.weapon; + if (w == 0) + return; // just in case + if(MUTATOR_CALLHOOK(ForbidThrowCurrentWeapon)) + return; + if(!autocvar_g_weapon_throwable) + return; + if(self.weaponentity.state != WS_READY) + return; + if(!W_IsWeaponThrowable(w)) + return; + + if(!(self.weapons & WepSet_FromWeapon(w))) + return; + self.weapons &= ~WepSet_FromWeapon(w); + + W_SwitchWeapon_Force(self, w_getbestweapon(self)); + a = W_ThrowNewWeapon(self, w, doreduce, self.origin + delta, velo); + + if(!a) return; + Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_WEAPON_DROP, a, w); +} + +void SpawnThrownWeapon(vector org, float w) +{ + if(self.weapons & WepSet_FromWeapon(self.weapon)) + if(W_IsWeaponThrowable(self.weapon)) + W_ThrowNewWeapon(self, self.weapon, FALSE, org, randomvec() * 125 + '0 0 200'); +} diff --git a/qcsrc/server/weapons/throwing.qh b/qcsrc/server/weapons/throwing.qh new file mode 100644 index 0000000000..c09a8f8829 --- /dev/null +++ b/qcsrc/server/weapons/throwing.qh @@ -0,0 +1,12 @@ +.float savenextthink; +void thrown_wep_think(); + +// returns amount of ammo used as string, or -1 for failure, or 0 for no ammo count +string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo); + +float W_IsWeaponThrowable(float w); + +// toss current weapon +void W_ThrowWeapon(vector velo, vector delta, float doreduce); + +void SpawnThrownWeapon(vector org, float w); diff --git a/qcsrc/server/weapons/tracing.qc b/qcsrc/server/weapons/tracing.qc new file mode 100644 index 0000000000..755ab59e59 --- /dev/null +++ b/qcsrc/server/weapons/tracing.qc @@ -0,0 +1,470 @@ +// this function calculates w_shotorg and w_shotdir based on the weapon model +// offset, trueaim and antilag, and won't put w_shotorg inside a wall. +// make sure you call makevectors first (FIXME?) +void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector mi, vector ma, float antilag, float recoil, string snd, float chan, float maxdamage, float range) +{ + float nudge = 1; // added to traceline target and subtracted from result TOOD(divVerent): do we still need this? Doesn't the engine do this now for us? + float oldsolid; + vector vecs, dv; + oldsolid = ent.dphitcontentsmask; + if(ent.weapon == WEP_RIFLE) + ent.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_CORPSE; + else + ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; + if(antilag) + WarpZone_traceline_antilag(world, ent.origin + ent.view_ofs, ent.origin + ent.view_ofs + s_forward * range, MOVE_NORMAL, ent, ANTILAG_LATENCY(ent)); + // passing world, because we do NOT want it to touch dphitcontentsmask + else + WarpZone_TraceLine(ent.origin + ent.view_ofs, ent.origin + ent.view_ofs + s_forward * range, MOVE_NOMONSTERS, ent); + ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; + + vector vf, vr, vu; + vf = v_forward; + vr = v_right; + vu = v_up; + w_shotend = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); // warpzone support + v_forward = vf; + v_right = vr; + v_up = vu; + + // un-adjust trueaim if shotend is too close + if(vlen(w_shotend - (ent.origin + ent.view_ofs)) < autocvar_g_trueaim_minrange) + w_shotend = ent.origin + ent.view_ofs + s_forward * autocvar_g_trueaim_minrange; + + // track max damage + if(accuracy_canbegooddamage(ent)) + accuracy_add(ent, ent.weapon, maxdamage, 0); + + W_HitPlotAnalysis(ent, v_forward, v_right, v_up); + + if(ent.weaponentity.movedir_x > 0) + vecs = ent.weaponentity.movedir; + else + vecs = '0 0 0'; + + dv = v_right * -vecs_y + v_up * vecs_z; + w_shotorg = ent.origin + ent.view_ofs + dv; + + // now move the shotorg forward as much as requested if possible + if(antilag) + { + if(ent.antilag_debug) + tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, ent, ent.antilag_debug); + else + tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, ent, ANTILAG_LATENCY(ent)); + } + else + tracebox(w_shotorg, mi, ma, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, ent); + w_shotorg = trace_endpos - v_forward * nudge; + // calculate the shotdir from the chosen shotorg + w_shotdir = normalize(w_shotend - w_shotorg); + + //vector prevdir = w_shotdir; + //vector prevorg = w_shotorg; + //vector prevend = w_shotend; + + if (antilag) + if (!ent.cvar_cl_noantilag) + { + if (autocvar_g_antilag == 1) // switch to "ghost" if not hitting original + { + traceline(w_shotorg, w_shotorg + w_shotdir * range, MOVE_NORMAL, ent); + if (!trace_ent.takedamage) + { + traceline_antilag_force (ent, w_shotorg, w_shotorg + w_shotdir * range, MOVE_NORMAL, ent, ANTILAG_LATENCY(ent)); + if (trace_ent.takedamage && IS_PLAYER(trace_ent)) + { + entity e; + e = trace_ent; + traceline(w_shotorg, e.origin, MOVE_NORMAL, ent); + if(trace_ent == e) + w_shotdir = normalize(trace_ent.origin - w_shotorg); + } + } + } + else if(autocvar_g_antilag == 3) // client side hitscan + { + // this part MUST use prydon cursor + if (ent.cursor_trace_ent) // client was aiming at someone + if (ent.cursor_trace_ent != ent) // just to make sure + if (ent.cursor_trace_ent.takedamage) // and that person is killable + if (IS_PLAYER(ent.cursor_trace_ent)) // and actually a player + { + // verify that the shot would miss without antilag + // (avoids an issue where guns would always shoot at their origin) + traceline(w_shotorg, w_shotorg + w_shotdir * range, MOVE_NORMAL, ent); + if (!trace_ent.takedamage) + { + // verify that the shot would hit if altered + traceline(w_shotorg, ent.cursor_trace_ent.origin, MOVE_NORMAL, ent); + if (trace_ent == ent.cursor_trace_ent) + w_shotdir = normalize(ent.cursor_trace_ent.origin - w_shotorg); + else + print("antilag fail\n"); + } + } + } + } + + ent.dphitcontentsmask = oldsolid; // restore solid type (generally SOLID_SLIDEBOX) + + if (!autocvar_g_norecoil) + ent.punchangle_x = recoil * -1; + + if (snd != "") + { + sound (ent, chan, snd, VOL_BASE, ATTN_NORM); + W_PlayStrengthSound(ent); + } + + // nudge w_shotend so a trace to w_shotend hits + w_shotend = w_shotend + normalize(w_shotend - w_shotorg) * nudge; + //if(w_shotend != prevend) { printf("SERVER: shotEND differs: %s - %s\n", vtos(w_shotend), vtos(prevend)); } + //if(w_shotorg != prevorg) { printf("SERVER: shotORG differs: %s - %s\n", vtos(w_shotorg), vtos(prevorg)); } + //if(w_shotdir != prevdir) { printf("SERVER: shotDIR differs: %s - %s\n", vtos(w_shotdir), vtos(prevdir)); } +} + +vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity, float forceAbsolute) +{ + vector mdirection; + float mspeed; + vector outvelocity; + + mvelocity = mvelocity * g_weaponspeedfactor; + + mdirection = normalize(mvelocity); + mspeed = vlen(mvelocity); + + outvelocity = get_shotvelocity(pvelocity, mdirection, mspeed, (forceAbsolute ? 0 : autocvar_g_projectiles_newton_style), autocvar_g_projectiles_newton_style_2_minfactor, autocvar_g_projectiles_newton_style_2_maxfactor); + + return outvelocity; +} + +void W_SetupProjVelocity_Explicit(entity proj, vector dir, vector upDir, float pSpeed, float pUpSpeed, float pZSpeed, float spread, float forceAbsolute) +{ + if(proj.owner == world) + error("Unowned missile"); + + dir = dir + upDir * (pUpSpeed / pSpeed); + dir_z += pZSpeed / pSpeed; + pSpeed *= vlen(dir); + dir = normalize(dir); + + #if 0 + if(autocvar_g_projectiles_spread_style != mspercallsstyle) + { + mspercallsum = mspercallcount = 0; + mspercallsstyle = autocvar_g_projectiles_spread_style; + } + mspercallsum -= gettime(GETTIME_HIRES); + #endif + + dir = W_CalculateSpread(dir, spread, g_weaponspreadfactor, autocvar_g_projectiles_spread_style); + + #if 0 + mspercallsum += gettime(GETTIME_HIRES); + mspercallcount += 1; + print("avg: ", ftos(mspercallcount / mspercallsum), " per sec\n"); + #endif + + proj.velocity = W_CalculateProjectileVelocity(proj.owner.velocity, pSpeed * dir, forceAbsolute); +} + + +// ==================== +// Ballistics Tracing +// ==================== + +void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, float deathtype) +{ + vector hitloc, force, endpoint, dir; + entity ent, endent; + float endq3surfaceflags; + float totaldmg; + entity o; + + float length; + vector beampos; + string snd; + entity pseudoprojectile; + float f, ffs; + + pseudoprojectile = world; + + dir = normalize(end - start); + length = vlen(end - start); + force = dir * bforce; + + // go a little bit into the wall because we need to hit this wall later + end = end + dir; + + totaldmg = 0; + + // trace multiple times until we hit a wall, each obstacle will be made + // non-solid so we can hit the next, while doing this we spawn effects and + // note down which entities were hit so we can damage them later + o = self; + while (1) + { + if(self.antilag_debug) + WarpZone_traceline_antilag (self, start, end, FALSE, o, self.antilag_debug); + else + WarpZone_traceline_antilag (self, start, end, FALSE, o, ANTILAG_LATENCY(self)); + if(o && WarpZone_trace_firstzone) + { + o = world; + continue; + } + + if(trace_ent.solid == SOLID_BSP || trace_ent.solid == SOLID_SLIDEBOX) + Damage_DamageInfo(trace_endpos, bdamage, 0, 0, force, deathtype, trace_ent.species, self); + + // if it is world we can't hurt it so stop now + if (trace_ent == world || trace_fraction == 1) + break; + + // make the entity non-solid so we can hit the next one + trace_ent.railgunhit = TRUE; + trace_ent.railgunhitloc = end; + trace_ent.railgunhitsolidbackup = trace_ent.solid; + trace_ent.railgundistance = vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos) - start); + trace_ent.railgunforce = WarpZone_TransformVelocity(WarpZone_trace_transform, force); + + // stop if this is a wall + if (trace_ent.solid == SOLID_BSP) + break; + + // make the entity non-solid + trace_ent.solid = SOLID_NOT; + } + + endpoint = trace_endpos; + endent = trace_ent; + endq3surfaceflags = trace_dphitq3surfaceflags; + + // find all the entities the railgun hit and restore their solid state + ent = findfloat(world, railgunhit, TRUE); + while (ent) + { + // restore their solid type + ent.solid = ent.railgunhitsolidbackup; + ent = findfloat(ent, railgunhit, TRUE); + } + + // spawn a temporary explosion entity for RadiusDamage calls + //explosion = spawn(); + + // Find all non-hit players the beam passed close by + if(deathtype == WEP_VAPORIZER || deathtype == WEP_VORTEX) + { + FOR_EACH_REALCLIENT(msg_entity) + if(msg_entity != self) + if(!msg_entity.railgunhit) + if(!(IS_SPEC(msg_entity) && msg_entity.enemy == self)) // we use realclient, so spectators can hear the whoosh too + { + // nearest point on the beam + beampos = start + dir * bound(0, (msg_entity.origin - start) * dir, length); + + f = bound(0, 1 - vlen(beampos - msg_entity.origin) / 512, 1); + if(f <= 0) + continue; + + snd = strcat("weapons/nexwhoosh", ftos(floor(random() * 3) + 1), ".wav"); + + if(!pseudoprojectile) + pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume + soundtoat(MSG_ONE, pseudoprojectile, beampos, CH_SHOTS, snd, VOL_BASE * f, ATTEN_NONE); + } + + if(pseudoprojectile) + remove(pseudoprojectile); + } + + // find all the entities the railgun hit and hurt them + ent = findfloat(world, railgunhit, TRUE); + while (ent) + { + // get the details we need to call the damage function + hitloc = ent.railgunhitloc; + + f = ExponentialFalloff(mindist, maxdist, halflifedist, ent.railgundistance); + ffs = ExponentialFalloff(mindist, maxdist, forcehalflifedist, ent.railgundistance); + + if(accuracy_isgooddamage(self.realowner, ent)) + totaldmg += bdamage * f; + + // apply the damage + if (ent.takedamage) + Damage (ent, self, self, bdamage * f, deathtype, hitloc, ent.railgunforce * ffs); + + // create a small explosion to throw gibs around (if applicable) + //setorigin (explosion, hitloc); + //RadiusDamage (explosion, self, 10, 0, 50, world, world, 300, deathtype); + + ent.railgunhitloc = '0 0 0'; + ent.railgunhitsolidbackup = SOLID_NOT; + ent.railgunhit = FALSE; + ent.railgundistance = 0; + + // advance to the next entity + ent = findfloat(ent, railgunhit, TRUE); + } + + // calculate hits and fired shots for hitscan + accuracy_add(self, self.weapon, 0, min(bdamage, totaldmg)); + + trace_endpos = endpoint; + trace_ent = endent; + trace_dphitq3surfaceflags = endq3surfaceflags; +} + +void fireBullet_trace_callback(vector start, vector hit, vector end) +{ + if(vlen(hit - start) > 16) + trailparticles(world, fireBullet_trace_callback_eff, start, hit); + WarpZone_trace_forent = world; + fireBullet_last_hit = world; +} + +void fireBullet(vector start, vector dir, float spread, float max_solid_penetration, float damage, float force, float dtype, float tracereffects) +{ + vector end; + + dir = normalize(dir + randomvec() * spread); + end = start + dir * MAX_SHOT_DISTANCE; + + entity pl; + fireBullet_last_hit = world; + float solid_penetration_left = 1; + float total_damage = 0; + + if(tracereffects & EF_RED) + fireBullet_trace_callback_eff = particleeffectnum("tr_rifle"); + else if(tracereffects & EF_BLUE) + fireBullet_trace_callback_eff = particleeffectnum("tr_rifle_weak"); + else + fireBullet_trace_callback_eff = particleeffectnum("tr_bullet"); + + float lag = ANTILAG_LATENCY(self); + if(lag < 0.001) + lag = 0; + if (!IS_REAL_CLIENT(self)) + lag = 0; + if(autocvar_g_antilag == 0 || self.cvar_cl_noantilag) + lag = 0; // only do hitscan, but no antilag + if(lag) + { + FOR_EACH_PLAYER(pl) + if(pl != self) + antilag_takeback(pl, time - lag); + FOR_EACH_MONSTER(pl) + antilag_takeback(pl, time - lag); + } + + WarpZone_trace_forent = self; + + for (;;) + { + // TODO also show effect while tracing + WarpZone_TraceBox_ThroughZone(start, '0 0 0', '0 0 0', end, FALSE, WarpZone_trace_forent, world, fireBullet_trace_callback); + dir = WarpZone_TransformVelocity(WarpZone_trace_transform, dir); + end = WarpZone_TransformOrigin(WarpZone_trace_transform, end); + start = trace_endpos; + entity hit = trace_ent; + + // When hitting sky, stop. + if (pointcontents(start) == CONTENT_SKY) + break; + + if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) + break; + + // if we hit "weapclip", bail out + // + // rationale of this check: + // + // any shader that is solid, nodraw AND trans is meant to clip weapon + // shots and players, but has no other effect! + // + // if it is not trans, it is caulk and should not have this side effect + // + // matching shaders: + // common/weapclip (intended) + // common/noimpact (is supposed to eat projectiles, but is erased anyway) + float is_weapclip = 0; + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW) + if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)) + if (!(trace_dphitcontents & DPCONTENTS_OPAQUE)) + is_weapclip = 1; + + if(!hit || hit.solid == SOLID_BSP || hit.solid == SOLID_SLIDEBOX) + Damage_DamageInfo(start, damage * solid_penetration_left, 0, 0, max(1, force) * dir * solid_penetration_left, dtype, hit.species, self); + + if (hit && hit != WarpZone_trace_forent && hit != fireBullet_last_hit) // Avoid self-damage (except after going through a warp); avoid hitting the same entity twice (engine bug). + { + fireBullet_last_hit = hit; + yoda = 0; + float g = accuracy_isgooddamage(self, hit); + Damage(hit, self, self, damage * solid_penetration_left, dtype, start, force * dir * solid_penetration_left); + // calculate hits for ballistic weapons + if(g) + { + // do not exceed 100% + float added_damage = min(damage - total_damage, damage * solid_penetration_left); + total_damage += damage * solid_penetration_left; + accuracy_add(self, self.weapon, 0, added_damage); + } + } + + if (is_weapclip) + break; + + // go through solid! + // outside the world? forget it + if(start_x > world.maxs_x || start_y > world.maxs_y || start_z > world.maxs_z || start_x < world.mins_x || start_y < world.mins_y || start_z < world.mins_z) + break; + + float maxdist; + if(max_solid_penetration < 0) + break; + else if(hit.ballistics_density < -1) + break; // -2: no solid penetration, ever + else if(hit.ballistics_density < 0) + maxdist = vlen(hit.maxs - hit.mins) + 1; // -1: infinite travel distance + else if(hit.ballistics_density == 0) + maxdist = max_solid_penetration * solid_penetration_left; + else + maxdist = max_solid_penetration * solid_penetration_left * hit.ballistics_density; + + if(maxdist <= autocvar_g_ballistics_mindistance) + break; + + // move the entity along its velocity until it's out of solid, then let it resume + // The previously hit entity is ignored here! + traceline_inverted (start, start + dir * maxdist, MOVE_NORMAL, WarpZone_trace_forent, TRUE, hit); + if(trace_fraction == 1) // 1: we never got out of solid + break; + + float dist_taken = max(autocvar_g_ballistics_mindistance, vlen(trace_endpos - start)); + solid_penetration_left *= (dist_taken / maxdist); + + // Only show effect when going through a player (invisible otherwise) + if (hit && (hit.solid != SOLID_BSP)) + if(vlen(trace_endpos - start) > 4) + trailparticles(self, fireBullet_trace_callback_eff, start, trace_endpos); + + start = trace_endpos; + + if(hit.solid == SOLID_BSP) + Damage_DamageInfo(start, 0, 0, 0, max(1, force) * normalize(dir) * -solid_penetration_left, dtype, 0, self); + } + + if(lag) + { + FOR_EACH_PLAYER(pl) + if(pl != self) + antilag_restore(pl); + FOR_EACH_MONSTER(pl) + antilag_restore(pl); + } +} diff --git a/qcsrc/server/weapons/tracing.qh b/qcsrc/server/weapons/tracing.qh new file mode 100644 index 0000000000..49605e8f21 --- /dev/null +++ b/qcsrc/server/weapons/tracing.qh @@ -0,0 +1,55 @@ +vector w_shotorg; +vector w_shotdir; +vector w_shotend; + +// this function calculates w_shotorg and w_shotdir based on the weapon model +// offset, trueaim and antilag, and won't put w_shotorg inside a wall. +// make sure you call makevectors first (FIXME?) +void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector mi, vector ma, float antilag, float recoil, string snd, float chan, float maxdamage, float range); + +#define W_SetupShot_Dir_ProjectileSize(ent,s_forward,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize_Range(ent, s_forward, mi, ma, antilag, recoil, snd, chan, maxdamage, MAX_SHOT_DISTANCE) +#define W_SetupShot_ProjectileSize(ent,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, v_forward, mi, ma, antilag, recoil, snd, chan, maxdamage) +#define W_SetupShot_Dir(ent,s_forward,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, s_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage) +#define W_SetupShot(ent,antilag,recoil,snd,chan,maxdamage) W_SetupShot_ProjectileSize(ent, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage) +#define W_SetupShot_Range(ent,antilag,recoil,snd,chan,maxdamage,range) W_SetupShot_Dir_ProjectileSize_Range(ent, v_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage, range) + +vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity, float forceAbsolute); + +#if 0 +float mspercallsum; +float mspercallsstyle; +float mspercallcount; +#endif + +void W_SetupProjVelocity_Explicit(entity proj, vector dir, vector upDir, float pSpeed, float pUpSpeed, float pZSpeed, float spread, float forceAbsolute); + +#define W_SetupProjVelocity_Basic(ent,pspeed,pspread) W_SetupProjVelocity_Explicit(ent, w_shotdir, v_up, pspeed, 0, 0, pspread, FALSE) + +#define W_SetupProjVelocity_UP_PRE(ent,wepname,prefix) W_SetupProjVelocity_Explicit(ent, w_shotdir, v_up, WEP_CVAR(wepname, prefix##speed), WEP_CVAR(wepname, prefix##speed_up), WEP_CVAR(wepname, prefix##speed_z), WEP_CVAR(wepname, prefix##spread), FALSE) +#define W_SetupProjVelocity_UP_PRI(ent,wepname) W_SetupProjVelocity_Explicit(ent, w_shotdir, v_up, WEP_CVAR_PRI(wepname, speed), WEP_CVAR_PRI(wepname, speed_up), WEP_CVAR_PRI(wepname, speed_z), WEP_CVAR_PRI(wepname, spread), FALSE) +#define W_SetupProjVelocity_UP_SEC(ent,wepname) W_SetupProjVelocity_Explicit(ent, w_shotdir, v_up, WEP_CVAR_SEC(wepname, speed), WEP_CVAR_SEC(wepname, speed_up), WEP_CVAR_SEC(wepname, speed_z), WEP_CVAR_SEC(wepname, spread), FALSE) + +#define W_SetupProjVelocity_UP_BOTH(ent,wepname,isprimary) \ + if(isprimary) { W_SetupProjVelocity_UP_PRI(ent, wepname); } \ + else { W_SetupProjVelocity_UP_SEC(ent, wepname); } + +#define W_SetupProjVelocity_PRE(ent,wepname,prefix) W_SetupProjVelocity_Explicit(ent, w_shotdir, v_up, WEP_CVAR(wepname, prefix##speed), 0, 0, WEP_CVAR(wepname, prefix##spread), FALSE) +#define W_SetupProjVelocity_PRI(ent,wepname) W_SetupProjVelocity_Explicit(ent, w_shotdir, v_up, WEP_CVAR_PRI(wepname, speed), 0, 0, WEP_CVAR_PRI(wepname, spread), FALSE) +#define W_SetupProjVelocity_SEC(ent,wepname) W_SetupProjVelocity_Explicit(ent, w_shotdir, v_up, WEP_CVAR_SEC(wepname, speed), 0, 0, WEP_CVAR_SEC(wepname, spread), FALSE) + +#define W_SetupProjVelocity_BOTH(ent,wepname,isprimary) \ + if(isprimary) { W_SetupProjVelocity_PRI(ent, wepname); } \ + else { W_SetupProjVelocity_SEC(ent, wepname); } + +// ==================== +// Ballistics Tracing +// ==================== + +.float railgundistance; +.vector railgunforce; +void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, float deathtype); + +float fireBullet_trace_callback_eff; +entity fireBullet_last_hit; +void fireBullet_trace_callback(vector start, vector hit, vector end); +void fireBullet(vector start, vector dir, float spread, float max_solid_penetration, float damage, float force, float dtype, float tracereffects); diff --git a/qcsrc/server/weapons/weaponstats.qc b/qcsrc/server/weapons/weaponstats.qc new file mode 100644 index 0000000000..d83db8b822 --- /dev/null +++ b/qcsrc/server/weapons/weaponstats.qc @@ -0,0 +1,103 @@ +void WeaponStats_Init() +{ + weaponstats_buffer = ((autocvar_sv_weaponstats_file != "") ? buf_create() : -1); +} + +void WeaponStats_ready(entity fh, entity pass, float status) +{ + float i, j, n, ibot, jbot, idx; + vector v; + string prefix, s; + switch(status) + { + case URL_READY_CANWRITE: + // we can write + prefix = strcat(autocvar_hostname, "\t", GetGametype(), "_", GetMapname(), "\t"); + url_fputs(fh, "#begin statsfile\n"); + url_fputs(fh, strcat("#date ", strftime(TRUE, "%a %b %e %H:%M:%S %Z %Y"), "\n")); +#ifdef WATERMARK + url_fputs(fh, strcat("#version ", WATERMARK, "\n")); +#endif + url_fputs(fh, strcat("#config ", ftos(crc16(FALSE, cvar_purechanges)), "\n")); + url_fputs(fh, strcat("#cvar_purechanges ", ftos(cvar_purechanges_count), "\n")); + n = tokenizebyseparator(cvar_purechanges, "\n"); + for(i = 0; i < n; ++i) + url_fputs(fh, strcat("#cvar_purechange ", argv(i), "\n")); + for(i = WEP_FIRST; i <= WEP_LAST; ++i) for(ibot = 0; ibot <= 1; ++ibot) + for(j = WEP_FIRST; j <= WEP_LAST; ++j) for(jbot = 0; jbot <= 1; ++jbot) + { + idx = WEAPONSTATS_GETINDEX(i, ibot, j, jbot); + v = stov(bufstr_get(weaponstats_buffer, idx)); + if(v != '0 0 0') + { + //vector is: kills hits damage + url_fputs(fh, sprintf("%s%d %d\t%d %d\t", prefix, i, ibot, j, jbot)); + url_fputs(fh, sprintf("%d %d %g\n", v_x, v_y, v_z)); + } + } + url_fputs(fh, "#end\n\n"); + url_fclose(fh); + break; + case URL_READY_CANREAD: + // url_fclose is processing, we got a response for writing the data + // this must come from HTTP + print("Got response from weapon stats server:\n"); + while((s = url_fgets(fh))) + print(" ", s, "\n"); + print("End of response.\n"); + url_fclose(fh); + break; + case URL_READY_CLOSED: + // url_fclose has finished + print("Weapon stats written\n"); + buf_del(weaponstats_buffer); + weaponstats_buffer = -1; + break; + case URL_READY_ERROR: + default: + print("Weapon stats writing failed: ", ftos(status), "\n"); + buf_del(weaponstats_buffer); + weaponstats_buffer = -1; + break; + } +} + +void WeaponStats_Shutdown() +{ + if(weaponstats_buffer < 0) + return; + if(autocvar_sv_weaponstats_file != "") + { + url_multi_fopen(autocvar_sv_weaponstats_file, FILE_APPEND, WeaponStats_ready, world); + } + else + { + buf_del(weaponstats_buffer); + weaponstats_buffer = -1; + } +} + +void WeaponStats_LogItem(float awep, float abot, float vwep, float vbot, vector item) +{ + float idx; + if(weaponstats_buffer < 0) + return; + if(awep < WEP_FIRST || vwep < WEP_FIRST) + return; + if(awep > WEP_LAST || vwep > WEP_LAST) + return; + idx = WEAPONSTATS_GETINDEX(awep,abot,vwep,vbot); + bufstr_set(weaponstats_buffer, idx, vtos(stov(bufstr_get(weaponstats_buffer, idx)) + item)); +} + +void WeaponStats_LogDamage(float awep, float abot, float vwep, float vbot, float damage) +{ + if(damage < 0) + error("negative damage?"); + WeaponStats_LogItem(awep, abot, vwep, vbot, '0 0 1' * damage + '0 1 0'); +} + +void WeaponStats_LogKill(float awep, float abot, float vwep, float vbot) +{ + WeaponStats_LogItem(awep, abot, vwep, vbot, '1 0 0'); +} diff --git a/qcsrc/server/weapons/weaponstats.qh b/qcsrc/server/weapons/weaponstats.qh new file mode 100644 index 0000000000..08ae8dd0a5 --- /dev/null +++ b/qcsrc/server/weapons/weaponstats.qh @@ -0,0 +1,10 @@ +float weaponstats_buffer; + +void WeaponStats_Init(); +void WeaponStats_ready(entity fh, entity pass, float status); +void WeaponStats_Shutdown(); +void WeaponStats_LogItem(float awep, float abot, float vwep, float vbot, vector item); +void WeaponStats_LogDamage(float awep, float abot, float vwep, float vbot, float damage); +void WeaponStats_LogKill(float awep, float abot, float vwep, float vbot); + +#define WEAPONSTATS_GETINDEX(awep,abot,vwep,vbot) (((vwep) + (awep) * (WEP_LAST - WEP_FIRST + 1) - (WEP_FIRST + WEP_FIRST * (WEP_LAST - WEP_FIRST + 1))) * 4 + (abot) * 2 + (vbot)) diff --git a/qcsrc/server/weapons/weaponsystem.qc b/qcsrc/server/weapons/weaponsystem.qc new file mode 100644 index 0000000000..df3380116c --- /dev/null +++ b/qcsrc/server/weapons/weaponsystem.qc @@ -0,0 +1,956 @@ +/* +=========================================================================== + + CLIENT WEAPONSYSTEM CODE + Bring back W_Weaponframe + +=========================================================================== +*/ + +.float weapon_frametime; + +float W_WeaponRateFactor() +{ + float t; + t = 1.0 / g_weaponratefactor; + + weapon_rate = t; + MUTATOR_CALLHOOK(WeaponRateFactor); + t = weapon_rate; + + return t; +} + +// VorteX: static frame globals +const float WFRAME_DONTCHANGE = -1; +const float WFRAME_FIRE1 = 0; +const float WFRAME_FIRE2 = 1; +const float WFRAME_IDLE = 2; +const float WFRAME_RELOAD = 3; +.float wframe; + +void(float fr, float t, void() func) weapon_thinkf; + +float CL_Weaponentity_CustomizeEntityForClient() +{ + self.viewmodelforclient = self.owner; + if(IS_SPEC(other)) + if(other.enemy == self.owner) + self.viewmodelforclient = other; + return TRUE; +} + +/* + * supported formats: + * + * 1. simple animated model, muzzle flash handling on h_ model: + * h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation + * tags: + * shot = muzzle end (shot origin, also used for muzzle flashes) + * shell = casings ejection point (must be on the right hand side of the gun) + * weapon = attachment for v_tuba.md3 + * v_tuba.md3 - first and third person model + * g_tuba.md3 - pickup model + * + * 2. simple animated model, muzzle flash handling on v_ model: + * h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation + * tags: + * weapon = attachment for v_tuba.md3 + * v_tuba.md3 - first and third person model + * tags: + * shot = muzzle end (shot origin, also used for muzzle flashes) + * shell = casings ejection point (must be on the right hand side of the gun) + * g_tuba.md3 - pickup model + * + * 3. fully animated model, muzzle flash handling on h_ model: + * h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model + * tags: + * shot = muzzle end (shot origin, also used for muzzle flashes) + * shell = casings ejection point (must be on the right hand side of the gun) + * handle = corresponding to the origin of v_tuba.md3 (used for muzzle flashes) + * v_tuba.md3 - third person model + * g_tuba.md3 - pickup model + * + * 4. fully animated model, muzzle flash handling on v_ model: + * h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model + * tags: + * shot = muzzle end (shot origin) + * shell = casings ejection point (must be on the right hand side of the gun) + * v_tuba.md3 - third person model + * tags: + * shot = muzzle end (for muzzle flashes) + * g_tuba.md3 - pickup model + */ + +// writes: +// self.origin, self.angles +// self.weaponentity +// self.movedir, self.view_ofs +// attachment stuff +// anim stuff +// to free: +// call again with "" +// remove the ent +void CL_WeaponEntity_SetModel(string name) +{ + float v_shot_idx; + if (name != "") + { + // if there is a child entity, hide it until we're sure we use it + if (self.weaponentity) + self.weaponentity.model = ""; + setmodel(self, strcat("models/weapons/v_", name, ".md3")); // precision set below + v_shot_idx = gettagindex(self, "shot"); // used later + if(!v_shot_idx) + v_shot_idx = gettagindex(self, "tag_shot"); + + setmodel(self, strcat("models/weapons/h_", name, ".iqm")); // precision set below + // preset some defaults that work great for renamed zym files (which don't need an animinfo) + self.anim_fire1 = animfixfps(self, '0 1 0.01', '0 0 0'); + self.anim_fire2 = animfixfps(self, '1 1 0.01', '0 0 0'); + self.anim_idle = animfixfps(self, '2 1 0.01', '0 0 0'); + self.anim_reload = animfixfps(self, '3 1 0.01', '0 0 0'); + + // if we have a "weapon" tag, let's attach the v_ model to it ("invisible hand" style model) + // if we don't, this is a "real" animated model + if(gettagindex(self, "weapon")) + { + if (!self.weaponentity) + self.weaponentity = spawn(); + setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter + setattachment(self.weaponentity, self, "weapon"); + } + else if(gettagindex(self, "tag_weapon")) + { + if (!self.weaponentity) + self.weaponentity = spawn(); + setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter + setattachment(self.weaponentity, self, "tag_weapon"); + } + else + { + if(self.weaponentity) + remove(self.weaponentity); + self.weaponentity = world; + } + + setorigin(self,'0 0 0'); + self.angles = '0 0 0'; + self.frame = 0; + self.viewmodelforclient = world; + + float idx; + + if(v_shot_idx) // v_ model attached to invisible h_ model + { + self.movedir = gettaginfo(self.weaponentity, v_shot_idx); + } + else + { + idx = gettagindex(self, "shot"); + if(!idx) + idx = gettagindex(self, "tag_shot"); + if(idx) + self.movedir = gettaginfo(self, idx); + else + { + print("WARNING: weapon model ", self.model, " does not support the 'shot' tag, will display shots TOTALLY wrong\n"); + self.movedir = '0 0 0'; + } + } + + if(self.weaponentity) // v_ model attached to invisible h_ model + { + idx = gettagindex(self.weaponentity, "shell"); + if(!idx) + idx = gettagindex(self.weaponentity, "tag_shell"); + if(idx) + self.spawnorigin = gettaginfo(self.weaponentity, idx); + } + else + idx = 0; + if(!idx) + { + idx = gettagindex(self, "shell"); + if(!idx) + idx = gettagindex(self, "tag_shell"); + if(idx) + self.spawnorigin = gettaginfo(self, idx); + else + { + print("WARNING: weapon model ", self.model, " does not support the 'shell' tag, will display casings wrong\n"); + self.spawnorigin = self.movedir; + } + } + + if(v_shot_idx) + { + self.oldorigin = '0 0 0'; // use regular attachment + } + else + { + if(self.weaponentity) + { + idx = gettagindex(self, "weapon"); + if(!idx) + idx = gettagindex(self, "tag_weapon"); + } + else + { + idx = gettagindex(self, "handle"); + if(!idx) + idx = gettagindex(self, "tag_handle"); + } + if(idx) + { + self.oldorigin = self.movedir - gettaginfo(self, idx); + } + else + { + print("WARNING: weapon model ", self.model, " does not support the 'handle' tag and neither does the v_ model support the 'shot' tag, will display muzzle flashes TOTALLY wrong\n"); + self.oldorigin = '0 0 0'; // there is no way to recover from this + } + } + + self.viewmodelforclient = self.owner; + } + else + { + self.model = ""; + if(self.weaponentity) + remove(self.weaponentity); + self.weaponentity = world; + self.movedir = '0 0 0'; + self.spawnorigin = '0 0 0'; + self.oldorigin = '0 0 0'; + self.anim_fire1 = '0 1 0.01'; + self.anim_fire2 = '0 1 0.01'; + self.anim_idle = '0 1 0.01'; + self.anim_reload = '0 1 0.01'; + } + + self.view_ofs = '0 0 0'; + + if(self.movedir_x >= 0) + { + vector v0; + v0 = self.movedir; + self.movedir = shotorg_adjust(v0, FALSE, FALSE); + self.view_ofs = shotorg_adjust(v0, FALSE, TRUE) - v0; + } + self.owner.stat_shotorg = compressShotOrigin(self.movedir); + self.movedir = decompressShotOrigin(self.owner.stat_shotorg); // make them match perfectly + + self.spawnorigin += self.view_ofs; // offset the casings origin by the same amount + + // check if an instant weapon switch occurred + setorigin(self, self.view_ofs); + // reset animstate now + self.wframe = WFRAME_IDLE; + setanim(self, self.anim_idle, TRUE, FALSE, TRUE); +} + +vector CL_Weapon_GetShotOrg(float wpn) +{ + entity wi, oldself; + vector ret; + wi = get_weaponinfo(wpn); + oldself = self; + self = spawn(); + CL_WeaponEntity_SetModel(wi.mdl); + ret = self.movedir; + CL_WeaponEntity_SetModel(""); + remove(self); + self = oldself; + return ret; +} + +void CL_Weaponentity_Think() +{ + float tb; + self.nextthink = time; + if (intermission_running) + self.frame = self.anim_idle_x; + if (self.owner.weaponentity != self) + { + if (self.weaponentity) + remove(self.weaponentity); + remove(self); + return; + } + if (self.owner.deadflag != DEAD_NO) + { + self.model = ""; + if (self.weaponentity) + self.weaponentity.model = ""; + return; + } + if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag) + { + self.weaponname = self.owner.weaponname; + self.dmg = self.owner.modelindex; + self.deadflag = self.owner.deadflag; + + CL_WeaponEntity_SetModel(self.owner.weaponname); + } + + tb = (self.effects & (EF_TELEPORT_BIT | EF_RESTARTANIM_BIT)); + self.effects = self.owner.effects & EFMASK_CHEAP; + self.effects &= ~EF_LOWPRECISION; + self.effects &= ~EF_FULLBRIGHT; // can mask team color, so get rid of it + self.effects &= ~EF_TELEPORT_BIT; + self.effects &= ~EF_RESTARTANIM_BIT; + self.effects |= tb; + + if(self.owner.alpha == default_player_alpha) + self.alpha = default_weapon_alpha; + else if(self.owner.alpha != 0) + self.alpha = self.owner.alpha; + else + self.alpha = 1; + + self.glowmod = self.owner.weaponentity_glowmod; + self.colormap = self.owner.colormap; + if (self.weaponentity) + { + self.weaponentity.effects = self.effects; + self.weaponentity.alpha = self.alpha; + self.weaponentity.colormap = self.colormap; + self.weaponentity.glowmod = self.glowmod; + } + + self.angles = '0 0 0'; + + float f = (self.owner.weapon_nextthink - time); + if (self.state == WS_RAISE && !intermission_running) + { + entity newwep = get_weaponinfo(self.owner.switchweapon); + f = f * g_weaponratefactor / max(f, newwep.switchdelay_raise); + self.angles_x = -90 * f * f; + } + else if (self.state == WS_DROP && !intermission_running) + { + entity oldwep = get_weaponinfo(self.owner.weapon); + f = 1 - f * g_weaponratefactor / max(f, oldwep.switchdelay_drop); + self.angles_x = -90 * f * f; + } + else if (self.state == WS_CLEAR) + { + f = 1; + self.angles_x = -90 * f * f; + } +} + +void CL_ExteriorWeaponentity_Think() +{ + float tag_found; + self.nextthink = time; + if (self.owner.exteriorweaponentity != self) + { + remove(self); + return; + } + if (self.owner.deadflag != DEAD_NO) + { + self.model = ""; + return; + } + if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag) + { + self.weaponname = self.owner.weaponname; + self.dmg = self.owner.modelindex; + self.deadflag = self.owner.deadflag; + if (self.owner.weaponname != "") + setmodel(self, strcat("models/weapons/v_", self.owner.weaponname, ".md3")); // precision set below + else + self.model = ""; + + if((tag_found = gettagindex(self.owner, "tag_weapon"))) + { + self.tag_index = tag_found; + self.tag_entity = self.owner; + } + else + setattachment(self, self.owner, "bip01 r hand"); + } + self.effects = self.owner.effects; + self.effects |= EF_LOWPRECISION; + self.effects = self.effects & EFMASK_CHEAP; // eat performance + if(self.owner.alpha == default_player_alpha) + self.alpha = default_weapon_alpha; + else if(self.owner.alpha != 0) + self.alpha = self.owner.alpha; + else + self.alpha = 1; + + self.glowmod = self.owner.weaponentity_glowmod; + self.colormap = self.owner.colormap; + + CSQCMODEL_AUTOUPDATE(); +} + +// spawning weaponentity for client +void CL_SpawnWeaponentity() +{ + self.weaponentity = spawn(); + self.weaponentity.classname = "weaponentity"; + self.weaponentity.solid = SOLID_NOT; + self.weaponentity.owner = self; + setmodel(self.weaponentity, ""); // precision set when changed + setorigin(self.weaponentity, '0 0 0'); + self.weaponentity.angles = '0 0 0'; + self.weaponentity.viewmodelforclient = self; + self.weaponentity.flags = 0; + self.weaponentity.think = CL_Weaponentity_Think; + self.weaponentity.customizeentityforclient = CL_Weaponentity_CustomizeEntityForClient; + self.weaponentity.nextthink = time; + + self.exteriorweaponentity = spawn(); + self.exteriorweaponentity.classname = "exteriorweaponentity"; + self.exteriorweaponentity.solid = SOLID_NOT; + self.exteriorweaponentity.exteriorweaponentity = self.exteriorweaponentity; + self.exteriorweaponentity.owner = self; + setorigin(self.exteriorweaponentity, '0 0 0'); + self.exteriorweaponentity.angles = '0 0 0'; + self.exteriorweaponentity.think = CL_ExteriorWeaponentity_Think; + self.exteriorweaponentity.nextthink = time; + + { + entity oldself = self; + self = self.exteriorweaponentity; + CSQCMODEL_AUTOINIT(); + self = oldself; + } +} + +// Weapon subs +void w_clear() +{ + if (self.weapon != -1) + { + self.weapon = 0; + self.switchingweapon = 0; + } + if (self.weaponentity) + { + self.weaponentity.state = WS_CLEAR; + self.weaponentity.effects = 0; + } +} + +void w_ready() +{ + if (self.weaponentity) + self.weaponentity.state = WS_READY; + weapon_thinkf(WFRAME_IDLE, 1000000, w_ready); +} + +.float prevdryfire; +.float prevwarntime; +float weapon_prepareattack_checkammo(float secondary) +{ + if (!(self.items & IT_UNLIMITED_WEAPON_AMMO)) + if (!WEP_ACTION(self.weapon, WR_CHECKAMMO1 + secondary)) + { + // always keep the Mine Layer if we placed mines, so that we can detonate them + entity mine; + if(self.weapon == WEP_MINE_LAYER) + for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self) + return FALSE; + + if(self.weapon == WEP_SHOTGUN) + if(!secondary && WEP_CVAR(shotgun, secondary) == 1) + return FALSE; // no clicking, just allow + + if(self.weapon == self.switchweapon && time - self.prevdryfire > 1) // only play once BEFORE starting to switch weapons + { + sound (self, CH_WEAPON_A, "weapons/dryfire.wav", VOL_BASE, ATTEN_NORM); + self.prevdryfire = time; + } + + if(WEP_ACTION(self.weapon, WR_CHECKAMMO2 - secondary)) // check if the other firing mode has enough ammo + { + if(time - self.prevwarntime > 1) + { + Send_Notification( + NOTIF_ONE, + self, + MSG_MULTI, + ITEM_WEAPON_PRIMORSEC, + self.weapon, + secondary, + (1 - secondary) + ); + } + self.prevwarntime = time; + } + else // this weapon is totally unable to fire, switch to another one + { + W_SwitchToOtherWeapon(self); + } + + return FALSE; + } + return TRUE; +} +.float race_penalty; +float weapon_prepareattack_check(float secondary, float attacktime) +{ + if(!weapon_prepareattack_checkammo(secondary)) + return FALSE; + + //if sv_ready_restart_after_countdown is set, don't allow the player to shoot + //if all players readied up and the countdown is running + if(time < game_starttime || time < self.race_penalty) { + return FALSE; + } + + if (timeout_status == TIMEOUT_ACTIVE) //don't allow the player to shoot while game is paused + return FALSE; + + // do not even think about shooting if switching + if(self.switchweapon != self.weapon) + return FALSE; + + if(attacktime >= 0) + { + // don't fire if previous attack is not finished + if (ATTACK_FINISHED(self) > time + self.weapon_frametime * 0.5) + return FALSE; + // don't fire while changing weapon + if (self.weaponentity.state != WS_READY) + return FALSE; + } + + return TRUE; +} +float weapon_prepareattack_do(float secondary, float attacktime) +{ + self.weaponentity.state = WS_INUSE; + + self.spawnshieldtime = min(self.spawnshieldtime, time); // kill spawn shield when you fire + + // if the weapon hasn't been firing continuously, reset the timer + if(attacktime >= 0) + { + if (ATTACK_FINISHED(self) < time - self.weapon_frametime * 1.5) + { + ATTACK_FINISHED(self) = time; + //dprint("resetting attack finished to ", ftos(time), "\n"); + } + ATTACK_FINISHED(self) = ATTACK_FINISHED(self) + attacktime * W_WeaponRateFactor(); + } + self.bulletcounter += 1; + //dprint("attack finished ", ftos(ATTACK_FINISHED(self)), "\n"); + return TRUE; +} +float weapon_prepareattack(float secondary, float attacktime) +{ + if(weapon_prepareattack_check(secondary, attacktime)) + { + weapon_prepareattack_do(secondary, attacktime); + return TRUE; + } + else + return FALSE; +} + +void weapon_thinkf(float fr, float t, void() func) +{ + vector a; + vector of, or, ou; + float restartanim; + + if(fr == WFRAME_DONTCHANGE) + { + fr = self.weaponentity.wframe; + restartanim = FALSE; + } + else if (fr == WFRAME_IDLE) + restartanim = FALSE; + else + restartanim = TRUE; + + of = v_forward; + or = v_right; + ou = v_up; + + if (self.weaponentity) + { + self.weaponentity.wframe = fr; + a = '0 0 0'; + if (fr == WFRAME_IDLE) + a = self.weaponentity.anim_idle; + else if (fr == WFRAME_FIRE1) + a = self.weaponentity.anim_fire1; + else if (fr == WFRAME_FIRE2) + a = self.weaponentity.anim_fire2; + else // if (fr == WFRAME_RELOAD) + a = self.weaponentity.anim_reload; + a_z *= g_weaponratefactor; + setanim(self.weaponentity, a, restartanim == FALSE, restartanim, restartanim); + } + + v_forward = of; + v_right = or; + v_up = ou; + + if(self.weapon_think == w_ready && func != w_ready && self.weaponentity.state == WS_RAISE) + { + backtrace("Tried to override initial weapon think function - should this really happen?"); + } + + t *= W_WeaponRateFactor(); + + // VorteX: haste can be added here + if (self.weapon_think == w_ready) + { + self.weapon_nextthink = time; + //dprint("started firing at ", ftos(time), "\n"); + } + if (self.weapon_nextthink < time - self.weapon_frametime * 1.5 || self.weapon_nextthink > time + self.weapon_frametime * 1.5) + { + self.weapon_nextthink = time; + //dprint("reset weapon animation timer at ", ftos(time), "\n"); + } + self.weapon_nextthink = self.weapon_nextthink + t; + self.weapon_think = func; + //dprint("next ", ftos(self.weapon_nextthink), "\n"); + + if((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t) + { + if((self.weapon == WEP_SHOCKWAVE || self.weapon == WEP_SHOTGUN) && fr == WFRAME_FIRE2) + animdecide_setaction(self, ANIMACTION_MELEE, restartanim); + else + animdecide_setaction(self, ANIMACTION_SHOOT, restartanim); + } + else + { + if(self.anim_upper_action == ANIMACTION_SHOOT || self.anim_upper_action == ANIMACTION_MELEE) + self.anim_upper_action = 0; + } +} + +float forbidWeaponUse() +{ + if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown) + return 1; + if(round_handler_IsActive() && !round_handler_IsRoundStarted()) + return 1; + if(self.player_blocked) + return 1; + if(self.frozen) + return 1; + return 0; +} + +void W_WeaponFrame() +{ + vector fo, ri, up; + + if (frametime) + self.weapon_frametime = frametime; + + if (!self.weaponentity || self.health < 1) + return; // Dead player can't use weapons and injure impulse commands + + if(forbidWeaponUse()) + if(self.weaponentity.state != WS_CLEAR) + { + w_ready(); + return; + } + + if(!self.switchweapon) + { + self.weapon = 0; + self.switchingweapon = 0; + self.weaponentity.state = WS_CLEAR; + self.weaponname = ""; + //self.items &= ~IT_AMMO; + return; + } + + makevectors(self.v_angle); + fo = v_forward; // save them in case the weapon think functions change it + ri = v_right; + up = v_up; + + // Change weapon + if (self.weapon != self.switchweapon) + { + if (self.weaponentity.state == WS_CLEAR) + { + // end switching! + self.switchingweapon = self.switchweapon; + entity newwep = get_weaponinfo(self.switchweapon); + + // the two weapon entities will notice this has changed and update their models + self.weapon = self.switchweapon; + self.weaponname = newwep.mdl; + self.bulletcounter = 0; + //self.ammo_field = newwep.ammo_field; + WEP_ACTION(self.switchweapon, WR_SETUP); + self.weaponentity.state = WS_RAISE; + + // set our clip load to the load of the weapon we switched to, if it's reloadable + if(newwep.spawnflags & WEP_FLAG_RELOADABLE && newwep.reloading_ammo) // prevent accessing undefined cvars + { + self.clip_load = self.(weapon_load[self.switchweapon]); + self.clip_size = newwep.reloading_ammo; + } + else + self.clip_load = self.clip_size = 0; + + weapon_thinkf(WFRAME_IDLE, newwep.switchdelay_raise, w_ready); + } + else if (self.weaponentity.state == WS_DROP) + { + // in dropping phase we can switch at any time + self.switchingweapon = self.switchweapon; + } + else if (self.weaponentity.state == WS_READY) + { + // start switching! + self.switchingweapon = self.switchweapon; + entity oldwep = get_weaponinfo(self.weapon); + + // set up weapon switch think in the future, and start drop anim + #ifndef INDEPENDENT_ATTACK_FINISHED + if(ATTACK_FINISHED(self) <= time + self.weapon_frametime * 0.5) + { + #endif + sound(self, CH_WEAPON_SINGLE, "weapons/weapon_switch.wav", VOL_BASE, ATTN_NORM); + self.weaponentity.state = WS_DROP; + weapon_thinkf(WFRAME_DONTCHANGE, oldwep.switchdelay_drop, w_clear); + #ifndef INDEPENDENT_ATTACK_FINISHED + } + #endif + } + } + + // LordHavoc: network timing test code + //if (self.button0) + // print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(self)), " >= ", ftos(self.weapon_nextthink), "\n"); + + float w; + w = self.weapon; + + // call the think code which may fire the weapon + // and do so multiple times to resolve framerate dependency issues if the + // server framerate is very low and the weapon fire rate very high + float c; + c = 0; + while (c < W_TICSPERFRAME) + { + c = c + 1; + if(w && !(self.weapons & WepSet_FromWeapon(w))) + { + if(self.weapon == self.switchweapon) + W_SwitchWeapon_Force(self, w_getbestweapon(self)); + w = 0; + } + + v_forward = fo; + v_right = ri; + v_up = up; + + if(w) + WEP_ACTION(self.weapon, WR_THINK); + else + WEP_ACTION(self.weapon, WR_GONETHINK); + + if (time + self.weapon_frametime * 0.5 >= self.weapon_nextthink) + { + if(self.weapon_think) + { + v_forward = fo; + v_right = ri; + v_up = up; + self.weapon_think(); + } + else + bprint("\{1}^1ERROR: undefined weapon think function for ", self.netname, "\n"); + } + } +} + +void W_AttachToShotorg(entity flash, vector offset) +{ + entity xflash; + flash.owner = self; + flash.angles_z = random() * 360; + + if(gettagindex(self.weaponentity, "shot")) + setattachment(flash, self.weaponentity, "shot"); + else + setattachment(flash, self.weaponentity, "tag_shot"); + setorigin(flash, offset); + + xflash = spawn(); + copyentity(flash, xflash); + + flash.viewmodelforclient = self; + + if(self.weaponentity.oldorigin_x > 0) + { + setattachment(xflash, self.exteriorweaponentity, ""); + setorigin(xflash, self.weaponentity.oldorigin + offset); + } + else + { + if(gettagindex(self.exteriorweaponentity, "shot")) + setattachment(xflash, self.exteriorweaponentity, "shot"); + else + setattachment(xflash, self.exteriorweaponentity, "tag_shot"); + setorigin(xflash, offset); + } +} + +void W_DecreaseAmmo(float ammo_use) +{ + entity wep = get_weaponinfo(self.weapon); + + if((self.items & IT_UNLIMITED_WEAPON_AMMO) && !wep.reloading_ammo) + return; + + // if this weapon is reloadable, decrease its load. Else decrease the player's ammo + if(wep.reloading_ammo) + { + self.clip_load -= ammo_use; + self.(weapon_load[self.weapon]) = self.clip_load; + } + else if(wep.ammo_field != ammo_none) + { + self.(wep.ammo_field) -= ammo_use; + if(self.(wep.ammo_field) < 0) + { + backtrace(sprintf( + "W_DecreaseAmmo(%.2f): '%s' subtracted too much %s from '%s', resulting with '%.2f' left... " + "Please notify Samual immediately with a copy of this backtrace!\n", + ammo_use, + wep.netname, + GetAmmoPicture(wep.ammo_field), + self.netname, + self.(wep.ammo_field) + )); + } + } +} + +// weapon reloading code + +.float reload_ammo_amount, reload_ammo_min, reload_time; +.float reload_complain; +.string reload_sound; + +void W_ReloadedAndReady() +{ + // finish the reloading process, and do the ammo transfer + + self.clip_load = self.old_clip_load; // restore the ammo counter, in case we still had ammo in the weapon before reloading + + // if the gun uses no ammo, max out weapon load, else decrease ammo as we increase weapon load + if(!self.reload_ammo_min || self.items & IT_UNLIMITED_WEAPON_AMMO || self.ammo_field == ammo_none) + self.clip_load = self.reload_ammo_amount; + else + { + while(self.clip_load < self.reload_ammo_amount && self.(self.ammo_field)) // make sure we don't add more ammo than we have + { + self.clip_load += 1; + self.(self.ammo_field) -= 1; + } + } + self.(weapon_load[self.weapon]) = self.clip_load; + + // do not set ATTACK_FINISHED in reload code any more. This causes annoying delays if eg: You start reloading a weapon, + // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there, + // so your weapon is disabled for a few seconds without reason + + //ATTACK_FINISHED(self) -= self.reload_time - 1; + + w_ready(); +} + +void W_Reload(float sent_ammo_min, string sent_sound) +{ + // set global values to work with + entity e; + e = get_weaponinfo(self.weapon); + + self.reload_ammo_min = sent_ammo_min; + self.reload_ammo_amount = e.reloading_ammo;; + self.reload_time = e.reloading_time; + self.reload_sound = sent_sound; + + // don't reload weapons that don't have the RELOADABLE flag + if (!(e.spawnflags & WEP_FLAG_RELOADABLE)) + { + dprint("Warning: Attempted to reload a weapon that does not have the WEP_FLAG_RELOADABLE flag. Fix your code!\n"); + return; + } + + // return if reloading is disabled for this weapon + if(!self.reload_ammo_amount) + return; + + // our weapon is fully loaded, no need to reload + if (self.clip_load >= self.reload_ammo_amount) + return; + + // no ammo, so nothing to load + if(self.ammo_field != ammo_none) + if(!self.(self.ammo_field) && self.reload_ammo_min) + if (!(self.items & IT_UNLIMITED_WEAPON_AMMO)) + { + if(IS_REAL_CLIENT(self) && self.reload_complain < time) + { + play2(self, "weapons/unavailable.wav"); + sprint(self, strcat("You don't have enough ammo to reload the ^2", WEP_NAME(self.weapon), "\n")); + self.reload_complain = time + 1; + } + // switch away if the amount of ammo is not enough to keep using this weapon + if (!(WEP_ACTION(self.weapon, WR_CHECKAMMO1) + WEP_ACTION(self.weapon, WR_CHECKAMMO2))) + { + self.clip_load = -1; // reload later + W_SwitchToOtherWeapon(self); + } + return; + } + + if (self.weaponentity) + { + if (self.weaponentity.wframe == WFRAME_RELOAD) + return; + + // allow switching away while reloading, but this will cause a new reload! + self.weaponentity.state = WS_READY; + } + + // now begin the reloading process + + sound(self, CH_WEAPON_SINGLE, self.reload_sound, VOL_BASE, ATTEN_NORM); + + // do not set ATTACK_FINISHED in reload code any more. This causes annoying delays if eg: You start reloading a weapon, + // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there, + // so your weapon is disabled for a few seconds without reason + + //ATTACK_FINISHED(self) = max(time, ATTACK_FINISHED(self)) + self.reload_time + 1; + + weapon_thinkf(WFRAME_RELOAD, self.reload_time, W_ReloadedAndReady); + + if(self.clip_load < 0) + self.clip_load = 0; + self.old_clip_load = self.clip_load; + self.clip_load = self.(weapon_load[self.weapon]) = -1; +} + +entity weapon_dropevent_item; +void W_DropEvent(float event, entity player, float weapon_type, entity weapon_item) +{ + entity oldself = self; + self = player; + weapon_dropevent_item = weapon_item; + WEP_ACTION(weapon_type, event); + self = oldself; +} diff --git a/qcsrc/server/weapons/weaponsystem.qh b/qcsrc/server/weapons/weaponsystem.qh new file mode 100644 index 0000000000..ddb1ee6958 --- /dev/null +++ b/qcsrc/server/weapons/weaponsystem.qh @@ -0,0 +1,6 @@ +float weaponswapping; +float internalteam; + +void W_DropEvent(float event, entity player, float weapon_type, entity weapon_item); + +float forbidWeaponUse(); diff --git a/qcsrc/warpzonelib/TODO b/qcsrc/warpzonelib/TODO index 5c9a55fc1a..927ab1298c 100644 --- a/qcsrc/warpzonelib/TODO +++ b/qcsrc/warpzonelib/TODO @@ -1,23 +1,24 @@ Open issues: - grep for TODO and FIXME -- when shot origin is inside warpzone, nex shot fails (and is even drawn in totally wrong direction). WHY? Possibly v_forward got lost? +- when shot origin is inside warpzone, vortex shot fails (and is even drawn in totally wrong direction). WHY? Possibly v_forward got lost? Weapon support: -- laser: YES +- blaster: YES - shotgun: YES -- uzi: YES -- grenadelauncher: YES +- machinegun: YES +- mortar: YES - electro: YES - crylink: YES -- nex: YES +- vortex: YES - hagar: YES -- rocketlauncher: YES (except for trail bug) +- devastator: YES (except for trail bug) - porto: YES (bwahahahaha) - hlac: YES -- minstanex: YES +- vaporizer: YES - rifle: YES - fireball: YES (BFG effect cannot work through warpzones by design, so it's not available through warpzones) - hook: YES +- shockwave: NO (does not support warpzones currently) - tuba: NO (sound) diff --git a/qcsrc/warpzonelib/client.qc b/qcsrc/warpzonelib/client.qc index f55cec1d52..c2a110b4a1 100644 --- a/qcsrc/warpzonelib/client.qc +++ b/qcsrc/warpzonelib/client.qc @@ -239,8 +239,8 @@ void WarpZone_FixView() vector org, ang, nearclip, corner0, corner1, corner2, corner3, o; float f; - org = getpropertyvec(VF_ORIGIN); - ang = getpropertyvec(VF_ANGLES); + warpzone_save_view_origin = org = getpropertyvec(VF_ORIGIN); + warpzone_save_view_angles = ang = getpropertyvec(VF_ANGLES); #ifdef WORKAROUND_XON010 float dirty; dirty = checkextension("DP_CSQC_ROTATEMOVES"); diff --git a/qcsrc/warpzonelib/client.qh b/qcsrc/warpzonelib/client.qh index 446c917dbf..c0a4ca0f0a 100644 --- a/qcsrc/warpzonelib/client.qh +++ b/qcsrc/warpzonelib/client.qh @@ -7,3 +7,6 @@ void WarpZone_FixView(); void WarpZone_Init(); void WarpZone_Shutdown(); + +vector warpzone_save_view_origin; +vector warpzone_save_view_angles; diff --git a/scripts/weapons.shader b/scripts/weapons.shader index a6dcfaab86..c5c673fbff 100644 --- a/scripts/weapons.shader +++ b/scripts/weapons.shader @@ -103,3 +103,11 @@ minelayer rgbgen lightingDiffuse } } +shotgun +{ + dpreflectcube cubemaps/default/sky + { + map textures/arc.tga + rgbgen lightingDiffuse + } +} diff --git a/textures/arc.tga b/textures/arc.tga new file mode 100644 index 0000000000..55d2f34b37 Binary files /dev/null and b/textures/arc.tga differ diff --git a/textures/arc_gloss.tga b/textures/arc_gloss.tga new file mode 100644 index 0000000000..45734a7ef7 Binary files /dev/null and b/textures/arc_gloss.tga differ diff --git a/textures/arc_glow.tga b/textures/arc_glow.tga new file mode 100644 index 0000000000..2b3575116a Binary files /dev/null and b/textures/arc_glow.tga differ diff --git a/textures/arc_norm.tga b/textures/arc_norm.tga new file mode 100644 index 0000000000..39e183148f Binary files /dev/null and b/textures/arc_norm.tga differ diff --git a/textures/arc_reflect.tga b/textures/arc_reflect.tga new file mode 100644 index 0000000000..b60f891b0b Binary files /dev/null and b/textures/arc_reflect.tga differ diff --git a/textures/arc_shirt.tga b/textures/arc_shirt.tga new file mode 100644 index 0000000000..90107daa13 Binary files /dev/null and b/textures/arc_shirt.tga differ diff --git a/tooltips.db.de b/tooltips.db.de index 7ba71e2745..f009272c9f 100644 --- a/tooltips.db.de +++ b/tooltips.db.de @@ -41,7 +41,6 @@ \g_rocket_flying\Raketen fliegen in allen Physikeinstellungen \g_weapon_stay\Alle Waffen bleiben liegen, auch wenn sie aufgenommen wurden \g_weaponarena\Waffen-Arenen: Die Auswahl einer Waffen-Arena führt dazu, dass jeder Spieler mit der gewählten Waffe startet. Diese hat unendlich viel Munition, andere Waffen sind nicht vorhanden - Spezielle Waffen-Arenen: Spieler starten mit allen Waffen und unendlich viel Munition -\menu_weaponarena_with_laser\Aktiviere auch den Laser in der Waffen-Arena \g_instagib\Alle Spieler starten mit der MinstaNex, eine elektromagnetische Schienenkanone mit unendlich viel Schaden. Wenn ein Spieler keine Munition mehr hat, bleiben ihm 10 Sekunden um neue zu finden, ansonsten stirbt er. Der 2. Feuermodus ist Laser, welcher keinen Schaden hinzufügen kann. Dieser eignet sich gut für Tricksprünge \g_nix\Es gibt keine aufzusammelnden Gegenstände in Xonotic - Anstelle der Möglichkeit Waffen aufzusammeln, spielen alle mit der gleichen Waffe. Nach einiger Zeit startet ein Countdown, danach wechseln alle Spieler zu einer neuen gleichen Waffe \g_nix_with_laser\In Nix ist als zweite Waffe der Laser vorhanden diff --git a/tooltips.db.es b/tooltips.db.es index 4cab5a6383..b54d36b1a8 100644 --- a/tooltips.db.es +++ b/tooltips.db.es @@ -40,7 +40,6 @@ \g_pinata\Los jugadores dejan todas las armas cuando mueren \g_weapon_stay\Las armas quedan despues de que son tomadas \g_weaponarena\Seleccionando un arma, dara a todos los jugadores cual arma se eligió asi como infinita munición, y deshabilita cualquier otra toma de arma. -\menu_weaponarena_with_laser\Tambien habilita el láser en la arena \g_instagib\Los jugadores tendran Minstanex, el cual es un railgun con daño infinito. Si el jugador queda sin munición, tendra 10 segundos para buscar mas o morira. El modo de disparo secundario es un laser que no inflige daño y es bueno para hacer bromas. \g_nix\Xonotic sin items - en vez de recoger items, todos juegan con la misma arma. Despues de algún tiempo, comienza una cuenta regresiva, despues del cual todos juegan con otra arma. \g_nix_with_laser\Siempre lleva el láser como arma adicional en Nix diff --git a/tooltips.db.hu b/tooltips.db.hu index 4080f0286c..4211aa0c3a 100644 --- a/tooltips.db.hu +++ b/tooltips.db.hu @@ -41,7 +41,6 @@ \g_pinata\A játékosok eldobnak minden fegyvert, amit birtokoltak a haláluk előtt \g_weapon_stay\A fegyverek a helyükön maradnak, még azután is, hogy valaki felvette őket \g_weaponarena\A kiválasztott fegyver aréna minden játékosnak ugyanazt a fegyvert biztosítja korlátlan lőszerrel, és letiltja minden más fegyver felvételét -\menu_weaponarena_with_laser\A lézer is engedélyezett a fegyver arénában \g_instagib\A játékosok egy Minstanex-et kapnak, ami egy azonnal ölő mesterlövész fegyver. Ha a játékos kifogy a lőszerből, 10 másodperce van muníciót találni, vagy meghal. A másodlagos tűz mód a lézer, amely nem okoz kárt, de jól jön trükkös ugrások végrehajtásánál \g_nix\Xonotic felvehető fegyverek nélkül – Mindenki ugyanazzal a fegyverrel játszik. Kis idő után visszaszámlálás indul, amely végén mindenki fegyvert vált \g_nix_with_laser\Mindig legyen a lézer a Nix mellett kiegészítésül diff --git a/tooltips.db.ru b/tooltips.db.ru index c5b5fccf59..3bf01a8c54 100644 --- a/tooltips.db.ru +++ b/tooltips.db.ru @@ -40,7 +40,6 @@ \g_pinata\Во время смерти выбрасывается всё оружие, которое нёс "убитый", что даёт возможность его подобрать \g_weapon_stay\Всё собранное оружие остаётся после возрождений \g_weaponarena\Selecting a weapon arena will give all players that weapon at spawn as well as unlimited ammo, and disable all other weapon pickups. -\menu_weaponarena_with_laser\Also enable the laser in the weapon arena \g_instagib\Players will be given the Minstanex, which is a railgun with infinite damage. If the player runs out of ammo, he will have 10 seconds to find some or if he fails to do so, face death. The secondary fire mode is a laser which does not inflict any damage and is good for doing trickjumps. \g_nix\No items Xonotic - instead of pickup items, everyone plays with the same weapon. After some time, a countdown will start, after which everyone will switch to another weapon. \g_nix_with_laser\Always carry the laser as an additional weapon in Nix diff --git a/tooltips.db.uk b/tooltips.db.uk index bece5dc112..59f4be66b8 100644 --- a/tooltips.db.uk +++ b/tooltips.db.uk @@ -40,7 +40,6 @@ \g_pinata\Після того, як гравця вб'ють, з нього випаде вся зброя яку він мав \g_weapon_stay\Зброя залишається після того, як була підібраною \g_weaponarena\Вибір арени з окремою зброєю дасть гравцям цю зброю і необмежену кількість боєприпасів до неї, і прибере з мапи всю іншу зброю -\menu_weaponarena_with_laser\Зробити лазер доступним на аренах \g_instagib\Гравці отримують МінстаНекс, рейкову гармату яка вбиває одним пострілом. Якщо гравець витратить усі боєприпаси, у нього буде десять секунд щоб поновити його, інакше він загине. Альтернативний вогонь гармати це лазер який не наносить шкоди, корисний для трюків \g_nix\Нікс (No items Xonotic) - замість того щоб підбирати предмети, всі гравці грають з однією зброєю. Через деякий час почнеться відлік, і зброя у всіх зміниться на іншу \g_nix_with_laser\Зробити лазер доступним у Нікс diff --git a/vehicles.cfg b/vehicles.cfg index d28aa4cad6..ea296146db 100644 --- a/vehicles.cfg +++ b/vehicles.cfg @@ -18,9 +18,9 @@ set cl_vehicles_hudscale 0.5 set g_vehicles_delayspawn 1 set g_vehicles_delayspawn_jitter 10 -set g_vehicles_nex_damagerate 0.5 -set g_vehicles_uzi_damagerate 0.65 +set g_vehicles_vortex_damagerate 0.5 +set g_vehicles_machinegun_damagerate 0.65 set g_vehicles_rifle_damagerate 1 -set g_vehicles_minstanex_damagerate 0.007 +set g_vehicles_vaporizer_damagerate 0.007 set g_vehicles_tag_damagerate 2 diff --git a/xonotic-credits.txt b/xonotic-credits.txt index 2fcdf98ce1..6a810c9ba9 100644 --- a/xonotic-credits.txt +++ b/xonotic-credits.txt @@ -1,22 +1,36 @@ **Core Team Rudolf "divVerent" Polzer Ant "Antibody" Zucaro +Marvin "Mirio" Beck Merlijn Hofstra Peter "Morphed" Pielak Samual "Ares" Lenks Tyler "-z-" Mulligan - +Zac "Mario" Jardine **Extended Team +Antonio "terencehill" Piu +Archer Debugger +GATTS +Halogene +IDWMaster Jan "zykure" Behrens +JH0nny Łukasz "kuniu the frogg" Polek -Mario -Marvin "Mirio" Beck Matthias "matthiaskrgr" Krüger MrBougo Nick "bitbomb" Lucca +nilyt/nyov +Nitroxis +packer +Pearce "theShadow" Michal Rasmus "FruitieX" Eskola +s1lence +Severin "sev" Meyer +Soelen +Sydes +unfa *Website @@ -50,7 +64,9 @@ Konrad "Justin" Slawinski Łukasz "kuniu the frogg" Polek Maik "SavageX" Merten Marvin "Mirio" Beck +MintOX Mircea "Taoki" Kitsune +packer Pearce "theShadow" Michal Rasmus "FruitieX" Eskola Ruszkai "C.Brutail" Ákos @@ -69,12 +85,14 @@ Saulo "mand1nga" Gil {SC0RP} - Ian "ID" Dorrell Stephan unfa +AquaNova (Archer) *Game Code Samual "Ares" Lenks Rudolf "divVerent" Polzer Jakob "tZork" Markström Gröhn Rasmus "FruitieX" Eskola +Zac "Mario" Jardine *Marketing / PR Tyler "-z-" Mulligan @@ -103,30 +121,38 @@ Dale "graphitemaster" Weiler **Other Active Contributors -Alexander "naryl" Suhoverhov -Antonio "terencehill" Piu Erik "Ablu" Schilling -Florian Paul "lda17h" Schmidt -Halogene Jope "Sless" Withers -Oleh "BlaXpirit" Prypin -Przemysław "atheros" Grzywacz -Robert "ai" Kuroto -The player with the unnecessarily long name Mattia "Melanosuchus" Basaglia +Robert "ai" Kuroto +TimePath **Translators +*Asturian +Llumex03 +Ximielga + +*Belarusian +Mihail "meequz" Varantsou + +*Bulgarian +lokster +set_killer + *Dutch Alexander "freefang" van Dam PinkRobot +vegiburger *German -Rudolf "divVerent" Polzer -Marvin "Mirio" Beck +cvcxc Erik "Ablu" Schilling Jope "Sless" Withers +Marvin "Mirio" Beck +Rudolf "divVerent" Polzer +Yepoleb *Finnish Henry "Exitium" Sanmark @@ -135,10 +161,13 @@ Rasmus "FruitieX" Eskola *French Calinou Maxime "Taximus" Paradis +RedGuff Yannick "SpiKe" Le Guen *Greek Γιάννης "Evropi" Α. +Savoritias +Vindex *Hungarian Ruszkai "C.Brutail" Ákos @@ -151,19 +180,34 @@ stdi *Portuguese Ricardo "Hellgardia" Silva +xXxCHAOTICxXx *Romanian +BusterDBK Mircea "Taoki" Kitsune *Russian +Alex "alextalker7" Talker +Andrei "adem4ik" Stepanov +gravicappa Hot Dog Lord Canistra Nikoli Sergej "Clearness High" Lutsyk +*Serbian +Саша "salepetronije" Петровић +Pendulla +Ristovski + *Spanish +0000simon +Ari_tent +brunodeleo Kammy +roader_gentto Rodrigo Mouton Laudin +SouL *Swedish Karl-Oskar "machine" RikÃ¥s @@ -172,10 +216,12 @@ marcus256 *Ukrainian Oleh "BlaXpirit" Prypin Vasyl "Harmata" Melnyk +Yuriy "herrniemand" Ackermann **Past Contributors Akari +Alexander "naryl" Suhoverhov Alexander "motorsep" Zubov Amos "torus" Dudley Andreas "Black" Kirsch @@ -196,6 +242,7 @@ Edgenetwork Edward "Ed" Holness Eric "Munyul Verminard" Sambach Fabien "H. Reaper" Tschirhart +Florian Paul "lda17h" Schmidt FrikaC Garth "Zombie" Hendy Gerd "Elysis" Raudenbusch @@ -232,8 +279,10 @@ Paul "Strahlemann" Evers Paul Scott Petithomme PlasmaSheep +Przemysław "atheros" Grzywacz Q1 Retexturing Project Qantourisc +Oleh "BlaXpirit" Prypin Rick "Rat" Kelley Ronan Sajt @@ -249,6 +298,7 @@ Stephan "esteel" Stahl Steve Vermeulen Supajoe Tei +The player with the unnecessarily long name Tomaz Ulrich Galbraith Vortex