Merge remote-tracking branch 'origin/master' into samual/spawn_weapons
authorSamual Lenks <samual@xonotic.org>
Mon, 10 Jun 2013 00:32:18 +0000 (20:32 -0400)
committerSamual Lenks <samual@xonotic.org>
Mon, 10 Jun 2013 00:32:18 +0000 (20:32 -0400)
Conflicts:
balance25.cfg
balanceFruitieX.cfg
balanceXPM.cfg
balanceXonotic.cfg
effectinfo.txt
qcsrc/common/constants.qh
qcsrc/common/util.qc
qcsrc/common/util.qh
qcsrc/server/cheats.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_weaponsystem.qc
qcsrc/server/g_damage.qc
qcsrc/server/tturrets/units/unit_walker.qc
qcsrc/server/vehicles/raptor.qc
qcsrc/server/vehicles/spiderbot.qc
qcsrc/server/vehicles/vehicles.qc
qcsrc/server/w_crylink.qc
qcsrc/server/w_electro.qc
qcsrc/server/w_hagar.qc
qcsrc/server/w_laser.qc
qcsrc/server/w_seeker.qc
qcsrc/server/w_shotgun.qc

46 files changed:
1  2 
balance25.cfg
balanceFruitieX.cfg
balanceXDF.cfg
balanceXPM.cfg
balanceXonotic.cfg
effectinfo.txt
qcsrc/client/Defs.qc
qcsrc/client/Main.qc
qcsrc/client/hook.qc
qcsrc/client/miscfunctions.qc
qcsrc/client/particles.qc
qcsrc/common/constants.qh
qcsrc/common/util.qc
qcsrc/common/util.qh
qcsrc/server/attic/monsters/monster_zombie.qc
qcsrc/server/autocvars.qh
qcsrc/server/cheats.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_weaponsystem.qc
qcsrc/server/func_breakable.qc
qcsrc/server/g_damage.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/mutator_touchexplode.qc
qcsrc/server/progs.src
qcsrc/server/tturrets/system/system_main.qc
qcsrc/server/tturrets/units/unit_walker.qc
qcsrc/server/vehicles/bumblebee.qc
qcsrc/server/vehicles/racer.qc
qcsrc/server/vehicles/raptor.qc
qcsrc/server/vehicles/spiderbot.qc
qcsrc/server/vehicles/vehicles.qc
qcsrc/server/w_common.qc
qcsrc/server/w_crylink.qc
qcsrc/server/w_electro.qc
qcsrc/server/w_fireball.qc
qcsrc/server/w_grenadelauncher.qc
qcsrc/server/w_hagar.qc
qcsrc/server/w_hlac.qc
qcsrc/server/w_hook.qc
qcsrc/server/w_laser.qc
qcsrc/server/w_minelayer.qc
qcsrc/server/w_minstanex.qc
qcsrc/server/w_rocketlauncher.qc
qcsrc/server/w_seeker.qc
qcsrc/server/w_tuba.qc

diff --cc balance25.cfg
@@@ -257,11 -241,8 +256,8 @@@ set g_balance_laser_primary_delay 0.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_reload_ammo 0 //default: 6
- set g_balance_laser_reload_time 2
 +set g_balance_laser_secondary 0 // 0 = switch away to last used weapon, 1 = projectile secondary, 2 = melee secondary
  set g_balance_laser_secondary_damage 35
  set g_balance_laser_secondary_edgedamage 10
  set g_balance_laser_secondary_force 400
@@@ -276,35 -257,11 +272,40 @@@ set g_balance_laser_secondary_delay 
  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_shockwave_damage 20
 +set g_balance_laser_shockwave_distance 2000
 +set g_balance_laser_shockwave_edgedamage 0
 +set g_balance_laser_shockwave_force 200
 +set g_balance_laser_shockwave_force_forwardbias 50
 +set g_balance_laser_shockwave_force_zscale 1.5
 +set g_balance_laser_shockwave_jump_damage 20
 +set g_balance_laser_shockwave_jump_edgedamage 0
 +set g_balance_laser_shockwave_jump_force 300
 +set g_balance_laser_shockwave_jump_force_velocitybias 0
 +set g_balance_laser_shockwave_jump_force_zscale 1.25
 +set g_balance_laser_shockwave_jump_multiplier_accuracy 0.5
 +set g_balance_laser_shockwave_jump_multiplier_distance 0.5
 +set g_balance_laser_shockwave_jump_multiplier_min 0
 +set g_balance_laser_shockwave_jump_radius 150
 +set g_balance_laser_shockwave_multiplier_accuracy 0.5
 +set g_balance_laser_shockwave_multiplier_distance 0.5
 +set g_balance_laser_shockwave_multiplier_min 0
 +set g_balance_laser_shockwave_splash_damage 15
 +set g_balance_laser_shockwave_splash_edgedamage 0
 +set g_balance_laser_shockwave_splash_force 100
 +set g_balance_laser_shockwave_splash_force_forwardbias 50
 +set g_balance_laser_shockwave_splash_multiplier_accuracy 0.5
 +set g_balance_laser_shockwave_splash_multiplier_distance 0.5
 +set g_balance_laser_shockwave_splash_multiplier_min 0
 +set g_balance_laser_shockwave_splash_radius 70
 +set g_balance_laser_shockwave_spread_max 100
 +set g_balance_laser_shockwave_spread_min 20
++
+ 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
@@@ -257,11 -241,8 +256,8 @@@ set g_balance_laser_primary_delay 
  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_reload_ammo 0 //default: 6
- set g_balance_laser_reload_time 2
 +set g_balance_laser_secondary 0 // 0 = switch away to last used weapon, 1 = projectile secondary, 2 = melee secondary
  set g_balance_laser_secondary_damage 200 // dps
  set g_balance_laser_secondary_edgedamage 0
  set g_balance_laser_secondary_force 1300
@@@ -276,35 -257,11 +272,40 @@@ set g_balance_laser_secondary_delay 
  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_shockwave_damage 20
 +set g_balance_laser_shockwave_distance 2000
 +set g_balance_laser_shockwave_edgedamage 0
 +set g_balance_laser_shockwave_force 200
 +set g_balance_laser_shockwave_force_forwardbias 50
 +set g_balance_laser_shockwave_force_zscale 1.5
 +set g_balance_laser_shockwave_jump_damage 20
 +set g_balance_laser_shockwave_jump_edgedamage 0
 +set g_balance_laser_shockwave_jump_force 300
 +set g_balance_laser_shockwave_jump_force_velocitybias 0
 +set g_balance_laser_shockwave_jump_force_zscale 1.25
 +set g_balance_laser_shockwave_jump_multiplier_accuracy 0.5
 +set g_balance_laser_shockwave_jump_multiplier_distance 0.5
 +set g_balance_laser_shockwave_jump_multiplier_min 0
 +set g_balance_laser_shockwave_jump_radius 150
 +set g_balance_laser_shockwave_multiplier_accuracy 0.5
 +set g_balance_laser_shockwave_multiplier_distance 0.5
 +set g_balance_laser_shockwave_multiplier_min 0
 +set g_balance_laser_shockwave_splash_damage 15
 +set g_balance_laser_shockwave_splash_edgedamage 0
 +set g_balance_laser_shockwave_splash_force 100
 +set g_balance_laser_shockwave_splash_force_forwardbias 50
 +set g_balance_laser_shockwave_splash_multiplier_accuracy 0.5
 +set g_balance_laser_shockwave_splash_multiplier_distance 0.5
 +set g_balance_laser_shockwave_splash_multiplier_min 0
 +set g_balance_laser_shockwave_splash_radius 70
 +set g_balance_laser_shockwave_spread_max 100
 +set g_balance_laser_shockwave_spread_min 20
++
+ 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
diff --cc balanceXDF.cfg
index 0000000,5745548..17b5e88
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,709 +1,753 @@@
 -set g_balance_laser_primary_gauntlet 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
+ // }}}
+ // {{{ weapon properties
+ // {{{ laser
++set g_balance_laser_melee_animtime 0.3
++set g_balance_laser_melee_damage 80
++set g_balance_laser_melee_delay 0.25
++set g_balance_laser_melee_force 200
++set g_balance_laser_melee_multihit 1
++set g_balance_laser_melee_no_doubleslap 1
++set g_balance_laser_melee_nonplayerdamage 40
++set g_balance_laser_melee_range 120
++set g_balance_laser_melee_refire 1.25
++set g_balance_laser_melee_swing_side 120
++set g_balance_laser_melee_swing_up 30
++set g_balance_laser_melee_time 0.15
++set g_balance_laser_melee_traces 10
++
++set g_balance_laser_primary 1 // 0 = shockwave attack, 1 = projectile primary
+ 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_secondary 0 // when 1, a secondary laser mode exists
+ 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_animtime 0.3
++
++set g_balance_laser_secondary 0 // 0 = switch away to last used weapon, 1 = projectile secondary, 2 = melee secondary
+ 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_gauntlet 0
++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_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_shockwave_damage 20
++set g_balance_laser_shockwave_distance 2000
++set g_balance_laser_shockwave_edgedamage 0
++set g_balance_laser_shockwave_force 200
++set g_balance_laser_shockwave_force_forwardbias 50
++set g_balance_laser_shockwave_force_zscale 2
++set g_balance_laser_shockwave_jump_damage 20
++set g_balance_laser_shockwave_jump_edgedamage 0
++set g_balance_laser_shockwave_jump_force 300
++set g_balance_laser_shockwave_jump_force_velocitybias 0
++set g_balance_laser_shockwave_jump_force_zscale 1.25
++set g_balance_laser_shockwave_jump_multiplier_accuracy 0.5
++set g_balance_laser_shockwave_jump_multiplier_distance 0.5
++set g_balance_laser_shockwave_jump_multiplier_min 0
++set g_balance_laser_shockwave_jump_radius 150
++set g_balance_laser_shockwave_multiplier_accuracy 0.5
++set g_balance_laser_shockwave_multiplier_distance 0.5
++set g_balance_laser_shockwave_multiplier_min 0
++set g_balance_laser_shockwave_splash_damage 15
++set g_balance_laser_shockwave_splash_edgedamage 0
++set g_balance_laser_shockwave_splash_force 100
++set g_balance_laser_shockwave_splash_force_forwardbias 50
++set g_balance_laser_shockwave_splash_multiplier_accuracy 0.5
++set g_balance_laser_shockwave_splash_multiplier_distance 0.5
++set g_balance_laser_shockwave_splash_multiplier_min 0
++set g_balance_laser_shockwave_splash_radius 70
++set g_balance_laser_shockwave_spread_max 120
++set g_balance_laser_shockwave_spread_min 25
++
+ 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_speed 8000
+ set g_balance_shotgun_primary_bulletconstant 75 // 3.8qu
+ 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_speed 18000
+ set g_balance_uzi_bulletconstant 115 // 13.1qu
+ 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_jointime 0
+ 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_jointime 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 // 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 --cc balanceXPM.cfg
@@@ -228,21 -227,6 +227,21 @@@ set g_balance_grapplehook_damagedbycont
  
  // {{{ weapon properties
  // {{{ laser
- set g_balance_laser_primary 1 // 0 = shockwave attack, 1 = projectile primary
 +set g_balance_laser_melee_animtime 0.3
 +set g_balance_laser_melee_damage 80
 +set g_balance_laser_melee_delay 0.25
 +set g_balance_laser_melee_force 200
 +set g_balance_laser_melee_multihit 1
 +set g_balance_laser_melee_no_doubleslap 1
 +set g_balance_laser_melee_nonplayerdamage 40
 +set g_balance_laser_melee_range 120
 +set g_balance_laser_melee_refire 1.25
 +set g_balance_laser_melee_swing_side 120
 +set g_balance_laser_melee_swing_up 30
 +set g_balance_laser_melee_time 0.15
 +set g_balance_laser_melee_traces 10
 +
++set g_balance_laser_primary 0 // 0 = shockwave attack, 1 = projectile primary
  set g_balance_laser_primary_damage 25
  set g_balance_laser_primary_edgedamage 12.5
  set g_balance_laser_primary_force 300
@@@ -257,11 -241,8 +256,8 @@@ set g_balance_laser_primary_delay 
  set g_balance_laser_primary_force_zscale 1.25
  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_reload_ammo 0 //default: 6
- set g_balance_laser_reload_time 2
- set g_balance_laser_secondary 0 // 0 = switch away to last used weapon, 1 = projectile secondary, 2 = melee secondary
++set g_balance_laser_secondary 2 // 0 = switch away to last used weapon, 1 = projectile secondary, 2 = melee secondary
  set g_balance_laser_secondary_damage 25
  set g_balance_laser_secondary_edgedamage 12.5
  set g_balance_laser_secondary_force 400
@@@ -276,35 -257,11 +272,40 @@@ set g_balance_laser_secondary_delay 
  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_shockwave_force_zscale 1.5
 +
 +set g_balance_laser_shockwave_damage 20
 +set g_balance_laser_shockwave_distance 2000
 +set g_balance_laser_shockwave_edgedamage 0
 +set g_balance_laser_shockwave_force 200
 +set g_balance_laser_shockwave_force_forwardbias 50
- set g_balance_laser_shockwave_spread_max 100
- set g_balance_laser_shockwave_spread_min 20
++set g_balance_laser_shockwave_force_zscale 2
 +set g_balance_laser_shockwave_jump_damage 20
 +set g_balance_laser_shockwave_jump_edgedamage 0
 +set g_balance_laser_shockwave_jump_force 300
 +set g_balance_laser_shockwave_jump_force_velocitybias 0
 +set g_balance_laser_shockwave_jump_force_zscale 1.25
 +set g_balance_laser_shockwave_jump_multiplier_accuracy 0.5
 +set g_balance_laser_shockwave_jump_multiplier_distance 0.5
 +set g_balance_laser_shockwave_jump_multiplier_min 0
 +set g_balance_laser_shockwave_jump_radius 150
 +set g_balance_laser_shockwave_multiplier_accuracy 0.5
 +set g_balance_laser_shockwave_multiplier_distance 0.5
 +set g_balance_laser_shockwave_multiplier_min 0
 +set g_balance_laser_shockwave_splash_damage 15
 +set g_balance_laser_shockwave_splash_edgedamage 0
 +set g_balance_laser_shockwave_splash_force 100
 +set g_balance_laser_shockwave_splash_force_forwardbias 50
 +set g_balance_laser_shockwave_splash_multiplier_accuracy 0.5
 +set g_balance_laser_shockwave_splash_multiplier_distance 0.5
 +set g_balance_laser_shockwave_splash_multiplier_min 0
 +set g_balance_laser_shockwave_splash_radius 70
++set g_balance_laser_shockwave_spread_max 120
++set g_balance_laser_shockwave_spread_min 25
++
+ 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
@@@ -257,11 -241,8 +256,8 @@@ set g_balance_laser_primary_delay 
  set g_balance_laser_primary_force_zscale 1.25
  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_reload_ammo 0 //default: 6
- set g_balance_laser_reload_time 2
 +set g_balance_laser_secondary 2 // 0 = switch away to last used weapon, 1 = projectile secondary, 2 = melee secondary
  set g_balance_laser_secondary_damage 25
  set g_balance_laser_secondary_edgedamage 12.5
  set g_balance_laser_secondary_force 400
@@@ -276,35 -257,11 +272,40 @@@ set g_balance_laser_secondary_delay 
  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_shockwave_damage 20
 +set g_balance_laser_shockwave_distance 2000
 +set g_balance_laser_shockwave_edgedamage 0
 +set g_balance_laser_shockwave_force 200
 +set g_balance_laser_shockwave_force_forwardbias 50
 +set g_balance_laser_shockwave_force_zscale 2
 +set g_balance_laser_shockwave_jump_damage 20
 +set g_balance_laser_shockwave_jump_edgedamage 0
 +set g_balance_laser_shockwave_jump_force 300
 +set g_balance_laser_shockwave_jump_force_velocitybias 0
 +set g_balance_laser_shockwave_jump_force_zscale 1.25
 +set g_balance_laser_shockwave_jump_multiplier_accuracy 0.5
 +set g_balance_laser_shockwave_jump_multiplier_distance 0.5
 +set g_balance_laser_shockwave_jump_multiplier_min 0
 +set g_balance_laser_shockwave_jump_radius 150
 +set g_balance_laser_shockwave_multiplier_accuracy 0.5
 +set g_balance_laser_shockwave_multiplier_distance 0.5
 +set g_balance_laser_shockwave_multiplier_min 0
 +set g_balance_laser_shockwave_splash_damage 15
 +set g_balance_laser_shockwave_splash_edgedamage 0
 +set g_balance_laser_shockwave_splash_force 100
 +set g_balance_laser_shockwave_splash_force_forwardbias 50
 +set g_balance_laser_shockwave_splash_multiplier_accuracy 0.5
 +set g_balance_laser_shockwave_splash_multiplier_distance 0.5
 +set g_balance_laser_shockwave_splash_multiplier_min 0
 +set g_balance_laser_shockwave_splash_radius 70
 +set g_balance_laser_shockwave_spread_max 120
 +set g_balance_laser_shockwave_spread_min 25
++
+ 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
diff --cc effectinfo.txt
@@@ -7638,83 -7638,342 +7638,422 @@@ sizeincrease -1
  airfriction 0.04
  gravity -0.2
  
+ // redflag_touch -- effects for touching the red flag
+ // used nowhere in code
+ effect redflag_touch
+ count 35
+ type spark
+ tex 40 40
+ color 0xFF0000 0x970000
+ size 1 3
+ alpha 0 256 556
+ gravity 1
+ bounce 1.5
+ originjitter 1 1 1
+ velocityjitter 300 300 300
+ velocitymultiplier 0.5
+ airfriction 3
+ // blueflag_touch -- effects for touching the blue flag
+ // used nowhere in code
+ effect blueflag_touch
+ count 35
+ type spark
+ tex 40 40
+ color 0x0000FF 0x000097
+ size 1 3
+ alpha 0 256 556
+ gravity 1
+ bounce 1.5
+ originjitter 1 1 1
+ velocityjitter 300 300 300
+ velocitymultiplier 0.5
+ airfriction 3
+ // red_pass
+ // used nowhere in code
+ effect red_pass
+ trailspacing 64
+ color 0xFF0000 0x970000
+ size 2 2
+ tex 32 32
+ alpha 64 128 64
+ airfriction 5
+ sizeincrease 2
+ type static
+ effect red_pass
+ trailspacing 12
+ color 0xFF0000 0x970000
+ size 1 1
+ tex 0 8
+ alpha 32 64 32
+ airfriction 9
+ sizeincrease 8
+ velocityjitter 64 64 64
+ type static
+ effect red_pass
+ trailspacing 12
+ color 0xFF0000 0x970000
+ size 4 4
+ //tex 48 55
+ alpha 256 256 1280
+ type static
+ // blue_pass
+ // used nowhere in code
+ effect blue_pass
+ trailspacing 64
+ color 0x0000FF 0x000097
+ size 2 2
+ tex 32 32
+ alpha 64 128 64
+ airfriction 5
+ sizeincrease 2
+ type static
+ effect blue_pass
+ trailspacing 12
+ color 0x0000FF 0x000097
+ size 1 1
+ tex 0 8
+ alpha 32 64 32
+ airfriction 9
+ sizeincrease 8
+ velocityjitter 64 64 64
+ type static
+ effect blue_pass
+ trailspacing 12
+ color 0x0000FF 0x000097
+ size 4 4
+ //tex 48 55
+ alpha 256 256 1280
+ type static
+ // red_cap -- red team capture effect
+ effect red_cap
+ count 500
+ type spark
+ tex 64 64
+ color 0xFF0000 0x970000
+ size 1 1
+ alpha 0 256 100
+ stretchfactor 2
+ //gravity 1
+ bounce 1.5
+ originjitter 1 1 1
+ velocityjitter 1000 1000 1500
+ velocitymultiplier 0.5
+ airfriction 2
+ stretchfactor 0.6
+ effect red_cap
+ countabsolute 1
+ type smoke
+ tex 65 65
+ size 150 150
+ alpha 190 190 180
+ sizeincrease -80
+ color 0xFF0000 0x970000
+ // blue_cap -- blue team capture effect
+ effect blue_cap
+ count 500
+ type spark
+ tex 64 64
+ color 0x0000FF 0x000097
+ size 1 1
+ alpha 0 256 100
+ stretchfactor 2
+ //gravity 1
+ bounce 1.5
+ originjitter 1 1 1
+ velocityjitter 1000 1000 1500
+ velocitymultiplier 0.5
+ airfriction 2
+ stretchfactor 0.6
+ effect blue_cap
+ countabsolute 1
+ type smoke
+ tex 65 65
+ size 150 150
+ alpha 190 190 180
+ sizeincrease -80
+ color 0x0000FF 0x000097
+ // spawn_point_red -- red team idle spawn point effect
+ effect spawn_point_red
+ count 37.5
+ type static
+ color 0xFF0F0F 0xFF0F0F
+ size 1.0 2.0
+ alpha 64 128 128
+ gravity -0.1
+ airfriction 0.2
+ liquidfriction 0.8
+ originjitter 16 16 64
+ velocityjitter 32 32 0
+ //lightradius 200
+ //lighttime 0
+ //lightcolor 0.4 0.9 0.9
+ // spawn_point_blue -- blue team idle spawn point effect
+ effect spawn_point_blue
+ count 37.5
+ type static
+ color 0x0F0FFF 0x0F0FFF
+ size 1.0 2.0
+ alpha 64 128 128
+ gravity -0.1
+ airfriction 0.2
+ liquidfriction 0.8
+ originjitter 16 16 64
+ velocityjitter 32 32 0
+ //lightradius 200
+ //lighttime 0
+ //lightcolor 0.4 0.9 0.9
+ // spawn_point_yellow -- yellow team idle spawn point effect
+ effect spawn_point_yellow
+ count 37.5
+ type static
+ color 0xFFFF0F 0xFFFF0F
+ size 1.0 2.0
+ alpha 64 128 128
+ gravity -0.1
+ airfriction 0.2
+ liquidfriction 0.8
+ originjitter 16 16 64
+ velocityjitter 32 32 0
+ //lightradius 200
+ //lighttime 0
+ //lightcolor 0.4 0.9 0.9
+ // spawn_point_pink -- pink team idle spawn point effect
+ effect spawn_point_pink
+ count 37.5
+ type static
+ color 0xFF0FFF 0xFF0FFF
+ size 1.0 2.0
+ alpha 64 128 128
+ gravity -0.1
+ airfriction 0.2
+ liquidfriction 0.8
+ originjitter 16 16 64
+ velocityjitter 32 32 0
+ //lightradius 200
+ //lighttime 0
+ //lightcolor 0.4 0.9 0.9
+ // spawn_point_neutral -- neutral idle spawn point effect
+ effect spawn_point_neutral
+ count 37.5
+ type static
+ color 0xFFFFFF 0xFFFFFF
+ size 1.0 2.0
+ alpha 64 128 128
+ gravity -0.1
+ airfriction 0.2
+ liquidfriction 0.8
+ originjitter 16 16 64
+ velocityjitter 32 32 0
+ //lightradius 200
+ //lighttime 0
+ //lightcolor 0.4 0.9 0.9
+ // spawn_event_red -- red team spawning effect
+ effect spawn_event_red
+ count 100
+ type spark
+ tex 64 64
+ color 0xFF0F0F 0xFF0F0F
+ size 1 1
+ alpha 0 256 256
+ stretchfactor 0.6
+ //gravity 1
+ bounce 1
+ originjitter 1 1 1
+ velocityjitter 500 500 500
+ velocitymultiplier 0.1
+ airfriction 2
+ effect spawn_event_red
+ countabsolute 1
+ type smoke
+ tex 65 65
+ size 100 100
+ alpha 190 190 180
+ sizeincrease -80
+ color 0xFF0F0F 0xFF0F0F
+ // spawn_event_blue -- blue team spawning effect
+ effect spawn_event_blue
+ count 100
+ type spark
+ tex 64 64
+ color 0x0F0FFF 0x0F0FFF
+ size 1 1
+ alpha 0 256 256
+ stretchfactor 0.6
+ //gravity 1
+ bounce 1
+ originjitter 1 1 1
+ velocityjitter 500 500 500
+ velocitymultiplier 0.1
+ airfriction 2
+ effect spawn_event_blue
+ countabsolute 1
+ type smoke
+ tex 65 65
+ size 100 100
+ alpha 190 190 180
+ sizeincrease -80
+ color 0x0F0FFF 0x0F0FFF
+ // spawn_event_yellow -- yellow team spawning effect
+ effect spawn_event_yellow
+ count 100
+ type spark
+ tex 64 64
+ color 0xFFFF0F 0xFFFF0F
+ size 1 1
+ alpha 0 256 256
+ stretchfactor 0.6
+ //gravity 1
+ bounce 1
+ originjitter 1 1 1
+ velocityjitter 500 500 500
+ velocitymultiplier 0.1
+ airfriction 2
+ effect spawn_event_yellow
+ countabsolute 1
+ type smoke
+ tex 65 65
+ size 100 100
+ alpha 190 190 180
+ sizeincrease -80
+ color 0xFFFF0F 0xFFFF0F
+ // spawn_event_pink -- pink team spawning effect
+ effect spawn_event_pink
+ count 100
+ type spark
+ tex 64 64
+ color 0xFF0FFF 0xFF0FFF
+ size 1 1
+ alpha 0 256 256
+ stretchfactor 0.6
+ //gravity 1
+ bounce 1
+ originjitter 1 1 1
+ velocityjitter 500 500 500
+ velocitymultiplier 0.1
+ airfriction 2
+ effect spawn_event_pink
+ countabsolute 1
+ type smoke
+ tex 65 65
+ size 100 100
+ alpha 190 190 180
+ sizeincrease -80
+ color 0xFF0FFF 0xFF0FFF
+ // spawn_event_neutral -- neutral spawning effect
+ effect spawn_event_neutral
+ count 100
+ type spark
+ tex 64 64
+ color 0xFFFFFF 0xFFFFFF
+ size 1 1
+ alpha 0 256 256
+ stretchfactor 0.6
+ //gravity 1
+ bounce 1
+ originjitter 1 1 1
+ velocityjitter 500 500 500
+ velocitymultiplier 0.1
+ airfriction 2
+ effect spawn_event_neutral
+ countabsolute 1
+ type smoke
+ tex 65 65
+ size 100 100
+ alpha 190 190 180
+ sizeincrease -80
+ color 0xFFFFFF 0xFFFFFF
 +
 +// laser_shockwave_attack
 +// used nowhere in code
 +effect laser_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
 +// electricity
 +effect laser_shockwave_attack
 +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
 +// fire
 +effect laser_shockwave_attack
 +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
 +
 +// new_laser_impact
 +// used nowhere in code
 +// decal
 +effect new_laser_impact
 +countabsolute 1
 +type decal
 +tex 8 16
 +size 72 72
 +alpha 256 256 0
 +originjitter 2 2 2
 +// flare effect
 +//effect new_laser_impact
 +//countabsolute 1
 +//type static
 +//tex 39 39
 +//color 0xFF2010 0xFF2010
 +//alpha 256 256 1024
 +//size 24 24
 +// sparks that rapidly expand and rapidly slow down to form an interesting spherical effect
 +effect new_laser_impact
 +count 128
 +type spark
 +color 0x800000 0xFF8020
 +alpha 256 256 1024
 +size 4 4
 +bounce 1.5
 +gravity 0.5
 +airfriction 1
 +liquidfriction 1
 +originjitter 20 20 20
 +velocityjitter 256 256 256
Simple merge
Simple merge
Simple merge
Simple merge
@@@ -301,121 -301,3 +301,121 @@@ void Net_ReadNexgunBeamParticle(
        else
                WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum("nex_beam"), shotorg, endpos, charge, 1);
  }
-       vector first_min_end, prev_min_end, new_min_end;
-       vector first_max_end, prev_max_end, new_max_end;
 +
 +.vector sw_shotorg;
 +.vector sw_endpos;
 +.float sw_spread_max;
 +.float sw_spread_min;
 +.float sw_time;
 +
 +void Draw_Shockwave()
 +{
 +      float a = bound(0, (0.5 - ((time - self.sw_time) / 0.4)), 0.5);
 +
 +      if not(a) { remove(self); }
 +      
 +      vector deviation, angle;
 +
 +      vector sw_color = getcsqcplayercolor(self.sv_entnum); // GetTeamRGB(GetPlayerColor(self.sv_entnum));
 +
++      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 shotdir = normalize(self.sw_endpos - self.sw_shotorg);
 +      vectorvectors(shotdir);
 +      vector right = v_right;
 +      vector up = v_up;
 +      
 +      float counter, dist_before_normal = 200, shots = 20;
 +      
 +      vector min_end = ((self.sw_shotorg + (shotdir * dist_before_normal)) + (up * self.sw_spread_min));
 +      vector max_end = (self.sw_endpos + (up * self.sw_spread_max));
 +      
 +      float spread_to_min = vlen(normalize(min_end - self.sw_shotorg) - shotdir);
 +      float spread_to_max = vlen(normalize(max_end - min_end) - shotdir);
 +      
 +      for(counter = 0; counter < shots; ++counter)
 +      {
 +              // perfect circle effect lines
 +              angle = '0 0 0';
 +              makevectors('0 360 0' * (0.75 + (counter - 0.5) / shots));
 +              angle_y = v_forward_x;
 +              angle_z = v_forward_y;
 +
 +              // first do the spread_to_min effect
 +              deviation = angle * spread_to_min;
 +              deviation = ((shotdir + (right * deviation_y) + (up * deviation_z)));
 +              new_min_dist = dist_before_normal;
 +              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 = ((shotdir + (right * deviation_y) + (up * deviation_z)));
 +              new_max_dist = vlen(new_min_end - self.sw_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)
 +              {
 +                      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();
 +
 +                      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;
 +
 +              if((counter + 1) == shots)
 +              {
 +                      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();
 +
 +                      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()
 +{
 +      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_endpos_x  = ReadCoord(); shockwave.sw_endpos_y  = ReadCoord(); shockwave.sw_endpos_z  = ReadCoord();
 +      
 +      shockwave.sw_spread_max = ReadByte();
 +      shockwave.sw_spread_min = ReadByte();
 +
 +      shockwave.sv_entnum = ReadByte();
 +
 +      shockwave.sw_time = time;
 +}
 +
@@@ -30,24 -30,18 +30,19 @@@ const float AS_FLOAT               = 8
  
  const float TE_CSQC_PICTURE = 100;
  const float TE_CSQC_RACE = 101;
- const float TE_CSQC_SPAWN = 102;
- const float TE_CSQC_ZCURVEPARTICLES = 103;
- const float TE_CSQC_NEXGUNBEAMPARTICLE = 104;
- const float TE_CSQC_LIGHTNINGARC = 105;
- const float TE_CSQC_TEAMNAGGER = 106;
- const float TE_CSQC_PINGPLREPORT = 107;
- const float TE_CSQC_SHOCKWAVEPARTICLE = 121;
- const float TE_CSQC_ANNOUNCE = 110;
- const float TE_CSQC_TARGET_MUSIC = 111;
- const float TE_CSQC_KILLNOTIFY = 112;
- const float TE_CSQC_KILLCENTERPRINT = 113;
- const float TE_CSQC_CENTERPRINT_GENERIC = 114;
- const float TE_CSQC_WEAPONCOMPLAIN = 115;
- const float TE_CSQC_NEX_SCOPE = 116;
- const float TE_CSQC_MINELAYER_MAXMINES = 117;
- const float TE_CSQC_HAGAR_MAXROCKETS = 118;
- const float TE_CSQC_VEHICLESETUP = 119;
- const float TE_CSQC_SVNOTICE = 120;
+ const float TE_CSQC_ZCURVEPARTICLES = 102;
+ const float TE_CSQC_NEXGUNBEAMPARTICLE = 103;
+ const float TE_CSQC_LIGHTNINGARC = 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_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;
@@@ -93,13 -87,16 +88,16 @@@ const float ENT_CLIENT_WARPZONE_CAMERA 
  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_ACCURACY = 30;
 -const float ENT_CLIENT_SHOWNAMES = 31;
 -const float ENT_CLIENT_WARPZONE_TELEPORTED = 32;
 -const float ENT_CLIENT_MODEL = 33;
 -const float ENT_CLIENT_ITEM = 34;
 -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_ACCURACY = 29;
 +const float ENT_CLIENT_SHOWNAMES = 30;
 +const float ENT_CLIENT_WARPZONE_TELEPORTED = 31;
 +const float ENT_CLIENT_MODEL = 32;
 +const float ENT_CLIENT_ITEM = 33;
 +const float ENT_CLIENT_BUMBLE_RAYGUN = 34;
++const float ENT_CLIENT_SPAWNPOINT = 35;
++const float ENT_CLIENT_SPAWNEVENT = 36;
++const float ENT_CLIENT_NOTIFICATION = 37;
 +
  
  const float ENT_CLIENT_TURRET = 40;
  const float ENT_CLIENT_AUXILIARYXHAIR = 50;
@@@ -2422,219 -2462,147 +2462,364 @@@ float cubic_speedfunc_is_sane(float sta
        // (4, 1)
  }
  
-       vector v1, v2;
+ .float FindConnectedComponent_processing;
+ void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass)
+ {
+       entity queue_start, queue_end;
+       // we build a queue of to-be-processed entities.
+       // queue_start is the next entity to be checked for neighbors
+       // queue_end is the last entity added
+       if(e.FindConnectedComponent_processing)
+               error("recursion or broken cleanup");
+       // start with a 1-element queue
+       queue_start = queue_end = e;
+       queue_end.fld = world;
+       queue_end.FindConnectedComponent_processing = 1;
+       // for each queued item:
+       for(; queue_start; queue_start = queue_start.fld)
+       {
+               // find all neighbors of queue_start
+               entity t;
+               for(t = world; (t = nxt(t, queue_start, pass)); )
+               {
+                       if(t.FindConnectedComponent_processing)
+                               continue;
+                       if(iscon(t, queue_start, pass))
+                       {
+                               // it is connected? ADD IT. It will look for neighbors soon too.
+                               queue_end.fld = t;
+                               queue_end = t;
+                               queue_end.fld = world;
+                               queue_end.FindConnectedComponent_processing = 1;
+                       }
+               }
+       }
+       // unmark
+       for(queue_start = e; queue_start; queue_start = queue_start.fld)
+               queue_start.FindConnectedComponent_processing = 0;
+ }
++
 +#ifndef MENUQC
 +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 position;
++      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;
 +       */
 +}
 +#endif
 +
 +#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)
+ {
+       float dev, war;
+       #ifdef SVQC
+       dev = autocvar_developer;
+       war = autocvar_prvm_backtraceforwarnings;
+       #else
+       dev = cvar("developer");
+       war = cvar("prvm_backtraceforwarnings");
+       #endif
+       cvar_set("developer", "1");
+       cvar_set("prvm_backtraceforwarnings", "1");
+       print("\n");
+       print("--- CUT HERE ---\nWARNING: ");
+       print(msg);
+       print("\n");
+       remove(world); // isn't there any better way to cause a backtrace?
+       print("\n--- CUT UNTIL HERE ---\n");
+       cvar_set("developer", ftos(dev));
+       cvar_set("prvm_backtraceforwarnings", ftos(war));
+ }
+ #endif
+ // color code replace, place inside of sprintf and parse the string
+ string CCR(string input)
+ {
+       // See the autocvar declarations in util.qh for default values
+       
+       // foreground/normal colors
+       input = strreplace("^F1", strcat("^", autocvar_hud_colorset_foreground_1), input); 
+       input = strreplace("^F2", strcat("^", autocvar_hud_colorset_foreground_2), input); 
+       input = strreplace("^F3", strcat("^", autocvar_hud_colorset_foreground_3), input); 
+       input = strreplace("^F4", strcat("^", autocvar_hud_colorset_foreground_4), input); 
+       // "kill" colors
+       input = strreplace("^K1", strcat("^", autocvar_hud_colorset_kill_1), input);
+       input = strreplace("^K2", strcat("^", autocvar_hud_colorset_kill_2), input);
+       input = strreplace("^K3", strcat("^", autocvar_hud_colorset_kill_3), input);
+       // background colors
+       input = strreplace("^BG", strcat("^", autocvar_hud_colorset_background), input);
+       input = strreplace("^N", "^7", input); // "none"-- reset to white...
+       return input;
+ }
+ vector vec3(float x, float y, float z)
+ {
+       vector v;
+       v_x = x;
+       v_y = y;
+       v_z = z;
+       return v;
+ }
+ #ifndef MENUQC
+ vector animfixfps(entity e, vector a, vector b)
+ {
+       // multi-frame anim: keep as-is
+       if(a_y == 1)
+       {
+               float dur;
+               dur = frameduration(e.modelindex, a_x);
+               if(dur <= 0 && b_y)
+               {
+                       a = b;
+                       dur = frameduration(e.modelindex, a_x);
+               }
+               if(dur > 0)
+                       a_z = 1.0 / dur;
+       }
+       return a;
+ }
+ #endif
+ #ifdef SVQC
+ void dedicated_print(string input) // print(), but only print if the server is not local
+ {
+       if(server_is_dedicated) { print(input); }
+ }
+ #endif
+ #ifndef MENUQC
+ float Announcer_PickNumber(float num)
+ {
+       switch(num)
+       {
+               case 10: num = ANNCE_NUM_10; break;
+               case 9:  num = ANNCE_NUM_9;  break;
+               case 8:  num = ANNCE_NUM_8;  break;
+               case 7:  num = ANNCE_NUM_7;  break;
+               case 6:  num = ANNCE_NUM_6;  break;
+               case 5:  num = ANNCE_NUM_5;  break;
+               case 4:  num = ANNCE_NUM_4;  break;
+               case 3:  num = ANNCE_NUM_3;  break;
+               case 2:  num = ANNCE_NUM_2;  break;
+               case 1:  num = ANNCE_NUM_1;  break;
+       }
+       return num;
+ }
+ #endif
@@@ -334,10 -342,67 +342,75 @@@ float cubic_speedfunc(float startspeedf
  // as it may exceed 0..1 bounds, or go in reverse
  float cubic_speedfunc_is_sane(float startspeedfactor, float endspeedfactor);
  
- vector W_CalculateSpread(vector forward, float spread, float spreadfactor, float spreadstyle)
+ typedef entity(entity cur, entity near, entity pass) findNextEntityNearFunction_t;
+ typedef float(entity a, entity b, entity pass) isConnectedFunction_t;
+ void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass);
 +#ifndef MENUQC
++vector W_CalculateSpread(vector forward, float spread, float spreadfactor, float spreadstyle);
 +#endif
 +
 +#ifdef SVQC
 +vector get_corner_position(entity box, float corner);
 +#endif
++
+ // expand multiple arguments into one argument by stripping parenthesis
+ #define XPD(...) __VA_ARGS__
+ #ifndef MENUQC
+ void backtrace(string msg);
+ #endif
+ // color code replace, place inside of sprintf and parse the string... defaults described as constants
+ // foreground/normal colors
+ var string autocvar_hud_colorset_foreground_1 = "2"; // F1 - Green  // primary priority (important names, etc)
+ var string autocvar_hud_colorset_foreground_2 = "3"; // F2 - Yellow // secondary priority (items, locations, numbers, etc)
+ var string autocvar_hud_colorset_foreground_3 = "4"; // F3 - Blue   // tertiary priority or relatively inconsequential text
+ var string autocvar_hud_colorset_foreground_4 = "1"; // F4 - Red    // notice/attention grabbing texting
+ // "kill" colors
+ var string autocvar_hud_colorset_kill_1 = "1"; // K1 - Red    // "bad" or "dangerous" text (death messages against you, kill notifications, etc)
+ var string autocvar_hud_colorset_kill_2 = "3"; // K2 - Yellow // similar to above, but less important... OR, a highlight out of above message type
+ var string autocvar_hud_colorset_kill_3 = "4"; // K3 - Blue   // "good" or "beneficial" text (you fragging someone, etc)
+ // background color
+ var string autocvar_hud_colorset_background = "7"; // BG - White // neutral/unimportant text
+ string CCR(string input);
+ #ifndef MENUQC
+ #ifdef CSQC
+ #define GENTLE (autocvar_cl_gentle || autocvar_cl_gentle_messages)
+ #else
+ #define GENTLE autocvar_sv_gentle
+ #endif
+ #define normal_or_gentle(normal,gentle) (GENTLE ? ((gentle != "") ? gentle : normal) : normal)
+ #endif
+ // allow writing to also pass through to spectators (like so spectators see the same centerprints as players for example)
+ #define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname
+ #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
+ #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
+ vector vec3(float x, float y, float z);
+ #ifndef MENUQC
+ vector animfixfps(entity e, vector a, vector b);
+ #endif
+ #ifdef SVQC
+ void dedicated_print(string input);
+ #endif
+ // todo: better way to do this?
+ #ifdef MENUQC
+ #define PROGNAME "MENUQC"
+ #else
+ #ifdef SVQC
+ #define PROGNAME "SVQC"
+ #else
+ #define PROGNAME "CSQC"
+ #endif
+ #endif
+ #ifndef MENUQC
+ float Announcer_PickNumber(float num);
+ #endif
@@@ -446,57 -420,27 +434,57 @@@ float autocvar_g_balance_laser_primary_
  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_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_primary_spread;
++//float autocvar_g_balance_laser_primary_spread;
 +float autocvar_g_balance_laser_reload_ammo;
 +float autocvar_g_balance_laser_reload_time;
  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_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_laser_secondary_refire;
- float autocvar_g_balance_laser_secondary_speed;
++//float autocvar_g_balance_laser_secondary_speed;
 +float autocvar_g_balance_laser_shockwave_damage;
 +float autocvar_g_balance_laser_shockwave_distance;
 +float autocvar_g_balance_laser_shockwave_edgedamage;
 +float autocvar_g_balance_laser_shockwave_force;
 +float autocvar_g_balance_laser_shockwave_force_forwardbias;
 +float autocvar_g_balance_laser_shockwave_force_zscale;
 +float autocvar_g_balance_laser_shockwave_jump_damage;
 +float autocvar_g_balance_laser_shockwave_jump_edgedamage;
 +float autocvar_g_balance_laser_shockwave_jump_force;
 +float autocvar_g_balance_laser_shockwave_jump_force_velocitybias;
 +float autocvar_g_balance_laser_shockwave_jump_force_zscale;
 +float autocvar_g_balance_laser_shockwave_jump_multiplier_accuracy;
 +float autocvar_g_balance_laser_shockwave_jump_multiplier_distance;
 +float autocvar_g_balance_laser_shockwave_jump_multiplier_min;
 +float autocvar_g_balance_laser_shockwave_jump_radius;
 +float autocvar_g_balance_laser_shockwave_multiplier_accuracy;
 +float autocvar_g_balance_laser_shockwave_multiplier_distance;
 +float autocvar_g_balance_laser_shockwave_multiplier_min;
 +float autocvar_g_balance_laser_shockwave_splash_damage;
 +float autocvar_g_balance_laser_shockwave_splash_edgedamage;
 +float autocvar_g_balance_laser_shockwave_splash_force;
 +float autocvar_g_balance_laser_shockwave_splash_force_forwardbias;
 +float autocvar_g_balance_laser_shockwave_splash_multiplier_accuracy;
 +float autocvar_g_balance_laser_shockwave_splash_multiplier_distance;
 +float autocvar_g_balance_laser_shockwave_splash_multiplier_min;
 +float autocvar_g_balance_laser_shockwave_splash_radius;
 +float autocvar_g_balance_laser_shockwave_spread_max;
 +float autocvar_g_balance_laser_shockwave_spread_min;
  float autocvar_g_balance_minelayer_ammo;
  float autocvar_g_balance_minelayer_animtime;
  float autocvar_g_balance_minelayer_damage;
@@@ -271,32 -249,24 +249,24 @@@ float CheatImpulse(float i
                        break;
                case CHIMPULSE_R00T:
                        IS_CHEAT(i, 0, 0);
+                       RandomSelection_Init();
                        FOR_EACH_PLAYER(e)
-                       {
-                               get_model_parameters(e.playermodel, e.skin);
-                               if(get_model_parameters_sex == "Female")
-                               {
-                                       makevectors(e.angles);
-                                       traceline(e.origin, e.origin + v_right * 256, MOVE_NORMAL, e);
-                               }
-                               else
-                               {
-                                       org_x = random();
-                                       org_y = random();
-                                       org_z = 0;
-                                       org = normalize(org);
-                                       traceline(e.origin, e.origin + org * 256, MOVE_NORMAL, e); // random direction
-                               }
+                               if(e.deadflag == DEAD_NO)
+                                       if(IsDifferentTeam(e, self))
+                                               RandomSelection_Add(e, 0, string_null, 1, 1);
+                       if(RandomSelection_chosen_ent)
+                               e = RandomSelection_chosen_ent;
+                       else
+                               e = self;
  
-                               org = findbetterlocation(trace_endpos, 12);
+                       pointparticles(particleeffectnum("rocket_explode"), e.origin, '0 0 0', 1);
+                       sound(e, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
+                       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);
  
-                               e2 = spawn();
-                               setorigin(e2, org);
-                               pointparticles(particleeffectnum("rocket_explode"), org, '0 0 0', 1);
-                               sound(e2, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
-                               RadiusDamage(e2, e, 1000, 0, 128, e, world, 500, DEATH_CHEAT, world);
-                               remove(e2);
-                       }
                        print("404 Sportsmanship not found.\n");
                        DID_CHEAT();
                        break;
Simple merge
Simple merge
Simple merge
@@@ -1032,133 -858,176 +856,130 @@@ float RadiusDamageForSource (entity inf
        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;
 -
 -                                              myblastorigin = WarpZone_TransformOrigin(targ, blastorigin);
 -
 -                                              // if it's a player, use the view origin as reference
 -                                              center = CENTER_OR_VIEWOFS(targ);
 +                                      float a;
 +                                      float c;
 +                                      vector hitloc;
 +                                      vector myblastorigin;
 +                                      vector center;
  
 -                                              force = normalize(center - myblastorigin);
 -                                              force = force * (finaldmg / coredamage) * forceintensity;
 -                                              hitloc = nearest;
 +                                      myblastorigin = WarpZone_TransformOrigin(targ, inflictororigin);
  
 -                                              if(targ != directhitentity)
 -                                              {
 -                                                      float hits;
 -                                                      float total;
 -                                                      float hitratio;
 -                                                      float mininv_f, mininv_d;
 +                                      // if it's a player, use the view origin as reference
-                                       if (targ.classname == "player")
-                                               center = targ.origin + targ.view_ofs;
-                                       else
-                                               center = targ.origin + (targ.mins + targ.maxs) * 0.5;
++                                      center = CENTER_OR_VIEWOFS(targ);
  
 -                                                      // test line of sight to multiple positions on box,
 -                                                      // and do damage if any of them hit
 -                                                      hits = 0;
 +                                      force = normalize(center - myblastorigin);
 +                                      force = force * (finaldmg / coredamage) * forceintensity;
 +                                      hitloc = nearest;
  
 -                                                      // 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(targ != directhitentity)
 +                                      {
 +                                              float hits;
 +                                              float total;
 +                                              float hitratio;
 +                                              float mininv_f, mininv_d;
  
 -                                                      mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev;
 -                                                      mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev;
 +                                              // test line of sight to multiple positions on box,
 +                                              // and do damage if any of them hit
 +                                              hits = 0;
  
 -                                                      if(autocvar_g_throughfloor_debug)
 -                                                              print(sprintf("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f));
 +                                              // we know: max stddev of hitratio = 1 / (2 * sqrt(n))
 +                                              // so for a given max stddev:
 +                                              // n = (1 / (2 * max stddev of hitratio))^2
  
 -                                                      total = 0.25 * pow(max(mininv_f, mininv_d), 2);
 +                                              mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev;
 +                                              mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev;
  
 -                                                      if(autocvar_g_throughfloor_debug)
 -                                                              print(sprintf(" steps=%f", total));
 +                                              if(autocvar_g_throughfloor_debug)
 +                                                      print(sprintf("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f));
  
 -                                                      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)
 -                                                              print(sprintf(" 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)
 +                                                      print(sprintf(" steps=%f", total));
  
-                                               if (targ.classname == "player")
 -                                                      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)
 -                                                              print(sprintf(" D=%f F=%f\n", finaldmg, vlen(force)));
 -                                              }
 +                                              if(autocvar_g_throughfloor_debug)
 +                                                      print(sprintf(" 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)
 -                                                      {
 -                                                              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;
 -                                                              }
 -                                                              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
 +                                                      //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)
                                                        {
 -                                                              if(deathtype & HITTYPE_SECONDARY)
 -                                                              {
 -                                                                      force *= autocvar_g_balance_laser_secondary_force_other_scale;
 -                                                              }
 +                                                              ++hits;
 +                                                              if (hits > 1)
 +                                                                      hitloc = hitloc + nearest;
                                                                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)
 +                                                      print(sprintf(" 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);
                                        }
                                }
                        }
Simple merge
Simple merge
index 0000000,f51ed65..3a9038f
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,46 +1,46 @@@
 -      RadiusDamage(e, world, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, world, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE, world);
+ .float touchexplode_time;
+ void PlayerTouchExplode(entity p1, entity p2)
+ {
+       vector org;
+       org = (p1.origin + p2.origin) * 0.5;
+       org_z += (p1.mins_z + p2.mins_z) * 0.5;
+       sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
+       pointparticles(particleeffectnum("explosion_small"), org, '0 0 0', 1);
+       entity e;
+       e = spawn();
+       setorigin(e, org);
++      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);
+ }
+ MUTATOR_HOOKFUNCTION(touchexplode_PlayerThink)
+ {
+       if(time > self.touchexplode_time)
+       if not(gameover)
+       if(IS_PLAYER(self))
+       if(self.deadflag == DEAD_NO)
+       if not(IS_INDEPENDENT_PLAYER(self))
+       FOR_EACH_PLAYER(other) if(self != other)
+       {
+               if(time > other.touchexplode_time)
+               if(other.deadflag == DEAD_NO)
+               if not(IS_INDEPENDENT_PLAYER(other))
+               if(boxesoverlap(self.absmin, self.absmax, other.absmin, other.absmax))
+               {
+                       PlayerTouchExplode(self, other);
+                       self.touchexplode_time = other.touchexplode_time = time + 0.2;
+               }
+       }
+       return FALSE;
+ }
+ MUTATOR_DEFINITION(mutator_touchexplode)
+ {
+       MUTATOR_HOOK(PlayerPreThink, touchexplode_PlayerThink, CBC_ORDER_ANY);
+       return FALSE;
+ }
Simple merge
@@@ -145,10 -145,10 +145,10 @@@ void turret_projectile_explode(
  {
      
      self.takedamage = DAMAGE_NO;
-     self.event_damage = SUB_Null;    
+     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
@@@ -51,7 -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, world, autocvar_g_turrets_unit_walker_std_rocket_force, DEATH_TURRET_WALKER_ROCKET, world);
 -    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);
  }
  
@@@ -740,9 -731,9 +731,9 @@@ 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_WAKIBLOWUP, world);
+                                DEATH_VH_BUMB_DEATH, world);
  
        sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
        pointparticles(particleeffectnum("explosion_large"), randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1);
@@@ -540,12 -540,12 +540,12 @@@ 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_WAKIBLOWUP, world);
+                                       DEATH_VH_WAKI_DEATH, world);
  
      self.nextthink  = time + autocvar_g_vehicle_racer_respawntime;
-     self.think      = racer_spawn;
+     self.think      = racer_spawn_default;
      self.movetype   = MOVETYPE_NONE;
      self.effects    = EF_NODRAW;
  
@@@ -92,8 -92,8 +92,8 @@@ 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_RAPTOR_BOMB, world);
+                                     autocvar_g_vehicle_raptor_bomblet_force, DEATH_VH_RAPT_BOMB, world);
      remove(self);
  }
  
@@@ -690,8 -690,7 +690,8 @@@ void raptor_blowup(
  {
      self.deadflag    = DEAD_DEAD;
      self.vehicle_exit(VHEF_NORMAL);
 -    RadiusDamage (self, self.enemy, 250, 15, 250, world, 250, DEATH_VH_RAPT_DEATH, world);
 +
-     RadiusDamage (self, self.enemy, 250, 15, 250, world, world, 250, DEATH_WAKIBLOWUP, world);
++    RadiusDamage (self, self.enemy, 250, 15, 250, world, world, 250, DEATH_VH_RAPT_DEATH, world);
  
      self.alpha          = -1;
      self.movetype       = MOVETYPE_NONE;
@@@ -723,7 -723,7 +723,7 @@@ void spiderbot_blowup(
      SUB_SetFade(g1, time, min(autocvar_g_vehicle_spiderbot_respawntime, 10));
      SUB_SetFade(g2, time, min(autocvar_g_vehicle_spiderbot_respawntime, 10));
  
-     RadiusDamage (self, self.enemy, 250, 15, 250, world, world, 250, DEATH_SBBLOWUP, world);
 -    RadiusDamage (self, self.enemy, 250, 15, 250, world, 250, DEATH_VH_SPID_DEATH, world);
++    RadiusDamage (self, self.enemy, 250, 15, 250, world, world, 250, DEATH_VH_SPID_DEATH, world);
  
      self.alpha = self.tur_head.alpha = self.gun1.alpha = self.gun2.alpha = -1;
      self.movetype   = MOVETYPE_NONE;
@@@ -397,8 -396,8 +396,8 @@@ void vehicles_projectile_explode(
  
        PROJECTILE_TOUCH;
  
-       self.event_damage = SUB_Null;
+       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);
  }
Simple merge
@@@ -242,7 -274,10 +274,10 @@@ void W_Crylink_Touch (void
                f = autocvar_g_balance_crylink_primary_bouncedamagefactor;
        if(a)
                f *= a;
-       if (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, world, autocvar_g_balance_crylink_primary_force * f, self.projectiledeathtype, other) && autocvar_g_balance_crylink_primary_linkexplode)
 -      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);
++      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, 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;
@@@ -283,7 -318,10 +318,10 @@@ void W_Crylink_Touch2 (void
                f = autocvar_g_balance_crylink_secondary_bouncedamagefactor;
        if(a)
                f *= a;
-       if (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, world, autocvar_g_balance_crylink_secondary_force * f, self.projectiledeathtype, other) && autocvar_g_balance_crylink_secondary_linkexplode)
 -      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);
++      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, 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;
@@@ -54,8 -64,8 +64,9 @@@ void W_Plasma_Explode_Combo (void
  {
        W_Plasma_TriggerCombo(self.origin, autocvar_g_balance_electro_combo_comboradius, self.realowner);
  
-       self.event_damage = SUB_Null;
+       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
 +      RadiusDamage (self, self.realowner, autocvar_g_balance_electro_combo_damage, autocvar_g_balance_electro_combo_edgedamage, autocvar_g_balance_electro_combo_radius, world, 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);
  }
  
Simple merge
Simple merge
@@@ -6,16 -16,16 +16,16 @@@ REGISTER_WEAPON
  
  void W_Hagar_Explode (void)
  {
-       self.event_damage = SUB_Null;
+       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);
 +      RadiusDamage (self, self.realowner, autocvar_g_balance_hagar_primary_damage, autocvar_g_balance_hagar_primary_edgedamage, autocvar_g_balance_hagar_primary_radius, world, world, autocvar_g_balance_hagar_primary_force, self.projectiledeathtype, other);
  
        remove (self);
  }
  
  void W_Hagar_Explode2 (void)
  {
-       self.event_damage = SUB_Null;
+       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);
 +      RadiusDamage (self, self.realowner, autocvar_g_balance_hagar_secondary_damage, autocvar_g_balance_hagar_secondary_edgedamage, autocvar_g_balance_hagar_secondary_radius, world, world, autocvar_g_balance_hagar_secondary_force, self.projectiledeathtype, other);
  
        remove (self);
  }
@@@ -7,12 -17,12 +17,12 @@@ void W_HLAC_Touch (void
  {
        PROJECTILE_TOUCH;
  
-       self.event_damage = SUB_Null;
+       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);
 +              RadiusDamage (self, self.realowner, autocvar_g_balance_hlac_secondary_damage, autocvar_g_balance_hlac_secondary_edgedamage, autocvar_g_balance_hlac_secondary_radius, world, 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);
 +              RadiusDamage (self, self.realowner, autocvar_g_balance_hlac_primary_damage, autocvar_g_balance_hlac_primary_edgedamage, autocvar_g_balance_hlac_primary_radius, world, world, autocvar_g_balance_hlac_primary_force, self.projectiledeathtype, other);
  
        remove (self);
  }
Simple merge
@@@ -1,5 -1,15 +1,15 @@@
  #ifdef REGISTER_WEAPON
- REGISTER_WEAPON(LASER, W_Laser, 0, 1, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH, 0, "laser", "laser", _("Blaster"))
+ 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")
++/* fullname  */ _("Blaster")
+ );
  #else
  #ifdef SVQC
  void(float imp) W_SwitchWeapon;
@@@ -28,14 -19,13 +38,15 @@@ void W_Laser_Touch(
  {
        PROJECTILE_TOUCH;
  
-       self.event_damage = SUB_Null;
+       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);
 +      
 +      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, 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);
 +              RadiusDamage(self, self.realowner, autocvar_g_balance_laser_primary_damage, autocvar_g_balance_laser_primary_edgedamage, autocvar_g_balance_laser_primary_radius, world, world, autocvar_g_balance_laser_primary_force, self.projectiledeathtype, other);
  
 -      remove (self);
 +      remove(self);
  }
  
  void W_Laser_Think()
        CSQCProjectile(self, TRUE, PROJECTILE_LASER, TRUE);
  }
  
 -void W_Laser_Attack (float issecondary)
 +
 +float W_Laser_Shockwave_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 = (autocvar_g_balance_laser_shockwave_spread_min * (1 - spreadlimit) + autocvar_g_balance_laser_shockwave_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_Laser_Shockwave_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_Laser_Shockwave_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_Laser_Shockwave_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_Laser_Shockwave_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;
 +}
 +
 +#define PLAYER_CENTER(ent) (ent.origin + ((ent.classname == "player") ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5)))
 +
 +entity shockwave_hit[32];
 +float shockwave_hit_damage[32];
 +vector shockwave_hit_force[32];
 +
 +float W_Laser_Shockwave_CheckHit(float queue, entity head, vector final_force, float final_damage)
 +{
 +      if not(head) { return FALSE; }
 +      float i;
 +
 +      ++queue;
 +      
 +      for(i = 1; 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_Laser_Shockwave()
 +{
 +      // declarations
 +      float multiplier, multiplier_from_accuracy, multiplier_from_distance;
-       float final_damage, final_spread;
++      float final_damage; //, final_spread;
 +      vector final_force, center, vel;
 +      entity head, next;
 +
-       float i, queue;
++      float i, queue = 0;
 +      
 +      // set up the shot direction
 +      W_SetupShot(self, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_shockwave_damage);
 +      vector attack_endpos = (w_shotorg + (w_shotdir * autocvar_g_balance_laser_shockwave_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;
++      //entity transform = WarpZone_trace_transform;
 +
 +      // do the firing effect now
 +      SendCSQCShockwaveParticle(attack_endpos);
 +      Damage_DamageInfo(attack_hitpos, autocvar_g_balance_laser_shockwave_splash_damage, autocvar_g_balance_laser_shockwave_splash_edgedamage, autocvar_g_balance_laser_shockwave_splash_radius, w_shotdir * autocvar_g_balance_laser_shockwave_splash_force, WEP_LASER, 0, self);
 +
 +      // splash damage/jumping trace
 +      head = WarpZone_FindRadius(attack_hitpos, max(autocvar_g_balance_laser_shockwave_splash_radius, autocvar_g_balance_laser_shockwave_jump_radius), FALSE);
 +      while(head)
 +      {
 +              next = head.chain;
 +
 +              if(head.takedamage)
 +              {
 +                      // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
 +                      center = PLAYER_CENTER(head);
 +
 +                      float distance_to_head = vlen(attack_hitpos - head.WarpZone_findradius_nearest);
 +                      
 +                      if((head == self) && (distance_to_head <= autocvar_g_balance_laser_shockwave_jump_radius))
 +                      {
 +                              multiplier_from_accuracy = (1 - (distance_to_head ? min(1, (distance_to_head / autocvar_g_balance_laser_shockwave_jump_radius)) : 0));
 +                              multiplier_from_distance = (1 - (distance_to_hit ? min(1, (distance_to_hit / distance_to_end)) : 0));
 +                              multiplier = max(autocvar_g_balance_laser_shockwave_jump_multiplier_min, ((multiplier_from_accuracy * autocvar_g_balance_laser_shockwave_jump_multiplier_accuracy) + (multiplier_from_distance * autocvar_g_balance_laser_shockwave_jump_multiplier_distance)));
 +
 +                              final_force = ((normalize(center - attack_hitpos) * autocvar_g_balance_laser_shockwave_jump_force) * multiplier);
 +                              vel = head.velocity; vel_z = 0;
 +                              vel = normalize(vel) * bound(0, vlen(vel) / autocvar_sv_maxspeed, 1) * autocvar_g_balance_laser_shockwave_jump_force_velocitybias;
 +                              final_force = (vlen(final_force) * normalize(normalize(final_force) + vel));
 +                              final_force_z *= autocvar_g_balance_laser_shockwave_jump_force_zscale;
 +                              final_damage = (autocvar_g_balance_laser_shockwave_jump_damage * multiplier + autocvar_g_balance_laser_shockwave_jump_edgedamage * (1 - multiplier));
 +
 +                              Damage(head, self, self, final_damage, WEP_LASER, head.origin, final_force);
 +                              //print("SELF HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n");
 +                      }
 +                      else if (distance_to_head <= autocvar_g_balance_laser_shockwave_splash_radius)
 +                      {       
 +                              multiplier_from_accuracy = (1 - (distance_to_head ? min(1, (distance_to_head / autocvar_g_balance_laser_shockwave_splash_radius)) : 0));
 +                              multiplier_from_distance = (1 - (distance_to_hit ? min(1, (distance_to_hit / distance_to_end)) : 0));
 +                              multiplier = max(autocvar_g_balance_laser_shockwave_splash_multiplier_min, ((multiplier_from_accuracy * autocvar_g_balance_laser_shockwave_splash_multiplier_accuracy) + (multiplier_from_distance * autocvar_g_balance_laser_shockwave_splash_multiplier_distance)));
 +
 +                              final_force = normalize(center - (attack_hitpos - (w_shotdir * autocvar_g_balance_laser_shockwave_splash_force_forwardbias)));
 +                              //te_lightning2(world, attack_hitpos, (attack_hitpos + (final_force * 200)));
 +                              final_force = ((final_force * autocvar_g_balance_laser_shockwave_splash_force) * multiplier);
 +                              final_force_z *= autocvar_g_balance_laser_shockwave_force_zscale;
 +                              final_damage = (autocvar_g_balance_laser_shockwave_splash_damage * multiplier + autocvar_g_balance_laser_shockwave_splash_edgedamage * (1 - multiplier));
 +
 +                              if(W_Laser_Shockwave_CheckHit(queue, head, final_force, final_damage)) { ++queue; }
 +                              //print("SPLASH HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n");
 +                      }
 +              }
 +              head = next;
 +      }
 +
 +      // cone damage trace
 +      head = WarpZone_FindRadius(w_shotorg, autocvar_g_balance_laser_shockwave_distance, FALSE);
 +      while(head)
 +      {
 +              next = head.chain;
 +              
 +              if((head != self) && head.takedamage)
 +              {
 +                      // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) 
 +                      center = PLAYER_CENTER(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);
 +
 +                      vector nearest_on_line = (w_shotorg + a * w_shotdir);
 +                      vector nearest_to_attacker = WarpZoneLib_NearestPointOnBox(center + head.mins, center + head.maxs, nearest_on_line);
 +                      float distance_to_target = vlen(w_shotorg - nearest_to_attacker); // todo: use the findradius function for this
 +
 +                      if((distance_to_target <= autocvar_g_balance_laser_shockwave_distance) 
 +                              && (W_Laser_Shockwave_IsVisible(head, nearest_on_line, w_shotorg, attack_endpos)))
 +                      {
 +                              multiplier_from_accuracy = (1 - W_Laser_Shockwave_CheckSpread(nearest_to_attacker, nearest_on_line, w_shotorg, attack_endpos));
 +                              multiplier_from_distance = (1 - (distance_to_hit ? min(1, (distance_to_target / distance_to_end)) : 0));
 +                              multiplier = max(autocvar_g_balance_laser_shockwave_multiplier_min, ((multiplier_from_accuracy * autocvar_g_balance_laser_shockwave_multiplier_accuracy) + (multiplier_from_distance * autocvar_g_balance_laser_shockwave_multiplier_distance)));
 +
 +                              final_force = normalize(center - (nearest_on_line - (w_shotdir * autocvar_g_balance_laser_shockwave_force_forwardbias)));
 +                              //te_lightning2(world, nearest_on_line, (attack_hitpos + (final_force * 200)));
 +                              final_force = ((final_force * autocvar_g_balance_laser_shockwave_force) * multiplier);
 +                              final_force_z *= autocvar_g_balance_laser_shockwave_force_zscale;
 +                              final_damage = (autocvar_g_balance_laser_shockwave_damage * multiplier + autocvar_g_balance_laser_shockwave_edgedamage * (1 - multiplier));
 +
 +                              if(W_Laser_Shockwave_CheckHit(queue, head, final_force, final_damage)) { ++queue; }
 +                              //print("CONE HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n");
 +                      }
 +              }
 +              head = next;
 +      }
 +
 +      for(i = 1; i <= queue; ++i)
 +      {
 +              head = shockwave_hit[i];
 +              final_force = shockwave_hit_force[i];
 +              final_damage = shockwave_hit_damage[i];
 +              
 +              Damage(head, self, self, final_damage, WEP_LASER, head.origin, final_force);
 +              print("SHOCKWAVE by ", self.netname, ": damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force)), ".\n");
 +              
 +              shockwave_hit[i] = world;
-               shockwave_hit_force = '0 0 0';
-               shockwave_hit_damage = 0;
++              shockwave_hit_force[i] = '0 0 0';
++              shockwave_hit_damage[i] = 0;
 +      }
 +      //print("queue was ", ftos(queue), ".\n\n");
 +}
 +
 +void W_Laser_Melee_Think()
 +{
 +      // 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 = autocvar_g_balance_laser_melee_time * W_WeaponRateFactor();
 +      swing = bound(0, (self.cnt + meleetime - time) / meleetime, 10);
 +      f = ((1 - swing) * autocvar_g_balance_laser_melee_traces);
 +      
 +      // check to see if we can still continue, otherwise give up now
 +      if((self.realowner.deadflag != DEAD_NO) && autocvar_g_balance_laser_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_laser_melee_traces)) * 2 - 1);
 +              
 +              targpos = (self.realowner.origin + self.realowner.view_ofs 
 +                      + (v_forward * autocvar_g_balance_laser_melee_range)
 +                      + (v_up * swing_factor * autocvar_g_balance_laser_melee_swing_up)
 +                      + (v_right * swing_factor * autocvar_g_balance_laser_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
 +              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 = (trace_ent.classname == "player" || trace_ent.classname == "body");
 +
 +              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 || autocvar_g_balance_laser_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 = (autocvar_g_balance_laser_melee_damage * min(1, swing_factor + 1));
 +                      else
 +                              swing_damage = (autocvar_g_balance_laser_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_LASER | HITTYPE_SECONDARY, 
 +                              self.realowner.origin + self.realowner.view_ofs, 
 +                              v_forward * autocvar_g_balance_laser_melee_force);
 +                              
 +                      if(accuracy_isgooddamage(self.realowner, target_victim)) { accuracy_add(self.realowner, WEP_LASER, 0, swing_damage); }
 +                      
 +                      if(autocvar_g_balance_laser_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_Laser_Melee()
 +{
 +      sound(self, CH_WEAPON_A, "weapons/shotgun_melee.wav", VOL_BASE, ATTN_NORM);
 +      weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_laser_melee_animtime, w_ready);
 +
 +      entity meleetemp;
 +      meleetemp = spawn();
 +      meleetemp.owner = meleetemp.realowner = self;
 +      meleetemp.think = W_Laser_Melee_Think;
 +      meleetemp.nextthink = time + autocvar_g_balance_laser_melee_delay * W_WeaponRateFactor();
 +      W_SetupShot_Range(self, TRUE, 0, "", 0, autocvar_g_balance_laser_melee_damage, autocvar_g_balance_laser_melee_range);
 +}
 +
 +void W_Laser_Attack(float issecondary)
  {
        entity missile;
        vector s_forward;
        a = autocvar_g_balance_laser_primary_shotangle;
        s_forward = v_forward * cos(a * DEG2RAD) + v_up * sin(a * DEG2RAD);
  
-       if(nodamage)
-               W_SetupShot_Dir(self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, 0);
-       else if(issecondary == 1)
 -      if(issecondary == 1)
 -              W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_secondary_damage);
++      //if(nodamage)
++      //      W_SetupShot_Dir(self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, 0);
++      /*else*/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);
 +              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 = spawn();
        missile.owner = missile.realowner = self;
        missile.classname = "laserbolt";
        missile.dmg = 0;
@@@ -436,142 -213,109 +438,139 @@@ void spawnfunc_weapon_laser(void
        weapon_defaultspawnfunc(WEP_LASER);
  }
  
 -float w_laser(float req)
 +float W_Laser(float request)
  {
 -      float r1;
 -      float r2;
 -      if (req == WR_AIM)
 +      switch(request)
        {
 -              if(autocvar_g_balance_laser_secondary)
 +              case WR_AIM:
                {
 -                      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);
 +                      if((autocvar_g_balance_laser_secondary == 2) && (vlen(self.origin-self.enemy.origin) <= autocvar_g_balance_laser_melee_range))
 +                              self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE);
                        else
 -                              self.BUTTON_ATCK = bot_aim(autocvar_g_balance_laser_primary_speed, 0, autocvar_g_balance_laser_primary_lifetime, FALSE);
 +                              self.BUTTON_ATCK = bot_aim(1000000, 0, 1, FALSE);
 +                      return TRUE;
                }
 -              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)
 +              
 +              case WR_THINK:
                {
 -                      if (weapon_prepareattack(0, autocvar_g_balance_laser_primary_refire))
 +                      if(autocvar_g_balance_laser_reload_ammo && self.clip_load < 1) // forced reload
 +                              weapon_action(self.weapon, WR_RELOAD);
 +                      else if(self.BUTTON_ATCK)
                        {
 -                              W_DecreaseAmmo(ammo_none, 1, TRUE);
 +                              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 not(autocvar_g_balance_laser_primary)
 +                                              W_Laser_Shockwave();
 +                                      else
 +                                              W_Laser_Attack(FALSE);
  
 -                              if (weapon_prepareattack(0, 0))
 -                              {
 -                                      W_Laser_Attack2();
 -                                      weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_laser_secondary_animtime, w_ready);
 +                                      weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_laser_primary_animtime, w_ready);
                                }
                        }
 -                      else
 +                      else if(self.BUTTON_ATCK2)
                        {
 -                              if(self.switchweapon == WEP_LASER) // don't do this if already switching
 -                                      W_LastWeapon();
 +                              switch(autocvar_g_balance_laser_secondary)
 +                              {
 +                                      case 0: // switch to last used weapon
 +                                      {
 +                                              if(self.switchweapon == WEP_LASER) // don't do this if already switching
 +                                                      W_LastWeapon();
 +
 +                                              break;
 +                                      }
 +
 +                                      case 1: // normal projectile secondary
 +                                      {
 +                                              if(weapon_prepareattack(1, autocvar_g_balance_laser_secondary_refire))
 +                                              {
 +                                                      W_DecreaseAmmo(ammo_none, 1, TRUE);
 +                                                      W_Laser_Attack(TRUE);
 +                                                      weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_laser_secondary_animtime, w_ready);
 +                                              }
 +
 +                                              break;
 +                                      }
 +
 +                                      case 2: // melee attack secondary
 +                                      {
 +                                              if(!self.crouch) // we are not currently crouching; this fixes an exploit where your melee anim is not visible, and besides wouldn't make much sense
 +                                              if(weapon_prepareattack(1, autocvar_g_balance_laser_melee_refire))
 +                                              {
 +                                                      // attempt forcing playback of the anim by switching to another anim (that we never play) here...
 +                                                      W_Laser_Melee();
 +                                                      weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_laser_melee_animtime, w_ready);
 +                                              }
 +                                      }
 +                              }
                        }
 +                      return TRUE;
 +              }
 +              
 +              case 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");
 +                      return TRUE;
 +              }
 +              
 +              case WR_SETUP:
 +              {
 +                      weapon_setup(WEP_LASER);
 +                      self.current_ammo = ammo_none;
 +                      return TRUE;
 +              }
 +              
 +              case WR_CHECKAMMO1:
 +              case WR_CHECKAMMO2:
 +              {
 +                      return TRUE; // laser has infinite ammo
 +              }
 +              
 +              case WR_RELOAD:
 +              {
 +                      W_Reload(0, autocvar_g_balance_laser_reload_ammo, autocvar_g_balance_laser_reload_time, "weapons/reload.wav");
 +                      return TRUE;
 +              }
++              
++              case WR_SUICIDEMESSAGE:
++              {
++                      return WEAPON_LASER_SUICIDE;
++              }
++              
++              case WR_KILLMESSAGE:
++              {
++                      return WEAPON_LASER_MURDER;
+               }
        }
 -      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)
 +float W_Laser(float request)
  {
 -      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, ATTN_NORM);
 -      }
 -      else if(req == WR_PRECACHE)
 +      switch(request)
        {
 -              precache_sound("weapons/laserimpact.wav");
 +              case WR_IMPACTEFFECT:
 +              {
 +                      vector org2;
 +                      org2 = w_org + w_backoff * 6;
 +                      pointparticles(particleeffectnum("new_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_PRECACHE:
 +              {
 +                      precache_sound("weapons/laserimpact.wav");
 +                      return TRUE;
 +              }
-               case WR_SUICIDEMESSAGE:
-               {
-                       w_deathtypestring = _("%s lasered themself to hell");
-                       return TRUE;
-               }
-               case WR_KILLMESSAGE:
-               {
-                       if(w_deathtype & HITTYPE_SECONDARY)
-                               w_deathtypestring = _("%s was cut in half by %s's gauntlet"); // unchecked: SPLASH // TODO 
-                       else
-                               w_deathtypestring = _("%s was lasered to death by %s"); // unchecked: SPLASH
-                       return TRUE;
-               }
        }
 +      
        return TRUE;
  }
  #endif
@@@ -65,12 -76,12 +76,12 @@@ void W_Mine_Explode (
                        if(IsDifferentTeam(self.realowner, other))
                                if(other.deadflag == DEAD_NO)
                                        if(IsFlying(other))
-                                               AnnounceTo(self.realowner, "airshot");
+                                               Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT);
  
-       self.event_damage = SUB_Null;
+       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);
 +      RadiusDamage (self, self.realowner, autocvar_g_balance_minelayer_damage, autocvar_g_balance_minelayer_edgedamage, autocvar_g_balance_minelayer_radius, world, world, autocvar_g_balance_minelayer_force, self.projectiledeathtype, other);
  
        if (self.realowner.weapon == WEP_MINE_LAYER)
        {
@@@ -95,9 -106,9 +106,9 @@@ void W_Mine_DoRemoteExplode (
        self.takedamage = DAMAGE_NO;
  
        if(self.movetype == MOVETYPE_NONE || self.movetype == MOVETYPE_FOLLOW)
-               self.velocity = self.oldvelocity;
+               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);
 +      RadiusDamage (self, self.realowner, autocvar_g_balance_minelayer_remote_damage, autocvar_g_balance_minelayer_remote_edgedamage, autocvar_g_balance_minelayer_remote_radius, world, world, autocvar_g_balance_minelayer_remote_force, self.projectiledeathtype | HITTYPE_BOUNCE, world);
  
        if (self.realowner.weapon == WEP_MINE_LAYER)
        {
Simple merge
@@@ -23,12 -33,12 +33,12 @@@ void W_Rocket_Explode (
                        if(IsDifferentTeam(self.realowner, other))
                                if(other.deadflag == DEAD_NO)
                                        if(IsFlying(other))
-                                               AnnounceTo(self.realowner, "airshot");
+                                               Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT);
  
-       self.event_damage = SUB_Null;
+       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);
 +      RadiusDamage (self, self.realowner, autocvar_g_balance_rocketlauncher_damage, autocvar_g_balance_rocketlauncher_edgedamage, autocvar_g_balance_rocketlauncher_radius, world, world, autocvar_g_balance_rocketlauncher_force, self.projectiledeathtype, other);
  
        if (self.realowner.weapon == WEP_ROCKET_LAUNCHER)
        {
@@@ -46,10 -56,10 +56,10 @@@ void W_Rocket_DoRemoteExplode (
  {
        W_Rocket_Unregister();
  
-       self.event_damage = SUB_Null;
+       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);
 +      RadiusDamage (self, self.realowner, autocvar_g_balance_rocketlauncher_remote_damage, autocvar_g_balance_rocketlauncher_remote_edgedamage, autocvar_g_balance_rocketlauncher_remote_radius, world, world, autocvar_g_balance_rocketlauncher_remote_force, self.projectiledeathtype | HITTYPE_BOUNCE, world);
  
        if (self.realowner.weapon == WEP_ROCKET_LAUNCHER)
        {
@@@ -12,9 -22,9 +22,10 @@@ REGISTER_WEAPON
  // ============================
  void Seeker_Missile_Explode ()
  {
-       self.event_damage = SUB_Null;
+       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);
 +      RadiusDamage (self, self.realowner, autocvar_g_balance_seeker_missile_damage, autocvar_g_balance_seeker_missile_edgedamage, autocvar_g_balance_seeker_missile_radius, world, world, autocvar_g_balance_seeker_missile_force, self.projectiledeathtype, other);
 +
        remove (self);
  }
  
@@@ -223,9 -233,9 +234,9 @@@ void Seeker_Fire_Missile(vector f_diff
  // ============================
  void Seeker_Flac_Explode ()
  {
-       self.event_damage = SUB_Null;
+       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);
 +      RadiusDamage (self, self.realowner, autocvar_g_balance_seeker_flac_damage, autocvar_g_balance_seeker_flac_edgedamage, autocvar_g_balance_seeker_flac_radius, world, world, autocvar_g_balance_seeker_flac_force, self.projectiledeathtype, other);
  
        remove (self);
  }
Simple merge