]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge remote-tracking branch 'origin/master' into samual/weapons
authorSamual Lenks <samual@xonotic.org>
Thu, 5 Sep 2013 18:19:01 +0000 (14:19 -0400)
committerSamual Lenks <samual@xonotic.org>
Thu, 5 Sep 2013 18:19:01 +0000 (14:19 -0400)
Conflicts:
qcsrc/client/hook.qc
qcsrc/common/constants.qh
qcsrc/common/weapons/w_electro.qc
qcsrc/common/weapons/w_fireball.qc
qcsrc/common/weapons/w_seeker.qc
qcsrc/common/weapons/w_shockwave.qc
qcsrc/common/weapons/weapons.qh
qcsrc/server/cl_weapons.qc
qcsrc/server/defs.qh
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/gamemode_nexball.qc
qcsrc/server/t_items.qc
qcsrc/server/tturrets/units/unit_walker.qc
qcsrc/server/w_crylink.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_nex.qc
qcsrc/server/w_rifle.qc
qcsrc/server/w_rocketlauncher.qc
qcsrc/server/w_uzi.qc
qcsrc/server/weapons/tracing.qc
qcsrc/server/weapons/weaponsystem.qc

51 files changed:
1  2 
balanceXonotic.cfg
oldbalanceXPM.cfg
qcsrc/client/Main.qc
qcsrc/client/View.qc
qcsrc/client/damage.qc
qcsrc/client/hook.qc
qcsrc/client/hud.qc
qcsrc/client/weapons/projectile.qc
qcsrc/common/constants.qh
qcsrc/common/util.qc
qcsrc/common/util.qh
qcsrc/common/weapons/w_electro.qc
qcsrc/common/weapons/w_fireball.qc
qcsrc/common/weapons/w_hook.qc
qcsrc/common/weapons/w_porto.qc
qcsrc/common/weapons/w_seeker.qc
qcsrc/common/weapons/w_shockwave.qc
qcsrc/common/weapons/weapons.qc
qcsrc/common/weapons/weapons.qh
qcsrc/server/bot/havocbot/havocbot.qc
qcsrc/server/cheats.qc
qcsrc/server/cl_client.qc
qcsrc/server/cl_impulse.qc
qcsrc/server/cl_physics.qc
qcsrc/server/cl_player.qc
qcsrc/server/defs.qh
qcsrc/server/func_breakable.qc
qcsrc/server/g_damage.qc
qcsrc/server/g_hook.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/gamemode_nexball.qc
qcsrc/server/mutators/mutator_minstagib.qc
qcsrc/server/mutators/mutator_nades.qc
qcsrc/server/mutators/mutator_nix.qc
qcsrc/server/mutators/mutator_touchexplode.qc
qcsrc/server/t_items.qc
qcsrc/server/t_items.qh
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/weapons/common.qc
qcsrc/server/weapons/selection.qc
qcsrc/server/weapons/spawning.qc
qcsrc/server/weapons/throwing.qc
qcsrc/server/weapons/tracing.qc
qcsrc/server/weapons/weaponsystem.qc

Simple merge
index 8f62776d9c134a2a0418d5844021911569cf5c8e,0000000000000000000000000000000000000000..3b6dd989714be115bd091f71dae466309e926e11
mode 100644,000000..100644
--- /dev/null
@@@ -1,781 -1,0 +1,781 @@@
- set g_balance_electro_primary_comboradius 200
 +g_mod_balance XPM
 +
 +// {{{ starting gear
 +set g_start_weapon_laser -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
 +set g_start_weapon_shotgun -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
 +set g_start_weapon_uzi -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
 +set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
 +set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
 +set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
 +set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
 +set g_start_weapon_hagar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" // UNTIL IT CAN BE REMOVED FROM CODE
 +set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
 +set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
 +set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
 +set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
 +set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
 +set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
 +set g_balance_health_start 100
 +set g_balance_armor_start 0
 +set g_start_ammo_shells 15
 +set g_start_ammo_nails 0
 +set g_start_ammo_rockets 0
 +set g_start_ammo_cells 0
 +set g_start_ammo_fuel 0
 +set g_warmup_start_health 100 "starting values when being in warmup-stage"
 +set g_warmup_start_armor 100 "starting values when being in warmup-stage"
 +set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage"
 +set g_warmup_start_ammo_nails 160 "starting values when being in warmup-stage"
 +set g_warmup_start_ammo_rockets 80 "starting values when being in warmup-stage"
 +set g_warmup_start_ammo_cells 90 "starting values when being in warmup-stage"
 +set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage"
 +set g_lms_start_health 200
 +set g_lms_start_armor 200
 +set g_lms_start_ammo_shells 60
 +set g_lms_start_ammo_nails 320
 +set g_lms_start_ammo_rockets 160
 +set g_lms_start_ammo_cells 180
 +set g_lms_start_ammo_fuel 0
 +set g_balance_nix_roundtime 25
 +set g_balance_nix_incrtime 1.6
 +set g_balance_nix_ammo_shells 60
 +set g_balance_nix_ammo_nails 320
 +set g_balance_nix_ammo_rockets 160
 +set g_balance_nix_ammo_cells 180
 +set g_balance_nix_ammo_fuel 0
 +set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume
 +set g_balance_nix_ammoincr_nails 6
 +set g_balance_nix_ammoincr_rockets 2
 +set g_balance_nix_ammoincr_cells 2
 +set g_balance_nix_ammoincr_fuel 2
 +// }}}
 +
 +// {{{ pickup items
 +set g_pickup_ammo_anyway 1
 +set g_pickup_weapons_anyway 1
 +set g_pickup_shells 15
 +set g_pickup_shells_weapon 15
 +set g_pickup_shells_max 60
 +set g_pickup_nails 80
 +set g_pickup_nails_weapon 80
 +set g_pickup_nails_max 320
 +set g_pickup_rockets 40
 +set g_pickup_rockets_weapon 40
 +set g_pickup_rockets_max 160
 +set g_pickup_cells 30
 +set g_pickup_cells_weapon 30
 +set g_pickup_cells_max 180
 +set g_pickup_fuel 50
 +set g_pickup_fuel_weapon 50
 +set g_pickup_fuel_jetpack 100
 +set g_pickup_fuel_max 100
 +set g_pickup_armorsmall 5
 +set g_pickup_armorsmall_max 200
 +set g_pickup_armorsmall_anyway 0
 +set g_pickup_armormedium 25
 +set g_pickup_armormedium_max 100
 +set g_pickup_armormedium_anyway 0
 +set g_pickup_armorbig 50
 +set g_pickup_armorbig_max 100
 +set g_pickup_armorbig_anyway 0
 +set g_pickup_armorlarge 100
 +set g_pickup_armorlarge_max 200
 +set g_pickup_armorlarge_anyway 0
 +set g_pickup_healthsmall 5
 +set g_pickup_healthsmall_max 200
 +set g_pickup_healthsmall_anyway 0
 +set g_pickup_healthmedium 25
 +set g_pickup_healthmedium_max 100
 +set g_pickup_healthmedium_anyway 0
 +set g_pickup_healthlarge 50
 +set g_pickup_healthlarge_max 100
 +set g_pickup_healthlarge_anyway 0
 +set g_pickup_healthmega 100
 +set g_pickup_healthmega_max 200
 +set g_pickup_healthmega_anyway 0
 +set g_pickup_respawntime_short 15
 +set g_pickup_respawntime_medium 20
 +set g_pickup_respawntime_long 30
 +set g_pickup_respawntime_powerup 120
 +set g_pickup_respawntime_weapon 10
 +set g_pickup_respawntime_superweapon 120
 +set g_pickup_respawntime_ammo 15
 +set g_pickup_respawntimejitter_short 0
 +set g_pickup_respawntimejitter_medium 0
 +set g_pickup_respawntimejitter_long 0
 +set g_pickup_respawntimejitter_powerup 30
 +set g_pickup_respawntimejitter_weapon 0
 +set g_pickup_respawntimejitter_superweapon 10
 +set g_pickup_respawntimejitter_ammo 0
 +// }}}
 +
 +// {{{ regen/rot
 +set g_balance_health_regen 0.08
 +set g_balance_health_regenlinear 0.5
 +set g_balance_pause_health_regen 5
 +set g_balance_pause_health_regen_spawn 0
 +set g_balance_health_rot 0.03
 +set g_balance_health_rotlinear 0.75
 +set g_balance_pause_health_rot 1
 +set g_balance_pause_health_rot_spawn 5
 +set g_balance_health_regenstable 100
 +set g_balance_health_rotstable 100
 +set g_balance_health_limit 999
 +set g_balance_armor_regen 0
 +set g_balance_armor_regenlinear 0
 +set g_balance_armor_rot 0.03
 +set g_balance_armor_rotlinear 0.75
 +set g_balance_pause_armor_rot 1
 +set g_balance_pause_armor_rot_spawn 5
 +set g_balance_armor_regenstable 100
 +set g_balance_armor_rotstable 100
 +set g_balance_armor_limit 999
 +set g_balance_armor_blockpercent 0.6
 +set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)"
 +set g_balance_fuel_regenlinear 0
 +set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter
 +set g_balance_fuel_rot 0.05
 +set g_balance_fuel_rotlinear 0
 +set g_balance_pause_fuel_rot 5
 +set g_balance_pause_fuel_rot_spawn 10
 +set g_balance_fuel_regenstable 50
 +set g_balance_fuel_rotstable 100
 +set g_balance_fuel_limit 999
 +// }}}
 +
 +// {{{ misc
 +set g_balance_selfdamagepercent 0.65
 +set g_weaponspeedfactor 1 "weapon projectile speed multiplier"
 +set g_weaponratefactor 1 "weapon fire rate multiplier"
 +set g_weapondamagefactor 1 "weapon damage multiplier"
 +set g_weaponforcefactor 1 "weapon force multiplier"
 +set g_weaponspreadfactor 1 "weapon spread multiplier"
 +set g_balance_firetransfer_time 0.9
 +set g_balance_firetransfer_damage 0.8
 +set g_throughfloor_damage 0.75
 +set g_throughfloor_force 0.75
 +set g_projectiles_damage 1
 +// possible values:
 +// -2: absolutely no damage to projectiles (no exceptions)
 +// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines)
 +// 0: only damage from contents (lava/slime) or exceptions
 +// 1: only self damage or damage from contents or exceptions
 +// 2: allow all damage to projectiles normally
 +set g_projectiles_keep_owner 0
 +set g_projectiles_newton_style 0
 +// possible values:
 +// 0: absolute velocity projectiles (like Quake)
 +// 1: relative velocity projectiles, "Newtonian" (like Tribes 2)
 +// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard)
 +set g_projectiles_newton_style_2_minfactor 0.8
 +set g_projectiles_newton_style_2_maxfactor 1.5
 +set g_projectiles_spread_style 7
 +// possible values:
 +// 0: forward + solid sphere (like Quake) - varies velocity
 +// 1: forward + flattened solid sphere
 +// 2: forward + solid circle
 +// 3: forward + normal distribution 3D - varies velocity
 +// 4: forward + normal distribution on a plane
 +// 5: forward + circle with 1-r falloff
 +// 6: forward + circle with 1-r^2 falloff
 +// 7: forward + circle with (1-r)(2-r) falloff
 +set g_balance_falldamage_deadminspeed 250
 +set g_balance_falldamage_minspeed 900
 +set g_balance_falldamage_factor 0.20
 +set g_balance_falldamage_maxdamage 40
 +set g_balance_damagepush_speedfactor 2.5
 +set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage
 +set g_balance_contents_drowndelay 10 // time under water before a player begins drowning
 +set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning
 +set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava
 +set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime
 +set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime
 +set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap"
 +// }}}
 +
 +// {{{ powerups
 +set g_balance_powerup_invincible_takedamage 0.25 // only 1/4th damage is taken
 +set g_balance_powerup_invincible_time 30
 +set g_balance_powerup_strength_damage 3
 +set g_balance_powerup_strength_force 3
 +set g_balance_powerup_strength_time 30
 +set g_balance_powerup_strength_selfdamage 1.5
 +set g_balance_powerup_strength_selfforce 1.5
 +set g_balance_superweapons_time 30
 +// }}}
 +
 +// {{{ jetpack/hook
 +set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack"
 +set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction"
 +set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)"
 +set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction"
 +set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction"
 +set g_jetpack_fuel 8 "fuel per second for jetpack"
 +set g_jetpack_attenuation 2 "jetpack sound attenuation"
 +
 +set g_grappling_hook_tarzan 2 // 2: can also pull players
 +set g_balance_grapplehook_speed_fly 1800
 +set g_balance_grapplehook_speed_pull 2000
 +set g_balance_grapplehook_force_rubber 2000
 +set g_balance_grapplehook_force_rubber_overstretch 1000
 +set g_balance_grapplehook_length_min 50
 +set g_balance_grapplehook_stretch 50
 +set g_balance_grapplehook_airfriction 0.2
 +set g_balance_grapplehook_health 50
 +set g_balance_grapplehook_damagedbycontents 1
 +// }}}
 +
 +// {{{ 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 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
 +set g_balance_laser_primary_radius 70
 +set g_balance_laser_primary_speed 6000
 +set g_balance_laser_primary_spread 0
 +set g_balance_laser_primary_refire 0.7
 +set g_balance_laser_primary_animtime 0.2
 +set g_balance_laser_primary_lifetime 5
 +set g_balance_laser_primary_shotangle 0
 +set g_balance_laser_primary_delay 0
 +set g_balance_laser_primary_force_zscale 1.2
 +set g_balance_laser_primary_force_velocitybias 0
 +set g_balance_laser_primary_force_other_scale 1
 +
 +set g_balance_laser_secondary 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
 +set g_balance_laser_secondary_radius 70
 +set g_balance_laser_secondary_speed 12000
 +set g_balance_laser_secondary_spread 0
 +set g_balance_laser_secondary_refire 0.7
 +set g_balance_laser_secondary_animtime 0.2
 +set g_balance_laser_secondary_lifetime 5
 +set g_balance_laser_secondary_shotangle -90
 +set g_balance_laser_secondary_delay 0
 +set g_balance_laser_secondary_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
 +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.2
 +set g_balance_shotgun_switchdelay_raise 0.2
 +set g_balance_shotgun_reload_ammo 0 //default: 5
 +set g_balance_shotgun_reload_time 2
 +// }}}
 +// {{{ uzi
 +set g_balance_uzi_mode 1                              // Activates varible spread for sustained & burst mode secondary
 +set g_balance_uzi_spread_min 0.02
 +set g_balance_uzi_spread_max 0.05
 +set g_balance_uzi_spread_add 0.012
 +
 +set g_balance_uzi_burst 3                             // # of bullets in a burst (if set to 2 or more)
 +set g_balance_uzi_burst_animtime 0.3
 +set g_balance_uzi_burst_refire 0.06           // refire between burst bullets
 +set g_balance_uzi_burst_refire2 0.45  // refire after burst
 +set g_balance_uzi_burst_spread 0.02
 +set g_balance_uzi_burst_damage 25
 +set g_balance_uzi_burst_force 20
 +set g_balance_uzi_burst_ammo 3
 +
 +set g_balance_uzi_first 1
 +set g_balance_uzi_first_damage 14
 +set g_balance_uzi_first_force 5
 +set g_balance_uzi_first_spread 0.03
 +set g_balance_uzi_first_refire 0.125
 +set g_balance_uzi_first_ammo 1
 +
 +set g_balance_uzi_sustained_damage 10 // 100 dps
 +set g_balance_uzi_sustained_force 5
 +set g_balance_uzi_sustained_spread 0.03
 +set g_balance_uzi_sustained_refire 0.1
 +set g_balance_uzi_sustained_ammo 1
 +
 +set g_balance_uzi_speed 18000
 +set g_balance_uzi_bulletconstant 115 // 13.1qu
 +
 +set g_balance_uzi_switchdelay_drop 0.2
 +set g_balance_uzi_switchdelay_raise 0.2
 +
 +set g_balance_uzi_reload_ammo 60 //default: 30
 +set g_balance_uzi_reload_time 2
 +// }}}
 +// {{{ mortar
 +set g_balance_grenadelauncher_primary_type 0
 +set g_balance_grenadelauncher_primary_damage 50
 +set g_balance_grenadelauncher_primary_edgedamage 25
 +set g_balance_grenadelauncher_primary_force 250
 +set g_balance_grenadelauncher_primary_radius 120
 +set g_balance_grenadelauncher_primary_speed 1900
 +set g_balance_grenadelauncher_primary_speed_up 225
 +set g_balance_grenadelauncher_primary_speed_z 0
 +set g_balance_grenadelauncher_primary_spread 0
 +set g_balance_grenadelauncher_primary_lifetime 5
 +set g_balance_grenadelauncher_primary_lifetime2 1
 +set g_balance_grenadelauncher_primary_refire 0.8
 +set g_balance_grenadelauncher_primary_animtime 0.3
 +set g_balance_grenadelauncher_primary_ammo 2
 +set g_balance_grenadelauncher_primary_health 15
 +set g_balance_grenadelauncher_primary_damageforcescale 0
 +set g_balance_grenadelauncher_primary_remote_minbouncecnt 0
 +
 +set g_balance_grenadelauncher_secondary_type 1
 +set g_balance_grenadelauncher_secondary_damage 60
 +set g_balance_grenadelauncher_secondary_edgedamage 30
 +set g_balance_grenadelauncher_secondary_force 250
 +set g_balance_grenadelauncher_secondary_radius 120
 +set g_balance_grenadelauncher_secondary_speed 1400
 +set g_balance_grenadelauncher_secondary_speed_up 150
 +set g_balance_grenadelauncher_secondary_speed_z 0
 +set g_balance_grenadelauncher_secondary_spread 0
 +set g_balance_grenadelauncher_secondary_lifetime 5
 +set g_balance_grenadelauncher_secondary_lifetime_bounce 0.5
 +set g_balance_grenadelauncher_secondary_lifetime_stick 0
 +set g_balance_grenadelauncher_secondary_refire 0.7
 +set g_balance_grenadelauncher_secondary_animtime 0.3
 +set g_balance_grenadelauncher_secondary_ammo 2
 +set g_balance_grenadelauncher_secondary_health 30
 +set g_balance_grenadelauncher_secondary_damageforcescale 4
 +set g_balance_grenadelauncher_secondary_remote_detonateprimary 0
 +
 +set g_balance_grenadelauncher_bouncefactor 0.5
 +set g_balance_grenadelauncher_bouncestop 0.075
 +
 +set g_balance_grenadelauncher_switchdelay_drop 0.2
 +set g_balance_grenadelauncher_switchdelay_raise 0.2
 +
 +set g_balance_grenadelauncher_reload_ammo 0 //default: 12
 +set g_balance_grenadelauncher_reload_time 2
 +// }}}
 +// {{{ electro
 +set g_balance_electro_lightning 0
 +set g_balance_electro_primary_damage 40
 +set g_balance_electro_primary_edgedamage 20
 +set g_balance_electro_primary_force 200
 +set g_balance_electro_primary_force_up 0
 +set g_balance_electro_primary_radius 100
++set g_balance_electro_primary_comboradius 300
 +set g_balance_electro_primary_speed 2500
 +set g_balance_electro_primary_spread 0
 +set g_balance_electro_primary_lifetime 5
 +set g_balance_electro_primary_refire 0.6
 +set g_balance_electro_primary_animtime 0.3
 +set g_balance_electro_primary_ammo 4
 +set g_balance_electro_primary_range 0
 +set g_balance_electro_primary_falloff_mindist 255 // 0.3 * radius
 +set g_balance_electro_primary_falloff_maxdist 850
 +set g_balance_electro_primary_falloff_halflifedist 425
 +set g_balance_electro_secondary_damage 40
 +set g_balance_electro_secondary_edgedamage 20
 +set g_balance_electro_secondary_force 50
 +set g_balance_electro_secondary_radius 150
 +set g_balance_electro_secondary_speed 1000
 +set g_balance_electro_secondary_speed_up 200
 +set g_balance_electro_secondary_speed_z 0
 +set g_balance_electro_secondary_spread 0.04
 +set g_balance_electro_secondary_lifetime 4
 +set g_balance_electro_secondary_refire 0.2
 +set g_balance_electro_secondary_refire2 1.6
 +set g_balance_electro_secondary_animtime 0.2
 +set g_balance_electro_secondary_ammo 2
 +set g_balance_electro_secondary_health 5
 +set g_balance_electro_secondary_damageforcescale 4
 +set g_balance_electro_secondary_damagedbycontents 1
 +set g_balance_electro_secondary_count 3
 +set g_balance_electro_secondary_bouncefactor 0.3
 +set g_balance_electro_secondary_bouncestop 0.05
 +set g_balance_electro_combo_damage 50
 +set g_balance_electro_combo_edgedamage 25
 +set g_balance_electro_combo_force 120
 +set g_balance_electro_combo_radius 150
 +set g_balance_electro_combo_comboradius 300
 +set g_balance_electro_combo_speed 2000
 +set g_balance_electro_combo_safeammocheck 1
 +set g_balance_electro_switchdelay_drop 0.2
 +set g_balance_electro_switchdelay_raise 0.2
 +set g_balance_electro_reload_ammo 0 //default: 20
 +set g_balance_electro_reload_time 2
 +// }}}
 +// {{{ lightning
 +set g_balance_lightning_primary_ammo 5
 +set g_balance_lightning_primary_animtime 0.2
 +set g_balance_lightning_primary_damage 100
 +set g_balance_lightning_primary_edgedamage 0
 +set g_balance_lightning_primary_falloff_mindist 0
 +set g_balance_lightning_primary_falloff_maxdist 0
 +set g_balance_lightning_primary_falloff_halflifedist 0
 +set g_balance_lightning_primary_force 425
 +set g_balance_lightning_primary_lifetime 0
 +set g_balance_lightning_primary_radius 850
 +set g_balance_lightning_primary_range 800
 +set g_balance_lightning_primary_refire 0.4
 +set g_balance_lightning_primary_speed 0
 +set g_balance_lightning_primary_spread 0
 +set g_balance_lightning_secondary_ammo 5
 +set g_balance_lightning_secondary_animtime 0.5
 +set g_balance_lightning_secondary_damage 100
 +set g_balance_lightning_secondary_damageforcescale 4
 +set g_balance_lightning_secondary_edgedamage 80
 +set g_balance_lightning_secondary_flyingdamage 1
 +set g_balance_lightning_secondary_flyingforce -80
 +set g_balance_lightning_secondary_flyingradius 200
 +set g_balance_lightning_secondary_force -200
 +set g_balance_lightning_secondary_health 1
 +set g_balance_lightning_secondary_lifetime 30
 +set g_balance_lightning_secondary_radius 300
 +set g_balance_lightning_secondary_refire 5
 +set g_balance_lightning_secondary_speed 600
 +// }}}
 +// {{{ crylink 
 +set g_balance_crylink_primary_damage 12
 +set g_balance_crylink_primary_edgedamage 6
 +set g_balance_crylink_primary_force -50
 +set g_balance_crylink_primary_radius 80
 +set g_balance_crylink_primary_speed 2000
 +set g_balance_crylink_primary_spread 0.08
 +set g_balance_crylink_primary_shots 6
 +set g_balance_crylink_primary_bounces 1
 +set g_balance_crylink_primary_refire 0.7
 +set g_balance_crylink_primary_animtime 0.3
 +set g_balance_crylink_primary_ammo 3
 +set g_balance_crylink_primary_bouncedamagefactor 0.5
 +set g_balance_crylink_primary_joindelay 0.1
 +set g_balance_crylink_primary_joinspread 0.2
 +set g_balance_crylink_primary_joinexplode 1
 +set g_balance_crylink_primary_joinexplode_damage 0
 +set g_balance_crylink_primary_joinexplode_edgedamage 0
 +set g_balance_crylink_primary_joinexplode_radius 0
 +set g_balance_crylink_primary_joinexplode_force 0
 +set g_balance_crylink_primary_linkexplode 1
 +
 +set g_balance_crylink_primary_middle_lifetime 5 // range: 35000 full, fades to 70000
 +set g_balance_crylink_primary_middle_fadetime 5
 +set g_balance_crylink_primary_other_lifetime 5
 +set g_balance_crylink_primary_other_fadetime 5
 +
 +set g_balance_crylink_secondary 1
 +set g_balance_crylink_secondary_damage 10
 +set g_balance_crylink_secondary_edgedamage 5
 +set g_balance_crylink_secondary_force -250
 +set g_balance_crylink_secondary_radius 100
 +set g_balance_crylink_secondary_speed 3000
 +set g_balance_crylink_secondary_spread 0.01
 +set g_balance_crylink_secondary_spreadtype 1
 +set g_balance_crylink_secondary_shots 5
 +set g_balance_crylink_secondary_bounces 0
 +set g_balance_crylink_secondary_refire 0.7
 +set g_balance_crylink_secondary_animtime 0.2
 +set g_balance_crylink_secondary_ammo 2
 +set g_balance_crylink_secondary_bouncedamagefactor 0.5
 +set g_balance_crylink_secondary_joindelay 0
 +set g_balance_crylink_secondary_joinspread 0
 +set g_balance_crylink_secondary_joinexplode 0
 +set g_balance_crylink_secondary_joinexplode_damage 0
 +set g_balance_crylink_secondary_joinexplode_edgedamage 0
 +set g_balance_crylink_secondary_joinexplode_radius 0
 +set g_balance_crylink_secondary_joinexplode_force 0
 +set g_balance_crylink_secondary_linkexplode 1
 +
 +set g_balance_crylink_secondary_middle_lifetime 5 // range: 35000 full, fades to 70000
 +set g_balance_crylink_secondary_middle_fadetime 5
 +set g_balance_crylink_secondary_line_lifetime 5
 +set g_balance_crylink_secondary_line_fadetime 5
 +
 +set g_balance_crylink_switchdelay_drop 0.2
 +set g_balance_crylink_switchdelay_raise 0.2
 +
 +set g_balance_crylink_reload_ammo 0 //default: 10
 +set g_balance_crylink_reload_time 2
 +// }}}
 +// {{{ nex
 +set g_balance_nex_primary_damage 80
 +set g_balance_nex_primary_force 400
 +set g_balance_nex_primary_refire 1.5
 +set g_balance_nex_primary_animtime 0.6
 +set g_balance_nex_primary_ammo 6
 +set g_balance_nex_primary_damagefalloff_mindist 0 // 1000    For tZork ;3
 +set g_balance_nex_primary_damagefalloff_maxdist 0 // 3000
 +set g_balance_nex_primary_damagefalloff_halflife 0 // 1500
 +set g_balance_nex_primary_damagefalloff_forcehalflife 0 // 1500
 +
 +set g_balance_nex_secondary 0
 +set g_balance_nex_secondary_charge 0
 +set g_balance_nex_secondary_charge_rate 0.1
 +set g_balance_nex_secondary_chargepool 0
 +set g_balance_nex_secondary_chargepool_regen 0.15
 +set g_balance_nex_secondary_chargepool_pause_regen 1
 +set g_balance_nex_secondary_chargepool_pause_health_regen 1
 +set g_balance_nex_secondary_damage 0
 +set g_balance_nex_secondary_force 0
 +set g_balance_nex_secondary_refire 0
 +set g_balance_nex_secondary_animtime 0
 +set g_balance_nex_secondary_ammo 2
 +set g_balance_nex_secondary_damagefalloff_mindist 0
 +set g_balance_nex_secondary_damagefalloff_maxdist 0
 +set g_balance_nex_secondary_damagefalloff_halflife 0
 +set g_balance_nex_secondary_damagefalloff_forcehalflife 0
 +
 +set g_balance_nex_charge 1
 +set g_balance_nex_charge_mindmg 40
 +set g_balance_nex_charge_start 0.5
 +set g_balance_nex_charge_rate 0.4
 +set g_balance_nex_charge_animlimit 0.5
 +set g_balance_nex_charge_limit 1
 +set g_balance_nex_charge_rot_rate 0
 +set g_balance_nex_charge_rot_pause 0 // Dont rot down until this long after release of charge button
 +set g_balance_nex_charge_shot_multiplier 0
 +set g_balance_nex_charge_velocity_rate 0
 +set g_balance_nex_charge_minspeed 400
 +set g_balance_nex_charge_maxspeed 800
 +
 +set g_balance_nex_switchdelay_drop 0.3
 +set g_balance_nex_switchdelay_raise 0.25
 +
 +set g_balance_nex_reload_ammo 0 //default: 25
 +set g_balance_nex_reload_time 2
 +// }}}
 +// {{{ minstanex
 +set g_balance_minstanex_refire 1
 +set g_balance_minstanex_animtime 0.3
 +set g_balance_minstanex_ammo 10
 +set g_balance_minstanex_laser_ammo 0
 +set g_balance_minstanex_laser_animtime 0.3
 +set g_balance_minstanex_laser_refire 0.7
 +set g_balance_minstanex_switchdelay_drop 0.2
 +set g_balance_minstanex_switchdelay_raise 0.2
 +set g_balance_minstanex_reload_ammo 0 //default: 50
 +set g_balance_minstanex_reload_time 2
 +// }}}
 +// {{{ hagar
 +set g_balance_hagar_primary_damage 25
 +set g_balance_hagar_primary_edgedamage 12.5
 +set g_balance_hagar_primary_force 100
 +set g_balance_hagar_primary_health 15
 +set g_balance_hagar_primary_damageforcescale 0
 +set g_balance_hagar_primary_radius 65
 +set g_balance_hagar_primary_spread 0.03
 +set g_balance_hagar_primary_speed 2500
 +set g_balance_hagar_primary_lifetime 5
 +set g_balance_hagar_primary_refire 0.16667 // 6 rockets per second
 +set g_balance_hagar_primary_ammo 1
 +set g_balance_hagar_secondary 1
 +set g_balance_hagar_secondary_load 1
 +set g_balance_hagar_secondary_load_speed 0.5
 +set g_balance_hagar_secondary_load_spread 0.075
 +set g_balance_hagar_secondary_load_spread_bias 0.5
 +set g_balance_hagar_secondary_load_max 4
 +set g_balance_hagar_secondary_load_hold 4
 +set g_balance_hagar_secondary_load_releasedeath 0
 +set g_balance_hagar_secondary_load_abort 0
 +set g_balance_hagar_secondary_load_linkexplode 0
 +set g_balance_hagar_secondary_load_animtime 0.2
 +set g_balance_hagar_secondary_damage 40
 +set g_balance_hagar_secondary_edgedamage 20
 +set g_balance_hagar_secondary_force 75
 +set g_balance_hagar_secondary_health 15
 +set g_balance_hagar_secondary_damageforcescale 0
 +set g_balance_hagar_secondary_radius 80
 +set g_balance_hagar_secondary_spread 0.05
 +set g_balance_hagar_secondary_speed 2000
 +set g_balance_hagar_secondary_lifetime_min 10
 +set g_balance_hagar_secondary_lifetime_rand 0
 +set g_balance_hagar_secondary_refire 0.5
 +set g_balance_hagar_secondary_ammo 1
 +set g_balance_hagar_switchdelay_drop 0.2
 +set g_balance_hagar_switchdelay_raise 0.2
 +set g_balance_hagar_reload_ammo 0 //default: 25
 +set g_balance_hagar_reload_time 2
 +// }}}
 +// {{{ rocketlauncher
 +set g_balance_rocketlauncher_damage 80
 +set g_balance_rocketlauncher_edgedamage 40
 +set g_balance_rocketlauncher_force 450
 +set g_balance_rocketlauncher_radius 110
 +set g_balance_rocketlauncher_speed 1300
 +set g_balance_rocketlauncher_speedaccel 1300
 +set g_balance_rocketlauncher_speedstart 1000
 +set g_balance_rocketlauncher_lifetime 10
 +set g_balance_rocketlauncher_refire 1.1
 +set g_balance_rocketlauncher_animtime 0.4
 +set g_balance_rocketlauncher_ammo 4
 +set g_balance_rocketlauncher_health 30 // 30 // 5 hitpoints above maximum laser value -- this way lasers can't blow it up, but grenadelauncher still can most the time.
 +set g_balance_rocketlauncher_damageforcescale 1 // low damage force scale so that it can still be affected by other hits, but not so much that it does a 90 degree turn
 +set g_balance_rocketlauncher_detonatedelay 0.02 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time
 +set g_balance_rocketlauncher_guiderate 90 // max degrees per second
 +set g_balance_rocketlauncher_guideratedelay 0.01 // immediate
 +set g_balance_rocketlauncher_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic)
 +set g_balance_rocketlauncher_guidedelay 0.2 // delay before guiding kicks in
 +set g_balance_rocketlauncher_guidestop 0 // stop guiding when firing again
 +set g_balance_rocketlauncher_remote_damage 70
 +set g_balance_rocketlauncher_remote_edgedamage 35
 +set g_balance_rocketlauncher_remote_radius 110
 +set g_balance_rocketlauncher_remote_force 400
 +set g_balance_rocketlauncher_switchdelay_drop 0.2
 +set g_balance_rocketlauncher_switchdelay_raise 0.2
 +set g_balance_rocketlauncher_reload_ammo 0 //default: 25
 +set g_balance_rocketlauncher_reload_time 2
 +// }}}
 +// {{{ porto
 +set g_balance_porto_primary_refire 1.5
 +set g_balance_porto_primary_animtime 0.3
 +set g_balance_porto_primary_speed 1000
 +set g_balance_porto_primary_lifetime 5
 +set g_balance_porto_secondary 1
 +set g_balance_porto_secondary_refire 1.5
 +set g_balance_porto_secondary_animtime 0.3
 +set g_balance_porto_secondary_speed 1000
 +set g_balance_porto_secondary_lifetime 5
 +set g_balance_porto_switchdelay_drop 0.2
 +set g_balance_porto_switchdelay_raise 0.2
 +set g_balance_portal_health 200 // these get recharged whenever the portal is used
 +set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used
 +// }}}
 +// {{{ hook
 +set g_balance_hook_primary_fuel 5 // hook monkeys set 0
 +set g_balance_hook_primary_refire 0 // hook monkeys set 0
 +set g_balance_hook_primary_animtime 0.3 // good shoot anim
 +set g_balance_hook_primary_hooked_time_max 0 // infinite
 +set g_balance_hook_primary_hooked_time_free 2 // 2s being hooked are free
 +set g_balance_hook_primary_hooked_fuel 5 // fuel per second hooked
 +set g_balance_hook_secondary_damage 25 // not much
 +set g_balance_hook_secondary_edgedamage 5 // not much
 +set g_balance_hook_secondary_radius 500 // LOTS
 +set g_balance_hook_secondary_force -2000 // LOTS
 +set g_balance_hook_secondary_ammo 30 // a whole pack
 +set g_balance_hook_secondary_lifetime 5 // infinite
 +set g_balance_hook_secondary_speed 0 // not much throwing
 +set g_balance_hook_secondary_gravity 5 // fast falling
 +set g_balance_hook_secondary_refire 3 // don't drop too many bombs...
 +set g_balance_hook_secondary_animtime 0.3 // good shoot anim
 +set g_balance_hook_secondary_power 3 // effect behaves like a square function
 +set g_balance_hook_secondary_duration 1.5 // effect runs for three seconds
 +set g_balance_hook_secondary_health 15
 +set g_balance_hook_secondary_damageforcescale 0
 +set g_balance_hook_switchdelay_drop 0.2
 +set g_balance_hook_switchdelay_raise 0.2
 +// }}}
 +// {{{ tuba
 +set g_balance_tuba_refire 0.05
 +set g_balance_tuba_animtime 0.05
 +set g_balance_tuba_attenuation 0.5
 +set g_balance_tuba_volume 1
 +set g_balance_tuba_fadetime 0.25
 +set g_balance_tuba_damage 5
 +set g_balance_tuba_edgedamage 0
 +set g_balance_tuba_radius 200
 +set g_balance_tuba_force 40
 +set g_balance_tuba_pitchstep 6
 +set g_balance_tuba_switchdelay_drop 0.2
 +set g_balance_tuba_switchdelay_raise 0.2
 +// }}}
 +// {{{ fireball // this is a superweapon -- lets make it behave as one.
 +set g_balance_fireball_primary_animtime 0.2
 +set g_balance_fireball_primary_bfgdamage 100
 +set g_balance_fireball_primary_bfgforce 0
 +set g_balance_fireball_primary_bfgradius 1000
 +set g_balance_fireball_primary_damage 200
 +set g_balance_fireball_primary_damageforcescale 0
 +set g_balance_fireball_primary_edgedamage 50
 +set g_balance_fireball_primary_force 600
 +set g_balance_fireball_primary_health 0
 +set g_balance_fireball_primary_laserburntime 0.5
 +set g_balance_fireball_primary_laserdamage 80
 +set g_balance_fireball_primary_laseredgedamage 20
 +set g_balance_fireball_primary_laserradius 256
 +set g_balance_fireball_primary_lifetime 15
 +set g_balance_fireball_primary_radius 200
 +set g_balance_fireball_primary_refire 2
 +set g_balance_fireball_primary_refire2 0
 +set g_balance_fireball_primary_speed 1200
 +set g_balance_fireball_primary_spread 0
 +set g_balance_fireball_secondary_animtime 0.3
 +set g_balance_fireball_secondary_damage 40
 +set g_balance_fireball_secondary_damageforcescale 4
 +set g_balance_fireball_secondary_damagetime 5
 +set g_balance_fireball_secondary_force 100
 +set g_balance_fireball_secondary_laserburntime 0.5
 +set g_balance_fireball_secondary_laserdamage 50
 +set g_balance_fireball_secondary_laseredgedamage 20
 +set g_balance_fireball_secondary_laserradius 110
 +set g_balance_fireball_secondary_lifetime 7
 +set g_balance_fireball_secondary_refire 1.5
 +set g_balance_fireball_secondary_speed 900
 +set g_balance_fireball_secondary_speed_up 100
 +set g_balance_fireball_secondary_speed_z 0
 +set g_balance_fireball_secondary_spread 0
 +set g_balance_fireball_switchdelay_drop 0.2
 +set g_balance_fireball_switchdelay_raise 0.2
 +// }}}
Simple merge
Simple merge
Simple merge
index 662de9fc7a67917a3b2744a71cd663b71698daa3,ab489153011cb2303905e28d04f3ce16d90be8ee..162063427149c9c3cf8c925d4cd7966d53ac73fe
@@@ -274,11 -274,11 +274,11 @@@ void Ent_ReadHook(float bIsNew, float t
                                setmodel(self, "models/hook.md3");
                                self.drawmask = MASK_NORMAL;
                                break;
 -                      case ENT_CLIENT_LGBEAM:
 +                      case ENT_CLIENT_ELECTRO_BEAM:
-                               sound (self, CH_SHOTS_SINGLE, "weapons/lgbeam_fly.wav", VOL_BASE, ATTN_NORM);
+                               sound (self, CH_SHOTS_SINGLE, "weapons/lgbeam_fly.wav", VOL_BASE, ATTEN_NORM);
                                break;
 -                      case ENT_CLIENT_GAUNTLET:
 -                              sound (self, CH_SHOTS_SINGLE, "weapons/gauntletbeam_fly.wav", VOL_BASE, ATTEN_NORM);
 +                      case ENT_CLIENT_ARC_BEAM:
-                               sound (self, CH_SHOTS_SINGLE, "weapons/lgbeam_fly.wav", VOL_BASE, ATTN_NORM);
++                              sound (self, CH_SHOTS_SINGLE, "weapons/lgbeam_fly.wav", VOL_BASE, ATTEN_NORM);
                                break;
                }
        }
Simple merge
index de45141a136c1bb71aba06fafa4de21ad7fa027b,0000000000000000000000000000000000000000..b86c234bdbba9062e6b121cb745efc96050a6351
mode 100644,000000..100644
--- /dev/null
@@@ -1,535 -1,0 +1,535 @@@
-               //self.move_flags &~= FL_ONGROUND;
 +.vector iorigin1, iorigin2;
 +.float spawntime;
 +.vector trail_oldorigin;
 +.float trail_oldtime;
 +.float fade_time, fade_rate;
 +
 +void SUB_Stop()
 +{
 +      self.move_velocity = self.move_avelocity = '0 0 0';
 +      self.move_movetype = MOVETYPE_NONE;
 +}
 +
 +.float alphamod;
 +.float count; // set if clientside projectile
 +.float cnt; // sound index
 +.float gravity;
 +.float snd_looping;
 +.float silent;
 +
 +void Projectile_ResetTrail(vector to)
 +{
 +      self.trail_oldorigin = to;
 +      self.trail_oldtime = time;
 +}
 +
 +void Projectile_DrawTrail(vector to)
 +{
 +      vector from;
 +      float t0;
 +
 +      from = self.trail_oldorigin;
 +      t0 = self.trail_oldtime;
 +      self.trail_oldorigin = to;
 +      self.trail_oldtime = time;
 +
 +      // force the effect even for stationary firemine
 +      if(self.cnt == PROJECTILE_FIREMINE)
 +              if(from == to)
 +                      from_z += 1;
 +
 +      if (self.traileffect)
 +      {
 +              particles_alphamin = particles_alphamax = particles_fade = sqrt(self.alpha);
 +              boxparticles(self.traileffect, self, from, to, self.velocity, self.velocity, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE | PARTICLES_DRAWASTRAIL);
 +      }
 +}
 +
 +void Projectile_Draw()
 +{
 +      vector rot;
 +      vector trailorigin;
 +      float f;
 +      float drawn;
 +      float t;
 +      float a;
 +
 +      f = self.move_flags;
 +
 +      if(self.count & 0x80)
 +      {
-               self.move_flags &~= FL_ONGROUND;
++              //self.move_flags &= ~FL_ONGROUND;
 +              if(self.move_movetype == MOVETYPE_NONE || self.move_movetype == MOVETYPE_FLY)
 +                      Movetype_Physics_NoMatchServer();
 +                      // the trivial movetypes do not have to match the
 +                      // server's ticrate as they are ticrate independent
 +                      // NOTE: this assumption is only true if MOVETYPE_FLY
 +                      // projectiles detonate on impact. If they continue
 +                      // moving, we might still be ticrate dependent.
 +              else
 +                      Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
 +              if(!(self.move_flags & FL_ONGROUND))
 +                      if(self.velocity != '0 0 0')
 +                              self.move_angles = self.angles = vectoangles(self.velocity);
 +      }
 +      else
 +      {
 +              InterpolateOrigin_Do();
 +      }
 +
 +      if(self.count & 0x80)
 +      {
 +              drawn = (time >= self.spawntime - 0.02);
 +              t = max(time, self.spawntime);
 +      }
 +      else
 +      {
 +              drawn = (self.iflags & IFLAG_VALID);
 +              t = time;
 +      }
 +
 +      if(!(f & FL_ONGROUND))
 +      {
 +              rot = '0 0 0';
 +              switch(self.cnt)
 +              {
 +                      /*
 +                      case PROJECTILE_GRENADE:
 +                              rot = '-2000 0 0'; // forward
 +                              break;
 +                      */
 +                      case PROJECTILE_GRENADE_BOUNCING:
 +                              rot = '0 -1000 0'; // sideways
 +                              break;
 +                      case PROJECTILE_NADE_RED_BURN:
 +                      case PROJECTILE_NADE_RED:
 +                      case PROJECTILE_NADE_BLUE_BURN:
 +                      case PROJECTILE_NADE_BLUE:
 +                      case PROJECTILE_NADE_YELLOW_BURN:
 +                      case PROJECTILE_NADE_YELLOW:
 +                      case PROJECTILE_NADE_PINK_BURN:
 +                      case PROJECTILE_NADE_PINK:
 +                      case PROJECTILE_NADE_BURN:
 +                      case PROJECTILE_NADE:
 +                              rot = self.avelocity; 
 +                              break;
 +                      case PROJECTILE_HOOKBOMB:
 +                              rot = '1000 0 0'; // forward
 +                              break;
 +                      default:
 +                              break;
 +              }
 +              self.angles = AnglesTransform_ToAngles(AnglesTransform_Multiply(AnglesTransform_FromAngles(self.angles), rot * (t - self.spawntime)));
 +      }
 +
 +      vector ang;
 +      ang = self.angles;
 +      ang_x = -ang_x;
 +      makevectors(ang);
 +
 +      a = 1 - (time - self.fade_time) * self.fade_rate;
 +      self.alpha = bound(0, self.alphamod * a, 1);
 +      if(self.alpha <= 0)
 +              drawn = 0;
 +      self.renderflags = 0;
 +
 +      trailorigin = self.origin;
 +      switch(self.cnt)
 +      {
 +          case PROJECTILE_NADE_RED_BURN:
 +              case PROJECTILE_NADE_RED:
 +              case PROJECTILE_NADE_BLUE_BURN:
 +              case PROJECTILE_NADE_BLUE:
 +              case PROJECTILE_NADE_YELLOW_BURN:
 +              case PROJECTILE_NADE_YELLOW:
 +              case PROJECTILE_NADE_PINK_BURN:
 +              case PROJECTILE_NADE_PINK:
 +              case PROJECTILE_NADE_BURN:
 +              case PROJECTILE_NADE:
 +                      trailorigin += v_up * 4;
 +                      break;
 +              case PROJECTILE_GRENADE:
 +              case PROJECTILE_GRENADE_BOUNCING:
 +                      trailorigin += v_right * 1 + v_forward * -10;
 +                      break;
 +              default:
 +                      break;
 +      }
 +      if(drawn)
 +              Projectile_DrawTrail(trailorigin);
 +      else
 +              Projectile_ResetTrail(trailorigin);
 +
 +      self.drawmask = 0;
 +
 +      if(!drawn)
 +              return;
 +
 +      switch(self.cnt)
 +      {
 +              case PROJECTILE_BULLET_GLOWING:
 +              case PROJECTILE_BULLET_GLOWING_TRACER:
 +                      adddynamiclight(self.origin, 50 * a, '1 1 0');
 +                      break;
 +              default:
 +                      break;
 +      }
 +
 +      self.drawmask = MASK_NORMAL;
 +}
 +
 +void loopsound(entity e, float ch, string samp, float vol, float attn)
 +{
 +      if(self.silent)
 +              return;
 +
 +      sound(e, ch, samp, vol, attn);
 +      e.snd_looping = ch;
 +}
 +
 +void Ent_RemoveProjectile()
 +{
 +      if(self.count & 0x80)
 +      {
 +              tracebox(self.origin, self.mins, self.maxs, self.origin + self.velocity * 0.05, MOVE_NORMAL, self);
 +              Projectile_DrawTrail(trace_endpos);
 +      }
 +}
 +
 +void Ent_Projectile()
 +{
 +      float f;
 +
 +      // projectile properties:
 +      //   kind (interpolated, or clientside)
 +      //
 +      //   modelindex
 +      //   origin
 +      //   scale
 +      //   if clientside:
 +      //     velocity
 +      //     gravity
 +      //   soundindex (hardcoded list)
 +      //   effects
 +      //
 +      // projectiles don't send angles, because they always follow the velocity
 +
 +      f = ReadByte();
 +      self.count = (f & 0x80);
 +      self.iflags = (self.iflags & IFLAG_INTERNALMASK) | IFLAG_AUTOANGLES | IFLAG_ANGLES | IFLAG_ORIGIN;
 +      self.solid = SOLID_TRIGGER;
 +      //self.effects = EF_NOMODELFLAGS;
 +
 +      // this should make collisions with bmodels more exact, but it leads to
 +      // projectiles no longer being able to lie on a bmodel
 +      self.move_nomonsters = MOVE_WORLDONLY;
 +      if(f & 0x40)
 +              self.move_flags |= FL_ONGROUND;
 +      else
-                               loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTN_NORM);
++              self.move_flags &= ~FL_ONGROUND;
 +
 +      if(!self.move_time)
 +      {
 +              // for some unknown reason, we don't need to care for
 +              // sv_gameplayfix_delayprojectiles here.
 +              self.move_time = time;
 +              self.spawntime = time;
 +      }
 +      else
 +              self.move_time = max(self.move_time, time);
 +
 +      if(!(self.count & 0x80))
 +              InterpolateOrigin_Undo();
 +
 +      if(f & 1)
 +      {
 +              self.origin_x = ReadCoord();
 +              self.origin_y = ReadCoord();
 +              self.origin_z = ReadCoord();
 +              setorigin(self, self.origin);
 +              if(self.count & 0x80)
 +              {
 +                      self.velocity_x = ReadCoord();
 +                      self.velocity_y = ReadCoord();
 +                      self.velocity_z = ReadCoord();
 +                      if(f & 0x10)
 +                              self.gravity = ReadCoord();
 +                      else
 +                              self.gravity = 0; // none
 +                      self.move_origin = self.origin;
 +                      self.move_velocity = self.velocity;
 +              }
 +
 +              if(time == self.spawntime || (self.count & 0x80) || (f & 0x08))
 +              {
 +                      self.trail_oldorigin = self.origin;
 +                      if(!(self.count & 0x80))
 +                              InterpolateOrigin_Reset();
 +              }
 +
 +              if(f & 0x20)
 +              {
 +                      self.fade_time = time + ReadByte() * ticrate;
 +                      self.fade_rate = 1 / (ReadByte() * ticrate);
 +              }
 +              else
 +              {
 +                      self.fade_time = 0;
 +                      self.fade_rate = 0;
 +              }
 +      }
 +
 +      if(f & 2)
 +      {
 +              self.cnt = ReadByte();
 +
 +              self.silent = (self.cnt & 0x80);
 +              self.cnt = (self.cnt & 0x7F);
 +
 +              self.scale = 1;
 +              self.traileffect = 0;
 +              switch(self.cnt)
 +              {
 +                      case PROJECTILE_ELECTRO: setmodel(self, "models/ebomb.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
 +                      case PROJECTILE_ROCKET: setmodel(self, "models/rocket.md3");self.traileffect = particleeffectnum("TR_ROCKET"); self.scale = 2; break;
 +                      case PROJECTILE_BULLET: setmodel(self, "models/tracer.mdl");self.traileffect = particleeffectnum("tr_bullet"); break;
 +                      case PROJECTILE_BULLET_GLOWING: setmodel(self, "models/tracer.mdl");self.traileffect = particleeffectnum("tr_rifle_weak"); break;
 +                      case PROJECTILE_BULLET_GLOWING_TRACER: setmodel(self, "models/tracer.mdl");self.traileffect = particleeffectnum("tr_rifle"); break;
 +                      case PROJECTILE_CRYLINK: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
 +                      case PROJECTILE_CRYLINK_BOUNCING: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
 +                      case PROJECTILE_ELECTRO_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
 +                      case PROJECTILE_GRENADE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
 +                      case PROJECTILE_GRENADE_BOUNCING: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
 +                      case PROJECTILE_MINE: setmodel(self, "models/mine.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
 +                      case PROJECTILE_LASER: setmodel(self, "models/laser.mdl");self.traileffect = particleeffectnum(""); break;
 +                      case PROJECTILE_HLAC: setmodel(self, "models/hlac_bullet.md3");self.traileffect = particleeffectnum(""); break;
 +                      case PROJECTILE_PORTO_RED: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break;
 +                      case PROJECTILE_PORTO_BLUE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break;
 +                      case PROJECTILE_HOOKBOMB: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_KNIGHTSPIKE"); break;
 +                      case PROJECTILE_HAGAR: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break;
 +                      case PROJECTILE_HAGAR_BOUNCING: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break;
 +                      case PROJECTILE_FIREBALL: self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("fireball"); break; // particle effect is good enough
 +                      case PROJECTILE_FIREMINE: self.model = ""; self.modelindex = 0; self.traileffect = particleeffectnum("firemine"); break; // particle effect is good enough
 +                      case PROJECTILE_TAG: setmodel(self, "models/laser.mdl"); self.traileffect = particleeffectnum("TR_ROCKET"); break;
 +                      case PROJECTILE_FLAC: setmodel(self, "models/hagarmissile.mdl"); self.scale = 0.4; self.traileffect = particleeffectnum("TR_SEEKER"); break;
 +                      case PROJECTILE_SEEKER: setmodel(self, "models/tagrocket.md3"); self.traileffect = particleeffectnum("TR_SEEKER"); break;
 +
 +                      case PROJECTILE_RAPTORBOMB:    setmodel(self, "models/vehicles/clusterbomb.md3"); self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break;
 +                      case PROJECTILE_RAPTORBOMBLET: setmodel(self, "models/vehicles/bomblet.md3");     self.gravity = 1; self.avelocity = '0 0 180'; self.traileffect = particleeffectnum(""); break;
 +                      case PROJECTILE_RAPTORCANNON:  setmodel(self, "models/plasmatrail.mdl"); self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
 +
 +                      case PROJECTILE_SPIDERROCKET: setmodel(self, "models/vehicles/rocket02.md3"); self.traileffect = particleeffectnum("spiderbot_rocket_thrust"); break;
 +                      case PROJECTILE_WAKIROCKET:   setmodel(self, "models/vehicles/rocket01.md3");  self.traileffect = particleeffectnum("wakizashi_rocket_thrust"); break;
 +                      case PROJECTILE_WAKICANNON:   setmodel(self, "models/laser.mdl");  self.traileffect = particleeffectnum(""); break;
 +
 +                      case PROJECTILE_BUMBLE_GUN: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
 +                      case PROJECTILE_BUMBLE_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
 +                      
 +                      case PROJECTILE_NADE_RED: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_red"); break;
 +                      case PROJECTILE_NADE_RED_BURN: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_red_burn"); break;
 +                      case PROJECTILE_NADE_BLUE: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_blue"); break;
 +                      case PROJECTILE_NADE_BLUE_BURN: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_blue_burn"); break;
 +                      case PROJECTILE_NADE_YELLOW: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_yellow"); break;
 +                      case PROJECTILE_NADE_YELLOW_BURN: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_yellow_burn"); break;
 +                      case PROJECTILE_NADE_PINK: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_pink"); break;
 +                      case PROJECTILE_NADE_PINK_BURN: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_pink_burn"); break;
 +                      case PROJECTILE_NADE: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade"); break;
 +                      case PROJECTILE_NADE_BURN: setmodel(self, "models/weapons/v_ok_grenade.md3");self.traileffect = particleeffectnum("nade_burn"); break;
 +
 +                      default:
 +                              error("Received invalid CSQC projectile, can't work with this!");
 +                              break;
 +              }
 +
 +              self.mins = '0 0 0';
 +              self.maxs = '0 0 0';
 +              self.colormod = '0 0 0';
 +              self.move_touch = SUB_Stop;
 +              self.move_movetype = MOVETYPE_TOSS;
 +              self.alphamod = 1;
 +
 +              switch(self.cnt)
 +              {
 +                      case PROJECTILE_ELECTRO:
 +                              // only new engines support sound moving with object
-                               loopsound(self, CH_SHOTS_SINGLE, "weapons/rocket_fly.wav", VOL_BASE, ATTN_NORM);
++                              loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTEN_NORM);
 +                              self.mins = '0 0 -4';
 +                              self.maxs = '0 0 -4';
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              self.move_bounce_factor = g_balance_electro_secondary_bouncefactor;
 +                              self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop;
 +                              break;
 +                      case PROJECTILE_ROCKET:
-                               loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly2.wav", VOL_BASE, ATTN_NORM);
++                              loopsound(self, CH_SHOTS_SINGLE, "weapons/rocket_fly.wav", VOL_BASE, ATTEN_NORM);
 +                              self.mins = '-3 -3 -3';
 +                              self.maxs = '3 3 3';
 +                              break;
 +                      case PROJECTILE_GRENADE:
 +                              self.mins = '-3 -3 -3';
 +                              self.maxs = '3 3 3';
 +                              break;
 +                      case PROJECTILE_NADE_RED_BURN:
 +                      case PROJECTILE_NADE_RED:
 +                      case PROJECTILE_NADE_BLUE_BURN:
 +                      case PROJECTILE_NADE_BLUE:
 +                              self.mins = '-3 -3 -3';
 +                              self.maxs = '3 3 3';
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              self.scale = 1.5;
 +                              self.avelocity = randomvec() * 720;
 +                              break;
 +                      case PROJECTILE_GRENADE_BOUNCING:
 +                              self.mins = '-3 -3 -3';
 +                              self.maxs = '3 3 3';
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              self.move_bounce_factor = g_balance_grenadelauncher_bouncefactor;
 +                              self.move_bounce_stopspeed = g_balance_grenadelauncher_bouncestop;
 +                              break;
 +                      case PROJECTILE_NADE_RED_BURN:
 +                      case PROJECTILE_NADE_RED:
 +                      case PROJECTILE_NADE_BLUE_BURN:
 +                      case PROJECTILE_NADE_BLUE:
 +                      case PROJECTILE_NADE_YELLOW_BURN:
 +                      case PROJECTILE_NADE_YELLOW:
 +                      case PROJECTILE_NADE_PINK_BURN:
 +                      case PROJECTILE_NADE_PINK:
 +                      case PROJECTILE_NADE_BURN:
 +                      case PROJECTILE_NADE:
 +                              self.mins = '-16 -16 -16';
 +                              self.maxs = '16 16 16';
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              self.scale = 1.5;
 +                              self.avelocity = randomvec() * 720;
 +                              break;
 +                      case PROJECTILE_MINE:
 +                              self.mins = '-4 -4 -4';
 +                              self.maxs = '4 4 4';
 +                              break;
 +                      case PROJECTILE_PORTO_RED:
 +                              self.colormod = '2 1 1';
 +                              self.alphamod = 0.5;
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              break;
 +                      case PROJECTILE_PORTO_BLUE:
 +                              self.colormod = '1 1 2';
 +                              self.alphamod = 0.5;
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              break;
 +                      case PROJECTILE_HAGAR_BOUNCING:
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              break;
 +                      case PROJECTILE_CRYLINK_BOUNCING:
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              break;
 +                      case PROJECTILE_FIREBALL:
-                               loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly.wav", VOL_BASE, ATTN_NORM);
++                              loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly2.wav", VOL_BASE, ATTEN_NORM);
 +                              self.mins = '-16 -16 -16';
 +                              self.maxs = '16 16 16';
 +                              break;
 +                      case PROJECTILE_FIREMINE:
-                               loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTN_NORM);
++                              loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly.wav", VOL_BASE, ATTEN_NORM);
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              self.mins = '-4 -4 -4';
 +                              self.maxs = '4 4 4';
 +                              break;
 +                      case PROJECTILE_TAG:
 +                              self.mins = '-2 -2 -2';
 +                              self.maxs = '2 2 2';
 +                              break;
 +                      case PROJECTILE_FLAC:
 +                              self.mins = '-2 -2 -2';
 +                              self.maxs = '2 2 2';
 +                              break;
 +                      case PROJECTILE_SEEKER:
-                 loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTN_NORM);
++                              loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM);
 +                              self.mins = '-4 -4 -4';
 +                              self.maxs = '4 4 4';
 +                              break;
 +            case PROJECTILE_RAPTORBOMB:
 +                              self.mins = '-3 -3 -3';
 +                              self.maxs = '3 3 3';
 +                              break;
 +            case PROJECTILE_RAPTORBOMBLET:
 +                              break;
 +            case PROJECTILE_RAPTORCANNON:
 +                              break;
 +            case PROJECTILE_SPIDERROCKET:
-                 loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTN_NORM);
++                loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM);
 +                              break;
 +            case PROJECTILE_WAKIROCKET:
-                               loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTN_NORM);
++                loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM);
 +                              break;            
 +            /*
 +            case PROJECTILE_WAKICANNON:
 +                              break;
 +                      case PROJECTILE_BUMBLE_GUN:
 +                              // only new engines support sound moving with object
++                              loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTEN_NORM);
 +                              self.mins = '0 0 -4';
 +                              self.maxs = '0 0 -4';
 +                              self.move_movetype = MOVETYPE_BOUNCE;
 +                              self.move_touch = func_null;
 +                              self.move_bounce_factor = g_balance_electro_secondary_bouncefactor;
 +                              self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop;
 +                              break;
 +                      */
 +                      default:
 +                              break;
 +              }
 +              setsize(self, self.mins, self.maxs);
 +      }
 +
 +      if(self.gravity)
 +      {
 +              if(self.move_movetype == MOVETYPE_FLY)
 +                      self.move_movetype = MOVETYPE_TOSS;
 +              if(self.move_movetype == MOVETYPE_BOUNCEMISSILE)
 +                      self.move_movetype = MOVETYPE_BOUNCE;
 +      }
 +      else
 +      {
 +              if(self.move_movetype == MOVETYPE_TOSS)
 +                      self.move_movetype = MOVETYPE_FLY;
 +              if(self.move_movetype == MOVETYPE_BOUNCE)
 +                      self.move_movetype = MOVETYPE_BOUNCEMISSILE;
 +      }
 +
 +      if(!(self.count & 0x80))
 +              InterpolateOrigin_Note();
 +
 +      self.draw = Projectile_Draw;
 +      self.entremove = Ent_RemoveProjectile;
 +}
 +
 +void Projectile_Precache()
 +{
 +      precache_model("models/ebomb.mdl");
 +      precache_model("models/elaser.mdl");
 +      precache_model("models/grenademodel.md3");
 +      precache_model("models/mine.md3");
 +      precache_model("models/hagarmissile.mdl");
 +      precache_model("models/hlac_bullet.md3");
 +      precache_model("models/laser.mdl");
 +      precache_model("models/plasmatrail.mdl");
 +      precache_model("models/rocket.md3");
 +      precache_model("models/tagrocket.md3");
 +      precache_model("models/tracer.mdl");
 +      
 +      precache_model("models/weapons/v_ok_grenade.md3");
 +
 +      precache_sound("weapons/electro_fly.wav");
 +      precache_sound("weapons/rocket_fly.wav");
 +      precache_sound("weapons/fireball_fly.wav");
 +      precache_sound("weapons/fireball_fly2.wav");
 +      precache_sound("weapons/tag_rocket_fly.wav");
 +
 +}
index e102227a53add921f9151970674fddc1c27a47ac,b3dafaaf7ba7293813715bb1d1ae0aa673af8f14..822d61a72f3bbe0c358d148a2932a664aa270c3a
@@@ -382,11 -384,29 +385,10 @@@ const float SPECIES_RESERVED = 15
  // we can use this frags value for both
  
  // water levels
- float WATERLEVEL_NONE = 0;
- float WATERLEVEL_WETFEET = 1;
- float WATERLEVEL_SWIMMING = 2;
- float WATERLEVEL_SUBMERGED = 3;
+ const float WATERLEVEL_NONE = 0;
+ const float WATERLEVEL_WETFEET = 1;
+ const float WATERLEVEL_SWIMMING = 2;
+ const float WATERLEVEL_SUBMERGED = 3;
 -
 -const float MAX_SHOT_DISTANCE = 32768;
 -
 -// weapon requests
 -const float WR_SETUP = 1; // (SVQC) setup weapon data
 -const float WR_THINK = 2; // (SVQC) logic to run every frame
 -const float WR_CHECKAMMO1 = 3; // (SVQC) checks ammo for weapon
 -const float WR_CHECKAMMO2 = 4; // (SVQC) checks ammo for weapon
 -const float WR_AIM = 5; // (SVQC) runs bot aiming code for this weapon
 -const float WR_PRECACHE = 6; // (CSQC and SVQC) precaches models/sounds used by this weapon
 -const float WR_SUICIDEMESSAGE = 7; // (SVQC) notification number for suicide message (may inspect w_deathtype for details)
 -const float WR_KILLMESSAGE = 8; // (SVQC) notification number for kill message (may inspect w_deathtype for details)
 -const float WR_RELOAD = 9; // (SVQC) does not need to do anything
 -const float WR_RESETPLAYER = 10; // (SVQC) does not need to do anything
 -const float WR_IMPACTEFFECT = 11; // (CSQC) impact effect
 -const float WR_SWITCHABLE = 12; // (CSQC) impact effect
 -const float WR_PLAYERDEATH = 13; // (SVQC) does not need to do anything
 -const float WR_GONETHINK = 14; // (SVQC) logic to run every frame, also if no longer having the weapon as long as the switch away has not been performed
 -
  #define SERVERFLAG_ALLOW_FULLBRIGHT 1
  #define SERVERFLAG_TEAMPLAY 2
  #define SERVERFLAG_PLAYERSTATS 4
Simple merge
Simple merge
index 14ae101cdbc1fedd2f63f6384939a0b7d874ddbb,0000000000000000000000000000000000000000..2b79a92b2d56222d5bec4d0f372d1af877ab6c0a
mode 100644,000000..100644
--- /dev/null
@@@ -1,638 -1,0 +1,638 @@@
-               spamsound (self, CH_SHOTS, "weapons/electro_bounce.wav", VOL_BASE, ATTN_NORM);
 +#ifdef REGISTER_WEAPON
 +REGISTER_WEAPON(
 +/* WEP_##id */ ELECTRO,
 +/* function */ w_electro,
 +/* ammotype */ IT_CELLS,
 +/* impulse  */ 5,
 +/* flags    */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH,
 +/* rating   */ BOT_PICKUP_RATING_MID,
 +/* model    */ "electro",
 +/* netname  */ "electro",
 +/* fullname */ _("Electro")
 +);
 +
 +#ifdef SVQC
 +void ElectroInit();
 +vector electro_shotorigin[4];
 +#endif
 +#else
 +#ifdef SVQC
 +void spawnfunc_weapon_electro() { weapon_defaultspawnfunc(WEP_ELECTRO); }
 +
 +.float electro_count;
 +.float electro_secondarytime;
 +
 +void W_Plasma_Explode_Combo (void);
 +
 +void W_Plasma_TriggerCombo(vector org, float rad, entity own)
 +{
 +      entity e;
 +      e = WarpZone_FindRadius(org, rad, TRUE);
 +      while (e)
 +      {
 +              if (e.classname == "plasma")
 +              {
 +                      // change owner to whoever caused the combo explosion
 +                      e.realowner = own;
 +                      e.takedamage = DAMAGE_NO;
 +                      e.classname = "plasma_chain";
 +                      e.think = W_Plasma_Explode_Combo;
 +                      e.nextthink = time + vlen(e.WarpZone_findradius_dist) / autocvar_g_balance_electro_combo_speed; // delay combo chains, looks cooler
 +              }
 +              e = e.chain;
 +      }
 +}
 +
 +void W_Plasma_Explode (void)
 +{
 +      if(other.takedamage == DAMAGE_AIM)
 +              if(IS_PLAYER(other))
 +                      if(IsDifferentTeam(self.realowner, other))
 +                              if(other.deadflag == DEAD_NO)
 +                                      if(IsFlying(other))
 +                                              Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_ELECTROBITCH);
 +
 +      self.event_damage = func_null;
 +      self.takedamage = DAMAGE_NO;
 +      if (self.movetype == MOVETYPE_BOUNCE)
 +      {
 +              RadiusDamage (self, self.realowner, autocvar_g_balance_electro_secondary_damage, autocvar_g_balance_electro_secondary_edgedamage, autocvar_g_balance_electro_secondary_radius, world, world, autocvar_g_balance_electro_secondary_force, self.projectiledeathtype, other);
 +      }
 +      else
 +      {
 +              W_Plasma_TriggerCombo(self.origin, autocvar_g_balance_electro_primary_comboradius, self.realowner);
 +              RadiusDamage (self, self.realowner, autocvar_g_balance_electro_primary_damage, autocvar_g_balance_electro_primary_edgedamage, autocvar_g_balance_electro_primary_radius, world, world, autocvar_g_balance_electro_primary_force, self.projectiledeathtype, other);
 +      }
 +
 +      remove (self);
 +}
 +
 +void W_Plasma_Explode_Combo (void)
 +{
 +      W_Plasma_TriggerCombo(self.origin, autocvar_g_balance_electro_combo_comboradius, self.realowner);
 +
 +      self.event_damage = func_null;
 +      RadiusDamage (self, self.realowner, autocvar_g_balance_electro_combo_damage, autocvar_g_balance_electro_combo_edgedamage, autocvar_g_balance_electro_combo_radius, world, world, autocvar_g_balance_electro_combo_force, WEP_ELECTRO | HITTYPE_BOUNCE, world); // use THIS type for a combo because primary can't bounce
 +
 +      remove (self);
 +}
 +
 +void W_Plasma_Touch (void)
 +{
 +      //self.velocity = self.velocity  * 0.1;
 +
 +      PROJECTILE_TOUCH;
 +      if (other.takedamage == DAMAGE_AIM) {
 +              W_Plasma_Explode ();
 +      } else {
 +              //UpdateCSQCProjectile(self);
-               sound (self, CH_WEAPON_A, "weapons/lgbeam_fire.wav", VOL_BASE, ATTN_NORM);
++              spamsound (self, CH_SHOTS, "weapons/electro_bounce.wav", VOL_BASE, ATTEN_NORM);
 +              self.projectiledeathtype |= HITTYPE_BOUNCE;
 +      }
 +}
 +
 +void W_Plasma_TouchExplode (void)
 +{
 +      PROJECTILE_TOUCH;
 +      W_Plasma_Explode ();
 +}
 +
 +void W_Plasma_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 +{
 +      if(self.health <= 0)
 +              return;
 +
 +      // note: combos are usually triggered by W_Plasma_TriggerCombo, not damage
 +      float is_combo = (inflictor.classname == "plasma_chain" || inflictor.classname == "plasma_prim");
 +      
 +      if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, (is_combo ? 1 : -1)))
 +              return; // g_projectiles_damage says to halt    
 +      
 +      self.health = self.health - damage;
 +      if (self.health <= 0)
 +      {
 +              self.takedamage = DAMAGE_NO;
 +              self.nextthink = time;
 +              if (is_combo)
 +              {
 +                      // change owner to whoever caused the combo explosion
 +                      self.realowner = inflictor.realowner;
 +                      self.classname = "plasma_chain";
 +                      self.think = W_Plasma_Explode_Combo;
 +                      self.nextthink = time + min(autocvar_g_balance_electro_combo_radius, vlen(self.origin - inflictor.origin)) / autocvar_g_balance_electro_combo_speed; // delay combo chains, looks cooler
 +                              //                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bounding the length, because inflictor may be in a galaxy far far away (warpzones)
 +              }
 +              else
 +              {
 +                      self.use = W_Plasma_Explode;
 +                      self.think = adaptor_think2use; // not _hittype_splash, as this runs "immediately"
 +              }
 +      }
 +}
 +
 +void W_Electro_Attack()
 +{
 +      entity proj;
 +
 +      W_DecreaseAmmo(ammo_cells, autocvar_g_balance_electro_primary_ammo, autocvar_g_balance_electro_reload_ammo);
 +
 +      W_SetupShot_ProjectileSize (self, '0 0 -3', '0 0 -3', FALSE, 2, "weapons/electro_fire.wav", CH_WEAPON_A, autocvar_g_balance_electro_primary_damage);
 +
 +      pointparticles(particleeffectnum("electro_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
 +
 +      proj = spawn ();
 +      proj.classname = "plasma_prim";
 +      proj.owner = proj.realowner = self;
 +      proj.bot_dodge = TRUE;
 +      proj.bot_dodgerating = autocvar_g_balance_electro_primary_damage;
 +      proj.use = W_Plasma_Explode;
 +      proj.think = adaptor_think2use_hittype_splash;
 +      proj.nextthink = time + autocvar_g_balance_electro_primary_lifetime;
 +      PROJECTILE_MAKETRIGGER(proj);
 +      proj.projectiledeathtype = WEP_ELECTRO;
 +      setorigin(proj, w_shotorg);
 +
 +      proj.movetype = MOVETYPE_FLY;
 +      W_SETUPPROJECTILEVELOCITY(proj, g_balance_electro_primary);
 +      proj.angles = vectoangles(proj.velocity);
 +      proj.touch = W_Plasma_TouchExplode;
 +      setsize(proj, '0 0 -3', '0 0 -3');
 +      proj.flags = FL_PROJECTILE;
 +      proj.missile_flags = MIF_SPLASH;
 +
 +      CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO_BEAM, TRUE);
 +
 +      other = proj; MUTATOR_CALLHOOK(EditProjectile);
 +}
 +
 +void W_Electro_Attack2()
 +{
 +      entity proj;
 +
 +      W_DecreaseAmmo(ammo_cells, autocvar_g_balance_electro_secondary_ammo, autocvar_g_balance_electro_reload_ammo);
 +
 +      W_SetupShot_ProjectileSize (self, '0 0 -4', '0 0 -4', FALSE, 2, "weapons/electro_fire2.wav", CH_WEAPON_A, autocvar_g_balance_electro_secondary_damage);
 +
 +      w_shotdir = v_forward; // no TrueAim for grenades please
 +
 +      pointparticles(particleeffectnum("electro_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
 +
 +      proj = spawn ();
 +      proj.classname = "plasma";
 +      proj.owner = proj.realowner = self;
 +      proj.use = W_Plasma_Explode;
 +      proj.think = adaptor_think2use_hittype_splash;
 +      proj.bot_dodge = TRUE;
 +      proj.bot_dodgerating = autocvar_g_balance_electro_secondary_damage;
 +      proj.nextthink = time + autocvar_g_balance_electro_secondary_lifetime;
 +      PROJECTILE_MAKETRIGGER(proj);
 +      proj.projectiledeathtype = WEP_ELECTRO | HITTYPE_SECONDARY;
 +      setorigin(proj, w_shotorg);
 +
 +      //proj.glow_size = 50;
 +      //proj.glow_color = 45;
 +      proj.movetype = MOVETYPE_BOUNCE;
 +      W_SETUPPROJECTILEVELOCITY_UP(proj, g_balance_electro_secondary);
 +      proj.touch = W_Plasma_Touch;
 +      setsize(proj, '0 0 -4', '0 0 -4');
 +      proj.takedamage = DAMAGE_YES;
 +      proj.damageforcescale = autocvar_g_balance_electro_secondary_damageforcescale;
 +      proj.health = autocvar_g_balance_electro_secondary_health;
 +      proj.event_damage = W_Plasma_Damage;
 +      proj.flags = FL_PROJECTILE;
 +      proj.damagedbycontents = (autocvar_g_balance_electro_secondary_damagedbycontents);
 +
 +      proj.bouncefactor = autocvar_g_balance_electro_secondary_bouncefactor;
 +      proj.bouncestop = autocvar_g_balance_electro_secondary_bouncestop;
 +      proj.missile_flags = MIF_SPLASH | MIF_ARC;
 +
 +#if 0
 +      entity p2;
 +      p2 = spawn();
 +      copyentity(proj, p2);
 +      setmodel(p2, "models/ebomb.mdl");
 +      setsize(p2, proj.mins, proj.maxs);
 +#endif
 +
 +      CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO, FALSE); // no culling, it has sound
 +
 +      other = proj; MUTATOR_CALLHOOK(EditProjectile);
 +}
 +
 +.vector hook_start, hook_end;
 +float lgbeam_send(entity to, float sf)
 +{
 +      WriteByte(MSG_ENTITY, ENT_CLIENT_ELECTRO_BEAM);
 +      sf = sf & 0x7F;
 +      if(sound_allowed(MSG_BROADCAST, self.realowner))
 +              sf |= 0x80;
 +      WriteByte(MSG_ENTITY, sf);
 +      if(sf & 1)
 +      {
 +              WriteByte(MSG_ENTITY, num_for_edict(self.realowner));
 +              WriteCoord(MSG_ENTITY, autocvar_g_balance_electro_primary_range);
 +      }
 +      if(sf & 2)
 +      {
 +              WriteCoord(MSG_ENTITY, self.hook_start_x);
 +              WriteCoord(MSG_ENTITY, self.hook_start_y);
 +              WriteCoord(MSG_ENTITY, self.hook_start_z);
 +      }
 +      if(sf & 4)
 +      {
 +              WriteCoord(MSG_ENTITY, self.hook_end_x);
 +              WriteCoord(MSG_ENTITY, self.hook_end_y);
 +              WriteCoord(MSG_ENTITY, self.hook_end_z);
 +      }
 +      return TRUE;
 +}
 +.entity lgbeam;
 +.float prevlgfire;
 +float lgbeam_checkammo()
 +{
 +      if(self.realowner.items & IT_UNLIMITED_WEAPON_AMMO)
 +              return TRUE;
 +      else if(autocvar_g_balance_electro_reload_ammo)
 +              return self.realowner.clip_load > 0;
 +      else
 +              return self.realowner.ammo_cells > 0;
 +}
 +
 +entity lgbeam_owner_ent;
 +void lgbeam_think()
 +{
 +      entity owner_player;
 +      owner_player = self.realowner;
 +
 +      owner_player.prevlgfire = time;
 +      if (self != owner_player.lgbeam)
 +      {
 +              remove(self);
 +              return;
 +      }
 +
 +      if (owner_player.weaponentity.state != WS_INUSE || !lgbeam_checkammo() || owner_player.deadflag != DEAD_NO || !owner_player.BUTTON_ATCK || owner_player.freezetag_frozen)
 +      {
 +              if(self == owner_player.lgbeam)
 +                      owner_player.lgbeam = world;
 +              remove(self);
 +              return;
 +      }
 +
 +      self.nextthink = time;
 +
 +      makevectors(owner_player.v_angle);
 +
 +      float dt, f;
 +      dt = frametime;
 +
 +      // if this weapon is reloadable, decrease its load. Else decrease the player's ammo
 +      if not(owner_player.items & IT_UNLIMITED_WEAPON_AMMO)
 +      {
 +              if(autocvar_g_balance_electro_primary_ammo)
 +              {
 +                      if(autocvar_g_balance_electro_reload_ammo)
 +                      {
 +                              dt = min(dt, owner_player.clip_load / autocvar_g_balance_electro_primary_ammo);
 +                              owner_player.clip_load = max(0, owner_player.clip_load - autocvar_g_balance_electro_primary_ammo * frametime);
 +                              owner_player.(weapon_load[WEP_ELECTRO]) = owner_player.clip_load;
 +                      }
 +                      else
 +                      {
 +                              dt = min(dt, owner_player.ammo_cells / autocvar_g_balance_electro_primary_ammo);
 +                              owner_player.ammo_cells = max(0, owner_player.ammo_cells - autocvar_g_balance_electro_primary_ammo * frametime);
 +                      }
 +              }
 +      }
 +
 +      W_SetupShot_Range(owner_player, TRUE, 0, "", 0, autocvar_g_balance_electro_primary_damage * dt, autocvar_g_balance_electro_primary_range);
 +      if(!lgbeam_owner_ent)
 +      {
 +              lgbeam_owner_ent = spawn();
 +              lgbeam_owner_ent.classname = "lgbeam_owner_ent";
 +      }
 +      WarpZone_traceline_antilag(lgbeam_owner_ent, w_shotorg, w_shotend, MOVE_NORMAL, lgbeam_owner_ent, ANTILAG_LATENCY(owner_player));
 +
 +      // apply the damage
 +      if(trace_ent)
 +      {
 +              vector force;
 +              force = w_shotdir * autocvar_g_balance_electro_primary_force + '0 0 1' * autocvar_g_balance_electro_primary_force_up;
 +
 +              f = ExponentialFalloff(autocvar_g_balance_electro_primary_falloff_mindist, autocvar_g_balance_electro_primary_falloff_maxdist, autocvar_g_balance_electro_primary_falloff_halflifedist, vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos) - w_shotorg));
 +
 +              if(accuracy_isgooddamage(owner_player, trace_ent))
 +                      accuracy_add(owner_player, WEP_ELECTRO, 0, autocvar_g_balance_electro_primary_damage * dt * f);
 +              Damage (trace_ent, owner_player, owner_player, autocvar_g_balance_electro_primary_damage * dt * f, WEP_ELECTRO, trace_endpos, force * dt);
 +      }
 +      W_Plasma_TriggerCombo(trace_endpos, autocvar_g_balance_electro_primary_comboradius, owner_player);
 +
 +      // draw effect
 +      if(w_shotorg != self.hook_start)
 +      {
 +              self.SendFlags |= 2;
 +              self.hook_start = w_shotorg;
 +      }
 +      if(w_shotend != self.hook_end)
 +      {
 +              self.SendFlags |= 4;
 +              self.hook_end = w_shotend;
 +      }
 +}
 +
 +// experimental lightning gun
 +void W_Electro_Attack3 (void)
 +{
 +      // only play fire sound if 0.5 sec has passed since player let go the fire button
 +      if(time - self.prevlgfire > 0.5)
-                                       sound(self, CH_SHOTS, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM);
++              sound (self, CH_WEAPON_A, "weapons/lgbeam_fire.wav", VOL_BASE, ATTEN_NORM);
 +
 +      entity beam, oldself;
 +
 +      self.lgbeam = beam = spawn();
 +      beam.classname = "lgbeam";
 +      beam.solid = SOLID_NOT;
 +      beam.think = lgbeam_think;
 +      beam.owner = beam.realowner = self;
 +      beam.movetype = MOVETYPE_NONE;
 +      beam.shot_spread = 0;
 +      beam.bot_dodge = TRUE;
 +      beam.bot_dodgerating = autocvar_g_balance_electro_primary_damage;
 +      Net_LinkEntity(beam, FALSE, 0, lgbeam_send);
 +
 +      oldself = self;
 +      self = beam;
 +      self.think();
 +      self = oldself;
 +}
 +
 +void ElectroInit()
 +{
 +      WEP_ACTION(WEP_ELECTRO, WR_INIT);
 +      electro_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ELECTRO), FALSE, FALSE, 1);
 +      electro_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ELECTRO), FALSE, FALSE, 2);
 +      electro_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ELECTRO), FALSE, FALSE, 3);
 +      electro_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ELECTRO), FALSE, FALSE, 4);
 +}
 +
 +void w_electro_checkattack()
 +{
 +      if(self.electro_count > 1)
 +      if(self.BUTTON_ATCK2)
 +      if(weapon_prepareattack(1, -1))
 +      {
 +              W_Electro_Attack2();
 +              self.electro_count -= 1;
 +              weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_electro_secondary_animtime, w_electro_checkattack);
 +              return;
 +      }
 +
 +      w_ready();
 +}
 +
 +.float bot_secondary_electromooth;
 +.float BUTTON_ATCK_prev;
 +float w_electro(float req)
 +{
 +      float ammo_amount;
 +      switch(req)
 +      {
 +              case WR_AIM:
 +              {
 +                      self.BUTTON_ATCK=FALSE;
 +                      self.BUTTON_ATCK2=FALSE;
 +                      if(vlen(self.origin-self.enemy.origin) > 1000)
 +                              self.bot_secondary_electromooth = 0;
 +                      if(self.bot_secondary_electromooth == 0)
 +                      {
 +                              float shoot;
 +
 +                              if(autocvar_g_balance_electro_primary_speed)
 +                                      shoot = bot_aim(autocvar_g_balance_electro_primary_speed, 0, autocvar_g_balance_electro_primary_lifetime, FALSE);
 +                              else
 +                                      shoot = bot_aim(1000000, 0, 0.001, FALSE);
 +
 +                              if(shoot)
 +                              {
 +                                      self.BUTTON_ATCK = TRUE;
 +                                      if(random() < 0.01) self.bot_secondary_electromooth = 1;
 +                              }
 +                      }
 +                      else
 +                      {
 +                              if(bot_aim(autocvar_g_balance_electro_secondary_speed, autocvar_g_balance_mortar_secondary_speed_up, autocvar_g_balance_electro_secondary_lifetime, TRUE)) // WHAT THE ACTUAL FUUUUUUUUUCK?!?!? WEAPONTODO
 +                              {
 +                                      self.BUTTON_ATCK2 = TRUE;
 +                                      if(random() < 0.03) self.bot_secondary_electromooth = 0;
 +                              }
 +                      }
 +                      
 +                      return TRUE;
 +              }
 +              case WR_THINK:
 +              {
 +                      if(autocvar_g_balance_electro_reload_ammo) // forced reload
 +                      {
 +                              ammo_amount = 0;
 +                              if(autocvar_g_balance_electro_lightning)
 +                              {
 +                                      if(self.clip_load > 0)
 +                                              ammo_amount = 1;
 +                              }
 +                              else if(self.clip_load >= autocvar_g_balance_electro_primary_ammo)
 +                                      ammo_amount = 1;
 +                              if(self.clip_load >= autocvar_g_balance_electro_secondary_ammo)
 +                                      ammo_amount += 1;
 +
 +                              if(!ammo_amount)
 +                              {
 +                                      WEP_ACTION(self.weapon, WR_RELOAD);
 +                                      return FALSE;
 +                              }
 +                              
 +                              return TRUE;
 +                      }
 +                      if (self.BUTTON_ATCK)
 +                      {
 +                              if(autocvar_g_balance_electro_lightning)
 +                                      if(self.BUTTON_ATCK_prev)
 +                                              weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_electro_primary_animtime, w_ready);
 +
 +                              if (weapon_prepareattack(0, (autocvar_g_balance_electro_lightning ? 0 : autocvar_g_balance_electro_primary_refire)))
 +                              {
 +                                      if(autocvar_g_balance_electro_lightning)
 +                                      {
 +                                              if ((!self.lgbeam) || wasfreed(self.lgbeam))
 +                                              {
 +                                                      W_Electro_Attack3();
 +                                              }
 +                                              if(!self.BUTTON_ATCK_prev)
 +                                              {
 +                                                      weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_electro_primary_animtime, w_ready);
 +                                                      self.BUTTON_ATCK_prev = 1;
 +                                              }
 +                                      }
 +                                      else
 +                                      {
 +                                              W_Electro_Attack();
 +                                              weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_electro_primary_animtime, w_ready);
 +                                      }
 +                              }
 +                      } else {
 +                              if(autocvar_g_balance_electro_lightning)
 +                              {
 +                                      if (self.BUTTON_ATCK_prev != 0)
 +                                      {
 +                                              weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_electro_primary_animtime, w_ready);
 +                                              ATTACK_FINISHED(self) = time + autocvar_g_balance_electro_primary_refire * W_WeaponRateFactor();
 +                                      }
 +                                      self.BUTTON_ATCK_prev = 0;
 +                              }
 +
 +                              if (self.BUTTON_ATCK2)
 +                              {
 +                                      if (time >= self.electro_secondarytime)
 +                                      if (weapon_prepareattack(1, autocvar_g_balance_electro_secondary_refire))
 +                                      {
 +                                              W_Electro_Attack2();
 +                                              self.electro_count = autocvar_g_balance_electro_secondary_count;
 +                                              weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_electro_secondary_animtime, w_electro_checkattack);
 +                                              self.electro_secondarytime = time + autocvar_g_balance_electro_secondary_refire2 * W_WeaponRateFactor();
 +                                      }
 +                              }
 +                      }
 +                      
 +                      return TRUE;
 +              }
 +              case WR_INIT:
 +              {
 +                      precache_model ("models/weapons/g_electro.md3");
 +                      precache_model ("models/weapons/v_electro.md3");
 +                      precache_model ("models/weapons/h_electro.iqm");
 +                      precache_sound ("weapons/electro_bounce.wav");
 +                      precache_sound ("weapons/electro_fire.wav");
 +                      precache_sound ("weapons/electro_fire2.wav");
 +                      precache_sound ("weapons/electro_impact.wav");
 +                      precache_sound ("weapons/electro_impact_combo.wav");
 +                      
 +                      if(autocvar_g_balance_electro_lightning)
 +                              precache_sound ("weapons/lgbeam_fire.wav");
 +                              
 +                      return TRUE;
 +              }
 +              case WR_SETUP:
 +              {
 +                      self.current_ammo = ammo_cells;
 +                      return TRUE;
 +              }
 +              case WR_CHECKAMMO1:
 +              {
 +                      if(autocvar_g_balance_electro_lightning)
 +                      {
 +                              if(!autocvar_g_balance_electro_primary_ammo)
 +                                      ammo_amount = 1;
 +                              else
 +                                      ammo_amount = self.ammo_cells > 0;
 +                              ammo_amount += self.(weapon_load[WEP_ELECTRO]) > 0;
 +                      }
 +                      else
 +                      {
 +                              ammo_amount = self.ammo_cells >= autocvar_g_balance_electro_primary_ammo;
 +                              ammo_amount += self.(weapon_load[WEP_ELECTRO]) >= autocvar_g_balance_electro_primary_ammo;
 +                      }
 +                      return ammo_amount;
 +              }
 +              case WR_CHECKAMMO2:
 +              {
 +                      if(autocvar_g_balance_electro_combo_safeammocheck) // true if you can fire at least one secondary blob AND one primary shot after it, otherwise false.
 +                      {
 +                              ammo_amount = self.ammo_cells >= autocvar_g_balance_electro_secondary_ammo + autocvar_g_balance_electro_primary_ammo;
 +                              ammo_amount += self.(weapon_load[WEP_ELECTRO]) >= autocvar_g_balance_electro_secondary_ammo + autocvar_g_balance_electro_primary_ammo;
 +                      }
 +                      else
 +                      {
 +                              ammo_amount = self.ammo_cells >= autocvar_g_balance_electro_secondary_ammo;
 +                              ammo_amount += self.(weapon_load[WEP_ELECTRO]) >= autocvar_g_balance_electro_secondary_ammo;
 +                      }
 +                      return ammo_amount;
 +              }
 +              case WR_RESETPLAYER:
 +              {
 +                      self.electro_secondarytime = time;
 +                      return TRUE;
 +              }
 +              case WR_RELOAD:
 +              {
 +                      W_Reload(min(autocvar_g_balance_electro_primary_ammo, autocvar_g_balance_electro_secondary_ammo), "weapons/reload.wav");
 +                      return TRUE;
 +              }
 +              case WR_SUICIDEMESSAGE:
 +              {
 +                      if(w_deathtype & HITTYPE_SECONDARY)
 +                              return WEAPON_ELECTRO_SUICIDE_ORBS;
 +                      else
 +                              return WEAPON_ELECTRO_SUICIDE_BOLT;
 +              }
 +              case WR_KILLMESSAGE:
 +              {
 +                      if(w_deathtype & HITTYPE_SECONDARY)
 +                      {
 +                              return WEAPON_ELECTRO_MURDER_ORBS;
 +                      }
 +                      else
 +                      {
 +                              if(w_deathtype & HITTYPE_BOUNCE)
 +                                      return WEAPON_ELECTRO_MURDER_COMBO;
 +                              else
 +                                      return WEAPON_ELECTRO_MURDER_BOLT;
 +                      }
 +              }
 +      }
 +      return TRUE;
 +}
 +#endif
 +#ifdef CSQC
 +float w_electro(float req)
 +{
 +      switch(req)
 +      {
 +              case WR_IMPACTEFFECT:
 +              {
 +                      vector org2;
 +                      org2 = w_org + w_backoff * 6;
 +                      if(w_deathtype & HITTYPE_SECONDARY)
 +                      {
 +                              pointparticles(particleeffectnum("electro_ballexplode"), org2, '0 0 0', 1);
 +                              if(!w_issilent)
-                                               sound(self, CH_SHOTS, "weapons/electro_impact_combo.wav", VOL_BASE, ATTN_NORM);
++                                      sound(self, CH_SHOTS, "weapons/electro_impact.wav", VOL_BASE, ATTEN_NORM);
 +                      }
 +                      else
 +                      {
 +                              if(w_deathtype & HITTYPE_BOUNCE)
 +                              {
 +                                      // this is sent as "primary (w_deathtype & HITTYPE_BOUNCE)" to distinguish it from (w_deathtype & HITTYPE_SECONDARY) bounced balls
 +                                      pointparticles(particleeffectnum("electro_combo"), org2, '0 0 0', 1);
 +                                      if(!w_issilent)
-                                               sound(self, CH_SHOTS, "weapons/electro_impact.wav", VOL_BASE, ATTN_NORM);
++                                              sound(self, CH_SHOTS, "weapons/electro_impact_combo.wav", VOL_BASE, ATTEN_NORM);
 +                              }
 +                              else
 +                              {
 +                                      pointparticles(particleeffectnum("electro_impact"), org2, '0 0 0', 1);
 +                                      if(!w_issilent)
++                                              sound(self, CH_SHOTS, "weapons/electro_impact.wav", VOL_BASE, ATTEN_NORM);
 +                              }
 +                      }
 +                      
 +                      return TRUE;
 +              }
 +              case WR_INIT:
 +              {
 +                      precache_sound("weapons/electro_impact.wav");
 +                      precache_sound("weapons/electro_impact_combo.wav");
 +                      return TRUE;
 +              }
 +      }
 +      return TRUE;
 +}
 +#endif
 +#endif
index c547b8c634176e99805c3e91ab8ed5feb00433f3,0000000000000000000000000000000000000000..83fe87ad2dfe066743d87ff91ce6ac2330e12d87
mode 100644,000000..100644
--- /dev/null
@@@ -1,471 -1,0 +1,471 @@@
-       sound (self, CH_WEAPON_SINGLE, "weapons/fireball_prefire2.wav", VOL_BASE, ATTN_NORM);
 +#ifdef REGISTER_WEAPON
 +REGISTER_WEAPON(
 +/* WEP_##id */ FIREBALL,
 +/* function */ w_fireball,
 +/* ammotype */ 0,
 +/* impulse  */ 9,
 +/* flags    */ WEP_FLAG_SUPERWEAPON | WEP_TYPE_SPLASH,
 +/* rating   */ BOT_PICKUP_RATING_MID,
 +/* model    */ "fireball",
 +/* netname  */ "fireball",
 +/* fullname */ _("Fireball")
 +);
 +#define FIREBALL_SETTINGS(weapon) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, animtime) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, refire) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, damage) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, damageforcescale) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, speed) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, lifetime) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, laserburntime) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, laserdamage) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, laseredgedamage) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, laserradius) \
 +      WEP_ADD_CVAR(weapon, MO_PRI,  edgedamage) \
 +      WEP_ADD_CVAR(weapon, MO_PRI,  force) \
 +      WEP_ADD_CVAR(weapon, MO_PRI,  radius) \
 +      WEP_ADD_CVAR(weapon, MO_PRI,  health) \
 +      WEP_ADD_CVAR(weapon, MO_PRI,  refire2) \
 +      WEP_ADD_CVAR(weapon, MO_PRI,  bfgdamage) \
 +      WEP_ADD_CVAR(weapon, MO_PRI,  bfgforce) \
 +      WEP_ADD_CVAR(weapon, MO_PRI,  bfgradius) \
 +      WEP_ADD_CVAR(weapon, MO_SEC,  damagetime) \
 +      WEP_ADD_CVAR(weapon, MO_SEC,  speed_up) \
 +      WEP_ADD_PROP(weapon, reloading_ammo, reload_ammo) \
 +      WEP_ADD_PROP(weapon, reloading_time, reload_time) \
 +      WEP_ADD_PROP(weapon, switchdelay_raise, switchdelay_raise) \
 +      WEP_ADD_PROP(weapon, switchdelay_drop, switchdelay_drop)
 +
 +#ifdef SVQC
 +FIREBALL_SETTINGS(fireball)
 +.float bot_primary_fireballmooth; // whatever a mooth is
 +.vector fireball_impactvec;
 +.float fireball_primarytime;
 +#endif
 +#else
 +#ifdef SVQC
 +void spawnfunc_weapon_fireball() { weapon_defaultspawnfunc(WEP_FIREBALL); }
 +
 +void W_Fireball_Explode (void)
 +{
 +      entity e;
 +      float dist;
 +      float points;
 +      vector dir;
 +      float d;
 +
 +      self.event_damage = func_null;
 +      self.takedamage = DAMAGE_NO;
 +
 +      // 1. dist damage
 +      d = (self.realowner.health + self.realowner.armorvalue);
 +      RadiusDamage (self, self.realowner, WEP_CVAR_PRI(fireball, damage), WEP_CVAR_PRI(fireball, edgedamage), WEP_CVAR_PRI(fireball, radius), world, world, WEP_CVAR_PRI(fireball, force), self.projectiledeathtype, other);
 +      if(self.realowner.health + self.realowner.armorvalue >= d)
 +      if(!self.cnt)
 +      {
 +              modeleffect_spawn("models/sphere/sphere.md3", 0, 0, self.origin, '0 0 0', '0 0 0', '0 0 0', 0, WEP_CVAR_PRI(fireball, bfgradius), 0.2, 0.05, 0.25);
 +
 +              // 2. bfg effect
 +              // NOTE: this cannot be made warpzone aware by design. So, better intentionally ignore warpzones here.
 +              for(e = findradius(self.origin, WEP_CVAR_PRI(fireball, bfgradius)); e; e = e.chain)
 +              if(e != self.realowner) if(e.takedamage == DAMAGE_AIM) if(!IS_PLAYER(e) || !self.realowner || IsDifferentTeam(e, self))
 +              {
 +                      // can we see fireball?
 +                      traceline(e.origin + e.view_ofs, self.origin, MOVE_NORMAL, e);
 +                      if(/* trace_startsolid || */ trace_fraction != 1) // startsolid should be never happening anyway
 +                              continue;
 +                      // can we see player who shot fireball?
 +                      traceline(e.origin + e.view_ofs, self.realowner.origin + self.realowner.view_ofs, MOVE_NORMAL, e);
 +                      if(trace_ent != self.realowner)
 +                      if(/* trace_startsolid || */ trace_fraction != 1)
 +                              continue;
 +                      dist = vlen(self.origin - e.origin - e.view_ofs);
 +                      points = (1 - sqrt(dist / WEP_CVAR_PRI(fireball, bfgradius)));
 +                      if(points <= 0)
 +                              continue;
 +                      dir = normalize(e.origin + e.view_ofs - self.origin);
 +
 +                      if(accuracy_isgooddamage(self.realowner, e))
 +                              accuracy_add(self.realowner, WEP_FIREBALL, 0, WEP_CVAR_PRI(fireball, bfgdamage) * points);
 +
 +                      Damage(e, self, self.realowner, WEP_CVAR_PRI(fireball, bfgdamage) * points, self.projectiledeathtype | HITTYPE_BOUNCE | HITTYPE_SPLASH, e.origin + e.view_ofs, WEP_CVAR_PRI(fireball, bfgforce) * dir);
 +                      pointparticles(particleeffectnum("fireball_bfgdamage"), e.origin, -1 * dir, 1);
 +              }
 +      }
 +
 +      remove (self);
 +}
 +
 +void W_Fireball_TouchExplode (void)
 +{
 +      PROJECTILE_TOUCH;
 +      W_Fireball_Explode ();
 +}
 +
 +void W_Fireball_LaserPlay(float dt, float dist, float damage, float edgedamage, float burntime)
 +{
 +      entity e;
 +      float d;
 +      vector p;
 +
 +      if(damage <= 0)
 +              return;
 +
 +      RandomSelection_Init();
 +      for(e = WarpZone_FindRadius(self.origin, dist, TRUE); e; e = e.chain)
 +      if(e != self.realowner) if(e.takedamage == DAMAGE_AIM) if(!IS_PLAYER(e) || !self.realowner || IsDifferentTeam(e, self))
 +      {
 +              p = e.origin;
 +              p_x += e.mins_x + random() * (e.maxs_x - e.mins_x);
 +              p_y += e.mins_y + random() * (e.maxs_y - e.mins_y);
 +              p_z += e.mins_z + random() * (e.maxs_z - e.mins_z);
 +              d = vlen(WarpZone_UnTransformOrigin(e, self.origin) - p);
 +              if(d < dist)
 +              {
 +                      e.fireball_impactvec = p;
 +                      RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e));
 +              }
 +      }
 +      if(RandomSelection_chosen_ent)
 +      {
 +              d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec);
 +              d = damage + (edgedamage - damage) * (d / dist);
 +              Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE);
 +              //trailparticles(self, particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec);
 +              pointparticles(particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1);
 +      }
 +}
 +
 +void W_Fireball_Think()
 +{
 +      if(time > self.pushltime)
 +      {
 +              self.cnt = 1;
 +              self.projectiledeathtype |= HITTYPE_SPLASH;
 +              W_Fireball_Explode();
 +              return;
 +      }
 +
 +      W_Fireball_LaserPlay(0.1, WEP_CVAR_PRI(fireball, laserradius), WEP_CVAR_PRI(fireball, laserdamage), WEP_CVAR_PRI(fireball, laseredgedamage), WEP_CVAR_PRI(fireball, laserburntime));
 +
 +      self.nextthink = time + 0.1;
 +}
 +
 +void W_Fireball_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 +{
 +      if(self.health <= 0)
 +              return;
 +              
 +      if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
 +              return; // g_projectiles_damage says to halt
 +              
 +      self.health = self.health - damage;
 +      if (self.health <= 0)
 +      {
 +              self.cnt = 1;
 +              W_PrepareExplosionByDamage(attacker, W_Fireball_Explode);
 +      }
 +}
 +
 +void W_Fireball_Attack1()
 +{
 +      entity proj;
 +
 +      W_SetupShot_ProjectileSize (self, '-16 -16 -16', '16 16 16', FALSE, 2, "weapons/fireball_fire2.wav", CH_WEAPON_A, WEP_CVAR_PRI(fireball, damage) + WEP_CVAR_PRI(fireball, bfgdamage));
 +
 +      pointparticles(particleeffectnum("fireball_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
 +
 +      proj = spawn ();
 +      proj.classname = "plasma_prim";
 +      proj.owner = proj.realowner = self;
 +      proj.bot_dodge = TRUE;
 +      proj.bot_dodgerating = WEP_CVAR_PRI(fireball, damage);
 +      proj.pushltime = time + WEP_CVAR_PRI(fireball, lifetime);
 +      proj.use = W_Fireball_Explode;
 +      proj.think = W_Fireball_Think;
 +      proj.nextthink = time;
 +      proj.health = WEP_CVAR_PRI(fireball, health);
 +      proj.team = self.team;
 +      proj.event_damage = W_Fireball_Damage;
 +      proj.takedamage = DAMAGE_YES;
 +      proj.damageforcescale = WEP_CVAR_PRI(fireball, damageforcescale);
 +      PROJECTILE_MAKETRIGGER(proj);
 +      proj.projectiledeathtype = WEP_FIREBALL;
 +      setorigin(proj, w_shotorg);
 +
 +      proj.movetype = MOVETYPE_FLY;
 +      W_SETUPPROJECTILEVELOCITY(proj, g_balance_fireball_primary);
 +      proj.angles = vectoangles(proj.velocity);
 +      proj.touch = W_Fireball_TouchExplode;
 +      setsize(proj, '-16 -16 -16', '16 16 16');
 +      proj.flags = FL_PROJECTILE;
 +    proj.missile_flags = MIF_SPLASH | MIF_PROXY;
 +    
 +      CSQCProjectile(proj, TRUE, PROJECTILE_FIREBALL, TRUE);
 +
 +      other = proj; MUTATOR_CALLHOOK(EditProjectile);
 +}
 +
 +void W_Fireball_AttackEffect(float i, vector f_diff)
 +{
 +      W_SetupShot_ProjectileSize (self, '-16 -16 -16', '16 16 16', FALSE, 0, "", 0, 0);
 +      w_shotorg += f_diff_x * v_up + f_diff_y * v_right;
 +      pointparticles(particleeffectnum("fireball_preattack_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
 +}
 +
 +void W_Fireball_Attack1_Frame4()
 +{
 +      W_Fireball_Attack1();
 +      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), w_ready);
 +}
 +
 +void W_Fireball_Attack1_Frame3()
 +{
 +      W_Fireball_AttackEffect(0, '+1.25 +3.75 0');
 +      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame4);
 +}
 +
 +void W_Fireball_Attack1_Frame2()
 +{
 +      W_Fireball_AttackEffect(0, '-1.25 +3.75 0');
 +      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame3);
 +}
 +
 +void W_Fireball_Attack1_Frame1()
 +{
 +      W_Fireball_AttackEffect(1, '+1.25 -3.75 0');
 +      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame2);
 +}
 +
 +void W_Fireball_Attack1_Frame0()
 +{
 +      W_Fireball_AttackEffect(0, '-1.25 -3.75 0');
-                                       sound(self, CH_SHOTS, "weapons/fireball_impact2.wav", VOL_BASE, ATTN_NORM * 0.25); // long range boom
++      sound (self, CH_WEAPON_SINGLE, "weapons/fireball_prefire2.wav", VOL_BASE, ATTEN_NORM);
 +      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame1);
 +}
 +
 +void W_Firemine_Think()
 +{
 +      if(time > self.pushltime)
 +      {
 +              remove(self);
 +              return;
 +      }
 +
 +      // make it "hot" once it leaves its owner
 +      if(self.owner)
 +      {
 +              if(vlen(self.origin - self.owner.origin - self.owner.view_ofs) > WEP_CVAR_SEC(fireball, laserradius))
 +              {
 +                      self.cnt += 1;
 +                      if(self.cnt == 3)
 +                              self.owner = world;
 +              }
 +              else
 +                      self.cnt = 0;
 +      }
 +
 +      W_Fireball_LaserPlay(0.1, WEP_CVAR_SEC(fireball, laserradius), WEP_CVAR_SEC(fireball, laserdamage), WEP_CVAR_SEC(fireball, laseredgedamage), WEP_CVAR_SEC(fireball, laserburntime));
 +
 +      self.nextthink = time + 0.1;
 +}
 +
 +void W_Firemine_Touch (void)
 +{
 +      PROJECTILE_TOUCH;
 +      if (other.takedamage == DAMAGE_AIM)
 +      if(Fire_AddDamage(other, self.realowner, WEP_CVAR_SEC(fireball, damage), WEP_CVAR_SEC(fireball, damagetime), self.projectiledeathtype) >= 0)
 +      {
 +              remove(self);
 +              return;
 +      }
 +      self.projectiledeathtype |= HITTYPE_BOUNCE;
 +}
 +
 +void W_Fireball_Attack2()
 +{
 +      entity proj;
 +      vector f_diff;
 +      float c;
 +
 +      c = mod(self.bulletcounter, 4);
 +      switch(c)
 +      {
 +              case 0:
 +                      f_diff = '-1.25 -3.75 0';
 +                      break;
 +              case 1:
 +                      f_diff = '+1.25 -3.75 0';
 +                      break;
 +              case 2:
 +                      f_diff = '-1.25 +3.75 0';
 +                      break;
 +              case 3:
 +              default:
 +                      f_diff = '+1.25 +3.75 0';
 +                      break;
 +      }
 +      W_SetupShot_ProjectileSize(self, '-4 -4 -4', '4 4 4', FALSE, 2, "weapons/fireball_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(fireball, damage));
 +      traceline(w_shotorg, w_shotorg + f_diff_x * v_up + f_diff_y * v_right, MOVE_NORMAL, self);
 +      w_shotorg = trace_endpos;
 +
 +      pointparticles(particleeffectnum("fireball_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
 +
 +      proj = spawn ();
 +      proj.owner = proj.realowner = self;
 +      proj.classname = "grenade";
 +      proj.bot_dodge = TRUE;
 +      proj.bot_dodgerating = WEP_CVAR_SEC(fireball, damage);
 +      proj.movetype = MOVETYPE_BOUNCE;
 +      proj.projectiledeathtype = WEP_FIREBALL | HITTYPE_SECONDARY;
 +      proj.touch = W_Firemine_Touch;
 +      PROJECTILE_MAKETRIGGER(proj);
 +      setsize(proj, '-4 -4 -4', '4 4 4');
 +      setorigin(proj, w_shotorg);
 +      proj.think = W_Firemine_Think;
 +      proj.nextthink = time;
 +      proj.damageforcescale = WEP_CVAR_SEC(fireball, damageforcescale);
 +      proj.pushltime = time + WEP_CVAR_SEC(fireball, lifetime);
 +      W_SETUPPROJECTILEVELOCITY_UP(proj, g_balance_fireball_secondary);
 +
 +      proj.angles = vectoangles(proj.velocity);
 +      proj.flags = FL_PROJECTILE;
 +    proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC;
 +    
 +      CSQCProjectile(proj, TRUE, PROJECTILE_FIREMINE, TRUE);
 +
 +      other = proj; MUTATOR_CALLHOOK(EditProjectile);
 +}
 +
 +float w_fireball(float req)
 +{
 +      switch(req)
 +      {
 +              case WR_AIM:
 +              {
 +                      self.BUTTON_ATCK = FALSE;
 +                      self.BUTTON_ATCK2 = FALSE;
 +                      if (self.bot_primary_fireballmooth == 0)
 +                      {
 +                              if(bot_aim(WEP_CVAR_PRI(fireball, speed), 0, WEP_CVAR_PRI(fireball, lifetime), FALSE))
 +                              {
 +                                      self.BUTTON_ATCK = TRUE;
 +                                      if(random() < 0.02) self.bot_primary_fireballmooth = 0;
 +                              }
 +                      }
 +                      else
 +                      {
 +                              if(bot_aim(WEP_CVAR_SEC(fireball, speed), WEP_CVAR_SEC(fireball, speed_up), WEP_CVAR_SEC(fireball, lifetime), TRUE))
 +                              {
 +                                      self.BUTTON_ATCK2 = TRUE;
 +                                      if(random() < 0.01) self.bot_primary_fireballmooth = 1;
 +                              }
 +                      }
 +                      
 +                      return TRUE;
 +              }
 +              case WR_THINK:
 +              {
 +                      if (self.BUTTON_ATCK)
 +                      {
 +                              if (time >= self.fireball_primarytime)
 +                              if (weapon_prepareattack(0, WEP_CVAR_PRI(fireball, refire)))
 +                              {
 +                                      W_Fireball_Attack1_Frame0();
 +                                      self.fireball_primarytime = time + WEP_CVAR_PRI(fireball, refire2) * W_WeaponRateFactor();
 +                              }
 +                      }
 +                      else if (self.BUTTON_ATCK2)
 +                      {
 +                              if (weapon_prepareattack(1, WEP_CVAR_SEC(fireball, refire)))
 +                              {
 +                                      W_Fireball_Attack2();
 +                                      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(fireball, animtime), w_ready);
 +                              }
 +                      }
 +                      
 +                      return TRUE;
 +              }
 +              case WR_INIT:
 +              {
 +                      precache_model ("models/weapons/g_fireball.md3");
 +                      precache_model ("models/weapons/v_fireball.md3");
 +                      precache_model ("models/weapons/h_fireball.iqm");
 +                      precache_model ("models/sphere/sphere.md3");
 +                      precache_sound ("weapons/fireball_fire.wav");
 +                      precache_sound ("weapons/fireball_fire2.wav");
 +                      precache_sound ("weapons/fireball_prefire2.wav");
 +                      WEP_SET_PROPS(FIREBALL_SETTINGS(fireball), WEP_FIREBALL)
 +                      return TRUE;
 +              }
 +              case WR_SETUP:
 +              {
 +                      self.current_ammo = ammo_none;
 +                      return TRUE;
 +              }
 +              case WR_CHECKAMMO1:
 +              case WR_CHECKAMMO2:
 +              {
 +                      return TRUE; // fireball has infinite ammo
 +              }
 +              case WR_CONFIG:
 +              {
 +                      WEP_CONFIG_SETTINGS(FIREBALL_SETTINGS(fireball))
 +                      return TRUE;
 +              }
 +              case WR_RESETPLAYER:
 +              {
 +                      self.fireball_primarytime = time;
 +                      return TRUE;
 +              }
 +              case WR_SUICIDEMESSAGE:
 +              {
 +                      if(w_deathtype & HITTYPE_SECONDARY)
 +                              return WEAPON_FIREBALL_SUICIDE_FIREMINE;
 +                      else
 +                              return WEAPON_FIREBALL_SUICIDE_BLAST;
 +              }
 +              case WR_KILLMESSAGE:
 +              {
 +                      if(w_deathtype & HITTYPE_SECONDARY)
 +                              return WEAPON_FIREBALL_MURDER_FIREMINE;
 +                      else
 +                              return WEAPON_FIREBALL_MURDER_BLAST;
 +              }
 +      }
 +      return TRUE;
 +}
 +#endif
 +#ifdef CSQC
 +float w_fireball(float req)
 +{
 +      switch(req)
 +      {
 +              case WR_IMPACTEFFECT:
 +              {
 +                      vector org2;
 +                      if(w_deathtype & HITTYPE_SECONDARY)
 +                      {
 +                              // firemine goes out silently
 +                      }
 +                      else
 +                      {
 +                              org2 = w_org + w_backoff * 16;
 +                              pointparticles(particleeffectnum("fireball_explode"), org2, '0 0 0', 1);
 +                              if(!w_issilent)
++                                      sound(self, CH_SHOTS, "weapons/fireball_impact2.wav", VOL_BASE, ATTEN_NORM * 0.25); // long range boom
 +                      }
 +                      
 +                      return TRUE;
 +              }
 +              case WR_INIT:
 +              {
 +                      precache_sound("weapons/fireball_impact2.wav");
 +                      return TRUE;
 +              }
 +      }
 +
 +      return TRUE;
 +}
 +#endif
 +#endif
index a9b817006d918c9d16328e14d374ec1579170ccb,0000000000000000000000000000000000000000..c80358703919692fab279d2c62a5c37d0e0e487b
mode 100644,000000..100644
--- /dev/null
@@@ -1,355 -1,0 +1,355 @@@
-                               self.hook_state &~= HOOK_PULLING;
 +#ifdef REGISTER_WEAPON
 +REGISTER_WEAPON(
 +/* WEP_##id */ HOOK,
 +/* function */ w_hook,
 +/* ammotype */ IT_CELLS|IT_FUEL,
 +/* impulse  */ 0,
 +/* flags    */ WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
 +/* rating   */ 0,
 +/* model    */ "hookgun",
 +/* netname  */ "hook",
 +/* fullname */ _("Grappling Hook")
 +);
 +
 +#define HOOK_SETTINGS(weapon) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, animtime) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, ammo) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, refire) \
 +      WEP_ADD_CVAR(weapon, MO_PRI,  hooked_ammo) \
 +      WEP_ADD_CVAR(weapon, MO_PRI,  hooked_time_free) \
 +      WEP_ADD_CVAR(weapon, MO_PRI,  hooked_time_max) \
 +      WEP_ADD_CVAR(weapon, MO_SEC,  damage) \
 +      WEP_ADD_CVAR(weapon, MO_SEC,  duration) \
 +      WEP_ADD_CVAR(weapon, MO_SEC,  edgedamage) \
 +      WEP_ADD_CVAR(weapon, MO_SEC,  force) \
 +      WEP_ADD_CVAR(weapon, MO_SEC,  gravity) \
 +      WEP_ADD_CVAR(weapon, MO_SEC,  lifetime) \
 +      WEP_ADD_CVAR(weapon, MO_SEC,  power) \
 +      WEP_ADD_CVAR(weapon, MO_SEC,  radius) \
 +      WEP_ADD_CVAR(weapon, MO_SEC,  speed) \
 +      WEP_ADD_CVAR(weapon, MO_SEC,  health) \
 +      WEP_ADD_CVAR(weapon, MO_SEC,  damageforcescale) \
 +      WEP_ADD_PROP(weapon, reloading_ammo, reload_ammo) \
 +      WEP_ADD_PROP(weapon, reloading_time, reload_time) \
 +      WEP_ADD_PROP(weapon, switchdelay_raise, switchdelay_raise) \
 +      WEP_ADD_PROP(weapon, switchdelay_drop, switchdelay_drop)
 +
 +#ifdef SVQC
 +HOOK_SETTINGS(hook)
 +
 +.float dmg;
 +.float dmg_edge;
 +.float dmg_radius;
 +.float dmg_force;
 +.float dmg_power;
 +.float dmg_duration;
 +.float dmg_last;
 +.float hook_refire;
 +.float hook_time_hooked;
 +.float hook_time_fueldecrease;
 +#endif
 +#else
 +#ifdef SVQC
 +
 +void spawnfunc_weapon_hook()
 +{
 +      if(g_grappling_hook) // offhand hook
 +      {
 +              startitem_failed = TRUE;
 +              remove(self);
 +              return;
 +      }
 +      weapon_defaultspawnfunc(WEP_HOOK);
 +}
 +
 +void W_Hook_ExplodeThink (void)
 +{
 +      float dt, dmg_remaining_next, f;
 +
 +      dt = time - self.teleport_time;
 +      dmg_remaining_next = pow(bound(0, 1 - dt / self.dmg_duration, 1), self.dmg_power);
 +
 +      f = self.dmg_last - dmg_remaining_next;
 +      self.dmg_last = dmg_remaining_next;
 +
 +      RadiusDamage (self, self.realowner, self.dmg * f, self.dmg_edge * f, self.dmg_radius, self.realowner, world, self.dmg_force * f, self.projectiledeathtype, world);
 +      self.projectiledeathtype |= HITTYPE_BOUNCE;
 +      //RadiusDamage (self, world, self.dmg * f, self.dmg_edge * f, self.dmg_radius, world, world, self.dmg_force * f, self.projectiledeathtype, world);
 +
 +      if(dt < self.dmg_duration)
 +              self.nextthink = time + 0.05; // soon
 +      else
 +              remove(self);
 +}
 +
 +void W_Hook_Explode2 (void)
 +{
 +      self.event_damage = func_null;
 +      self.touch = func_null;
 +      self.effects |= EF_NODRAW;
 +
 +      self.think = W_Hook_ExplodeThink;
 +      self.nextthink = time;
 +      self.dmg = WEP_CVAR_SEC(hook, damage);
 +      self.dmg_edge = WEP_CVAR_SEC(hook, edgedamage);
 +      self.dmg_radius = WEP_CVAR_SEC(hook, radius);
 +      self.dmg_force = WEP_CVAR_SEC(hook, force);
 +      self.dmg_power = WEP_CVAR_SEC(hook, power);
 +      self.dmg_duration = WEP_CVAR_SEC(hook, duration);
 +      self.teleport_time = time;
 +      self.dmg_last = 1;
 +      self.movetype = MOVETYPE_NONE;
 +}
 +
 +void W_Hook_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 +{
 +      if (self.health <= 0)
 +              return;
 +              
 +      if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
 +              return; // g_projectiles_damage says to halt    
 +      
 +      self.health = self.health - damage;
 +      
 +      if (self.health <= 0)
 +              W_PrepareExplosionByDamage(self.realowner, W_Hook_Explode2);
 +}
 +
 +void W_Hook_Touch2 (void)
 +{
 +      PROJECTILE_TOUCH;
 +      self.use();
 +}
 +
 +void W_Hook_Attack2()
 +{
 +      entity gren;
 +
 +      W_DecreaseAmmo(ammo_cells, WEP_CVAR_SEC(hook, ammo), FALSE);
 +      W_SetupShot (self, FALSE, 4, "weapons/hookbomb_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(hook, damage));
 +
 +      gren = spawn ();
 +      gren.owner = gren.realowner = self;
 +      gren.classname = "hookbomb";
 +      gren.bot_dodge = TRUE;
 +      gren.bot_dodgerating = WEP_CVAR_SEC(hook, damage);
 +      gren.movetype = MOVETYPE_TOSS;
 +      PROJECTILE_MAKETRIGGER(gren);
 +      gren.projectiledeathtype = WEP_HOOK | HITTYPE_SECONDARY;
 +      setorigin(gren, w_shotorg);
 +      setsize(gren, '0 0 0', '0 0 0');
 +
 +      gren.nextthink = time + WEP_CVAR_SEC(hook, lifetime);
 +      gren.think = adaptor_think2use_hittype_splash;
 +      gren.use = W_Hook_Explode2;
 +      gren.touch = W_Hook_Touch2;
 +      
 +      gren.takedamage = DAMAGE_YES;
 +      gren.health = WEP_CVAR_SEC(hook, health);
 +      gren.damageforcescale = WEP_CVAR_SEC(hook, damageforcescale);
 +      gren.event_damage = W_Hook_Damage;
 +      gren.damagedbycontents = TRUE;
 +      gren.missile_flags = MIF_SPLASH | MIF_ARC;
 +
 +      gren.velocity = '0 0 1' * WEP_CVAR_SEC(hook, speed);
 +      if(autocvar_g_projectiles_newton_style)
 +              gren.velocity = gren.velocity + self.velocity;
 +
 +      gren.gravity = WEP_CVAR_SEC(hook, gravity);
 +      //W_SetupProjectileVelocity(gren); // just falling down!
 +
 +      gren.angles = '0 0 0';
 +      gren.flags = FL_PROJECTILE;
 +
 +      CSQCProjectile(gren, TRUE, PROJECTILE_HOOKBOMB, TRUE);
 +
 +      other = gren; MUTATOR_CALLHOOK(EditProjectile);
 +}
 +
 +float w_hook(float req)
 +{
 +      float hooked_time_max, hooked_fuel;
 +              
 +      switch(req)
 +      {
 +              case WR_AIM:
 +              {
 +                      // no bot AI for hook (yet?)
 +                      return TRUE;
 +              }
 +              case WR_THINK:
 +              {
 +                      if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
 +                      {
 +                              if(!self.hook)
 +                              if not(self.hook_state & HOOK_WAITING_FOR_RELEASE)
 +                              if not(self.hook_state & HOOK_FIRING)
 +                              if (time > self.hook_refire)
 +                              if (weapon_prepareattack(0, -1))
 +                              {
 +                                      W_DecreaseAmmo(ammo_fuel, WEP_CVAR_PRI(hook, ammo), FALSE);
 +                                      self.hook_state |= HOOK_FIRING;
 +                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hook, animtime), w_ready);                             
 +                              }
 +                      }
 +
 +                      if (self.BUTTON_ATCK2)
 +                      {
 +                              if (weapon_prepareattack(1, WEP_CVAR_SEC(hook, refire)))
 +                              {
 +                                      W_Hook_Attack2();
 +                                      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(hook, animtime), w_ready);
 +                              }
 +                      }
 +
 +                      if(self.hook)
 +                      {
 +                              // if hooked, no bombs, and increase the timer
 +                              self.hook_refire = max(self.hook_refire, time + WEP_CVAR_PRI(hook, refire) * W_WeaponRateFactor());
 +
 +                              // hook also inhibits health regeneration, but only for 1 second
 +                              if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
 +                                      self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
 +                      }
 +
 +                      if(self.hook && self.hook.state == 1)
 +                      {
 +                              hooked_time_max = WEP_CVAR_PRI(hook, hooked_time_max);                  
 +                              if (hooked_time_max > 0)
 +                              {
 +                                      if ( time > self.hook_time_hooked + hooked_time_max )
 +                                              self.hook_state |= HOOK_REMOVING;
 +                              }
 +                              
 +                              hooked_fuel = WEP_CVAR_PRI(hook, hooked_ammo);
 +                              if (hooked_fuel > 0)
 +                              {
 +                                      if ( time > self.hook_time_fueldecrease )
 +                                      {
 +                                              if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
 +                                              {
 +                                                      if ( self.ammo_fuel >= (time - self.hook_time_fueldecrease) * hooked_fuel )
 +                                                      {
 +                                                              W_DecreaseAmmo(ammo_fuel, (time - self.hook_time_fueldecrease) * hooked_fuel, FALSE);
 +                                                              self.hook_time_fueldecrease = time;
 +                                                              // decrease next frame again
 +                                                      }
 +                                                      else
 +                                                      {
 +                                                              self.ammo_fuel = 0;
 +                                                              self.hook_state |= HOOK_REMOVING;
 +                                                              W_SwitchWeapon_Force(self, w_getbestweapon(self));
 +                                                      }
 +                                              }
 +                                      }
 +                              }
 +                      }
 +                      else
 +                      {
 +                              self.hook_time_hooked = time;                           
 +                              self.hook_time_fueldecrease = time + WEP_CVAR_PRI(hook, hooked_time_free);
 +                      }
 +
 +                      if (self.BUTTON_CROUCH)
 +                      {
-                                       self.hook_state &~= HOOK_RELEASING;
++                              self.hook_state &= ~HOOK_PULLING;
 +                              if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
-                               self.hook_state &~= HOOK_RELEASING;
++                                      self.hook_state &= ~HOOK_RELEASING;
 +                              else
 +                                      self.hook_state |= HOOK_RELEASING;
 +                      }
 +                      else
 +                      {
 +                              self.hook_state |= HOOK_PULLING;
-                                       self.hook_state &~= HOOK_WAITING_FOR_RELEASE;
++                              self.hook_state &= ~HOOK_RELEASING;
 +
 +                              if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
 +                              {
 +                                      // already fired
 +                                      if(self.hook)
 +                                              self.hook_state |= HOOK_WAITING_FOR_RELEASE;
 +                              }
 +                              else
 +                              {
 +                                      self.hook_state |= HOOK_REMOVING;
-                       self.hook_state &~= HOOK_WAITING_FOR_RELEASE;
++                                      self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
 +                              }
 +                      }
 +                      
 +                      return TRUE;
 +              }
 +              case WR_INIT:
 +              {
 +                      precache_model ("models/weapons/g_hookgun.md3");
 +                      precache_model ("models/weapons/v_hookgun.md3");
 +                      precache_model ("models/weapons/h_hookgun.iqm");
 +                      precache_sound ("weapons/hook_impact.wav"); // done by g_hook.qc
 +                      precache_sound ("weapons/hook_fire.wav");
 +                      precache_sound ("weapons/hookbomb_fire.wav");
 +                      WEP_SET_PROPS(HOOK_SETTINGS(hook), WEP_HOOK)
 +                      return TRUE;
 +              }
 +              case WR_SETUP:
 +              {
 +                      self.current_ammo = ammo_fuel;
++                      self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
 +                      return TRUE;
 +              }
 +              case WR_CHECKAMMO1:
 +              {
 +                      if(self.hook)
 +                              return self.ammo_fuel > 0;
 +                      else
 +                              return self.ammo_fuel >= WEP_CVAR_PRI(hook, ammo);
 +              }
 +              case WR_CHECKAMMO2:
 +              {
 +                      return self.ammo_cells >= WEP_CVAR_SEC(hook, ammo);
 +              }
 +              case WR_CONFIG:
 +              {
 +                      WEP_CONFIG_SETTINGS(HOOK_SETTINGS(hook))
 +                      return TRUE;
 +              }
 +              case WR_RESETPLAYER:
 +              {
 +                      self.hook_refire = time;
 +                      return TRUE;
 +              }
 +              case WR_SUICIDEMESSAGE:
 +              {
 +                      return FALSE;
 +              }
 +              case WR_KILLMESSAGE:
 +              {
 +                      return WEAPON_HOOK_MURDER;
 +              }
 +      }
 +      return TRUE;
 +}
 +#endif
 +#ifdef CSQC
 +float w_hook(float req)
 +{
 +      switch(req)
 +      {
 +              case WR_IMPACTEFFECT:
 +              {
 +                      vector org2;
 +                      org2 = w_org + w_backoff * 2;
 +                      pointparticles(particleeffectnum("hookbomb_explode"), org2, '0 0 0', 1);
 +                      if(!w_issilent)
 +                              sound(self, CH_SHOTS, "weapons/hookbomb_impact.wav", VOL_BASE, ATTN_NORM);
 +                              
 +                      return TRUE;
 +              }
 +              case WR_INIT:
 +              {
 +                      precache_sound("weapons/hookbomb_impact.wav");
 +                      return TRUE;
 +              }
 +      }
 +      return TRUE;
 +}
 +#endif
 +#endif
index 5625a40a1190f0610134bf848cb3a1061dfd74a6,0000000000000000000000000000000000000000..53d28c0b807c6c3eeb6b65438795d32c658fc3c7
mode 100644,000000..100644
--- /dev/null
@@@ -1,410 -1,0 +1,410 @@@
-       if(self.cnt < 0 && !failhard && self.realowner.playerid == self.playerid && self.realowner.deadflag == DEAD_NO && !WEPSET_CONTAINS_EW(self.realowner, WEP_PORTO))
 +#ifdef REGISTER_WEAPON
 +REGISTER_WEAPON(
 +/* WEP_##id */ PORTO,
 +/* function */ w_porto,
 +/* ammotype */ 0,
 +/* impulse  */ 0,
 +/* flags    */ WEP_TYPE_OTHER | WEP_FLAG_SUPERWEAPON,
 +/* rating   */ 0,
 +/* model    */ "porto" ,
 +/* netname  */ "porto",
 +/* fullname */ _("Port-O-Launch")
 +);
 +
 +#define PORTO_SETTINGS(weapon) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, animtime) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, lifetime) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, refire) \
 +      WEP_ADD_CVAR(weapon, MO_BOTH, speed) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, secondary) \
 +      WEP_ADD_PROP(weapon, reloading_ammo, reload_ammo) \
 +      WEP_ADD_PROP(weapon, reloading_time, reload_time) \
 +      WEP_ADD_PROP(weapon, switchdelay_raise, switchdelay_raise) \
 +      WEP_ADD_PROP(weapon, switchdelay_drop, switchdelay_drop)
 +
 +#ifdef SVQC
 +PORTO_SETTINGS(porto)
 +.entity porto_current;
 +.vector porto_v_angle; // holds "held" view angles
 +.float porto_v_angle_held;
 +.vector right_vector;
 +#endif
 +#else
 +#ifdef SVQC
 +void spawnfunc_weapon_porto (void) { weapon_defaultspawnfunc(WEP_PORTO); }
 +
 +void W_Porto_Success (void)
 +{
 +      if(self.realowner == world)
 +      {
 +              objerror("Cannot succeed successfully: no owner\n");
 +              return;
 +      }
 +
 +      self.realowner.porto_current = world;
 +      remove(self);
 +}
 +
 +string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo);
 +void W_Porto_Fail (float failhard)
 +{
 +      if(self.realowner == world)
 +      {
 +              objerror("Cannot fail successfully: no owner\n");
 +              return;
 +      }
 +
 +      // no portals here!
 +      if(self.cnt < 0)
 +      {
 +              Portal_ClearWithID(self.realowner, self.portal_id);
 +      }
 +
 +      self.realowner.porto_current = world;
 +
-               sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTN_NORM);
++      if(self.cnt < 0 && !failhard && self.realowner.playerid == self.playerid && self.realowner.deadflag == DEAD_NO && !(self.realowner.weapons & WEPSET_PORTO))
 +      {
 +              setsize (self, '-16 -16 0', '16 16 32');
 +              setorigin(self, self.origin + trace_plane_normal);
 +              if(move_out_of_solid(self))
 +              {
 +                      self.flags = FL_ITEM;
 +                      self.velocity = trigger_push_calculatevelocity(self.origin, self.realowner, 128);
 +                      tracetoss(self, self);
 +                      if(vlen(trace_endpos - self.realowner.origin) < 128)
 +                      {
 +                              W_ThrowNewWeapon(self.realowner, WEP_PORTO, 0, self.origin, self.velocity);
 +                              centerprint(self.realowner, "^1Portal deployment failed.\n\n^2Catch it to try again!");
 +                      }
 +              }
 +      }
 +      remove(self);
 +}
 +
 +void W_Porto_Remove (entity p)
 +{
 +      if(p.porto_current.realowner == p && p.porto_current.classname == "porto")
 +      {
 +              entity oldself;
 +              oldself = self;
 +              self = p.porto_current;
 +              W_Porto_Fail(1);
 +              self = oldself;
 +      }
 +}
 +
 +void W_Porto_Think (void)
 +{
 +      trace_plane_normal = '0 0 0';
 +      if(self.realowner.playerid != self.playerid)
 +              remove(self);
 +      else
 +              W_Porto_Fail(0);
 +}
 +
 +void W_Porto_Touch (void)
 +{
 +      vector norm;
 +
 +      // do not use PROJECTILE_TOUCH here
 +      // FIXME but DO handle warpzones!
 +
 +      if(other.classname == "portal")
 +              return; // handled by the portal
 +
 +      norm = trace_plane_normal;
 +      if(trace_ent.iscreature)
 +      {
 +              traceline(trace_ent.origin, trace_ent.origin + '0 0 2' * PL_MIN_z, MOVE_WORLDONLY, self);
 +              if(trace_fraction >= 1)
 +                      return;
 +              if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
 +                      return;
 +              if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
 +                      return;
 +      }
 +
 +      if(self.realowner.playerid != self.playerid)
 +      {
-               spamsound(self, CH_SHOTS, "porto/bounce.wav", VOL_BASE, ATTN_NORM);
++              sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
 +              remove(self);
 +      }
 +      else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
 +      {
-               sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTN_NORM);
++              spamsound(self, CH_SHOTS, "porto/bounce.wav", VOL_BASE, ATTEN_NORM);
 +              // just reflect
 +              self.right_vector = self.right_vector - 2 * trace_plane_normal * (self.right_vector * trace_plane_normal);
 +              self.angles = vectoangles(self.velocity - 2 * trace_plane_normal * (self.velocity * trace_plane_normal));
 +      }
 +      else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
 +      {
-                       sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTN_NORM);
++              sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
 +              W_Porto_Fail(0);
 +              if(self.cnt < 0)
 +                      Portal_ClearAll_PortalsOnly(self.realowner);
 +      }
 +      else if(self.cnt == 0)
 +      {
 +              // in-portal only
 +              if(Portal_SpawnInPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
 +              {
-                       sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTN_NORM);
++                      sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
 +                      trace_plane_normal = norm;
 +                      centerprint(self.realowner, "^1In^7-portal created.");
 +                      W_Porto_Success();
 +              }
 +              else
 +              {
-                       sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTN_NORM);
++                      sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
 +                      trace_plane_normal = norm;
 +                      W_Porto_Fail(0);
 +              }
 +      }
 +      else if(self.cnt == 1)
 +      {
 +              // out-portal only
 +              if(Portal_SpawnOutPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
 +              {
-                       sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTN_NORM);
++                      sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
 +                      trace_plane_normal = norm;
 +                      centerprint(self.realowner, "^4Out^7-portal created.");
 +                      W_Porto_Success();
 +              }
 +              else
 +              {
-                       sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTN_NORM);
++                      sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
 +                      trace_plane_normal = norm;
 +                      W_Porto_Fail(0);
 +              }
 +      }
 +      else if(self.effects & EF_RED)
 +      {
 +              self.effects += EF_BLUE - EF_RED;
 +              if(Portal_SpawnInPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
 +              {
-                       sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTN_NORM);
++                      sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
 +                      trace_plane_normal = norm;
 +                      centerprint(self.realowner, "^1In^7-portal created.");
 +                      self.right_vector = self.right_vector - 2 * trace_plane_normal * (self.right_vector * norm);
 +                      self.angles = vectoangles(self.velocity - 2 * trace_plane_normal * (self.velocity * norm));
 +                      CSQCProjectile(self, TRUE, PROJECTILE_PORTO_BLUE, TRUE); // change type
 +              }
 +              else
 +              {
-                               sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTN_NORM);
++                      sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
 +                      trace_plane_normal = norm;
 +                      Portal_ClearAll_PortalsOnly(self.realowner);
 +                      W_Porto_Fail(0);
 +              }
 +      }
 +      else
 +      {
 +              if(self.realowner.portal_in.portal_id == self.portal_id)
 +              {
 +                      if(Portal_SpawnOutPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
 +                      {
-                               sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTN_NORM);
++                              sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
 +                              trace_plane_normal = norm;
 +                              centerprint(self.realowner, "^4Out^7-portal created.");
 +                              W_Porto_Success();
 +                      }
 +                      else
 +                      {
-                       sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTN_NORM);
++                              sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
 +                              Portal_ClearAll_PortalsOnly(self.realowner);
 +                              W_Porto_Fail(0);
 +                      }
 +              }
 +              else
 +              {
++                      sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
 +                      Portal_ClearAll_PortalsOnly(self.realowner);
 +                      W_Porto_Fail(0);
 +              }
 +      }
 +}
 +
 +void W_Porto_Attack (float type)
 +{
 +      entity gren;
 +
 +      W_SetupShot (self, FALSE, 4, "porto/fire.wav", CH_WEAPON_A, 0);
 +      // always shoot from the eye
 +      w_shotdir = v_forward;
 +      w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward;
 +
 +      //pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
 +
 +      gren = spawn ();
 +      gren.cnt = type;
 +      gren.owner = gren.realowner = self;
 +      gren.playerid = self.playerid;
 +      gren.classname = "porto";
 +      gren.bot_dodge = TRUE;
 +      gren.bot_dodgerating = 200;
 +      gren.movetype = MOVETYPE_BOUNCEMISSILE;
 +      PROJECTILE_MAKETRIGGER(gren);
 +      gren.effects = EF_RED;
 +      gren.scale = 4;
 +      setorigin(gren, w_shotorg);
 +      setsize(gren, '0 0 0', '0 0 0');
 +      
 +      gren.nextthink = time + WEP_CVAR_BOTH(porto, (type <= 0), lifetime);
 +      gren.think = W_Porto_Think;
 +      gren.touch = W_Porto_Touch;
 +      
 +      if(self.items & IT_STRENGTH)
 +              W_SetupProjectileVelocity(gren, WEP_CVAR_BOTH(porto, (type <= 0), speed) * autocvar_g_balance_powerup_strength_force, 0);
 +      else
 +              W_SetupProjectileVelocity(gren, WEP_CVAR_BOTH(porto, (type <= 0), speed), 0);
 +
 +      gren.angles = vectoangles (gren.velocity);
 +      gren.flags = FL_PROJECTILE;
 +
 +      gren.portal_id = time;
 +      self.porto_current = gren;
 +      gren.playerid = self.playerid;
 +      fixedmakevectors(fixedvectoangles(gren.velocity));
 +      gren.right_vector = v_right;
 +
 +      gren.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP;
 +
 +      if(type > 0)
 +              CSQCProjectile(gren, TRUE, PROJECTILE_PORTO_BLUE, TRUE);
 +      else
 +              CSQCProjectile(gren, TRUE, PROJECTILE_PORTO_RED, TRUE);
 +
 +      other = gren; MUTATOR_CALLHOOK(EditProjectile);
 +}
 +
 +float w_nexball_weapon(float req);
 +float w_porto(float req)
 +{
 +      //vector v_angle_save;
 +
 +      if (g_nexball) { return w_nexball_weapon(req); }
 +      
 +      switch(req)
 +      {
 +              case WR_AIM:
 +              {
 +                      self.BUTTON_ATCK = FALSE;
 +                      self.BUTTON_ATCK2 = FALSE;
 +                      if(!WEP_CVAR(porto, secondary))
 +                              if(bot_aim(WEP_CVAR_PRI(porto, speed), 0, WEP_CVAR_PRI(porto, lifetime), FALSE))
 +                                      self.BUTTON_ATCK = TRUE;
 +                                      
 +                      return TRUE;
 +              }
 +              case WR_CONFIG:
 +              {
 +                      WEP_CONFIG_SETTINGS(PORTO_SETTINGS(porto))
 +                      return TRUE;
 +              }
 +              case WR_THINK:
 +              {
 +                      if(WEP_CVAR(porto, secondary))
 +                      {
 +                              if (self.BUTTON_ATCK)
 +                              if (!self.porto_current)
 +                              if (!self.porto_forbidden)
 +                              if (weapon_prepareattack(0, WEP_CVAR_PRI(porto, refire)))
 +                              {
 +                                      W_Porto_Attack(0);
 +                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready);
 +                              }
 +
 +                              if (self.BUTTON_ATCK2)
 +                              if (!self.porto_current)
 +                              if (!self.porto_forbidden)
 +                              if (weapon_prepareattack(1, WEP_CVAR_SEC(porto, refire)))
 +                              {
 +                                      W_Porto_Attack(1);
 +                                      weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(porto, animtime), w_ready);
 +                              }
 +                      }
 +                      else
 +                      {
 +                              if(self.porto_v_angle_held)
 +                              {
 +                                      if(!self.BUTTON_ATCK2)
 +                                      {
 +                                              self.porto_v_angle_held = 0;
 +
 +                                              ClientData_Touch(self);
 +                                      }
 +                              }
 +                              else
 +                              {
 +                                      if(self.BUTTON_ATCK2)
 +                                      {
 +                                              self.porto_v_angle = self.v_angle;
 +                                              self.porto_v_angle_held = 1;
 +
 +                                              ClientData_Touch(self);
 +                                      }
 +                              }
 +                              if(self.porto_v_angle_held)
 +                                      makevectors(self.porto_v_angle); // override the previously set angles
 +
 +                              if (self.BUTTON_ATCK)
 +                              if (!self.porto_current)
 +                              if (!self.porto_forbidden)
 +                              if (weapon_prepareattack(0, WEP_CVAR_PRI(porto, refire)))
 +                              {
 +                                      W_Porto_Attack(-1);
 +                                      weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready);
 +                              }
 +                      }
 +                      
 +                      return TRUE;
 +              }
 +              case WR_INIT:
 +              {
 +                      precache_model ("models/weapons/g_porto.md3");
 +                      precache_model ("models/weapons/v_porto.md3");
 +                      precache_model ("models/weapons/h_porto.iqm");
 +                      precache_model ("models/portal.md3");
 +                      precache_sound ("porto/bounce.wav");
 +                      precache_sound ("porto/create.wav");
 +                      precache_sound ("porto/expire.wav");
 +                      precache_sound ("porto/explode.wav");
 +                      precache_sound ("porto/fire.wav");
 +                      precache_sound ("porto/unsupported.wav");
 +                      WEP_SET_PROPS(PORTO_SETTINGS(porto), WEP_PORTO)
 +                      return TRUE;
 +              }
 +              case WR_SETUP:
 +              {
 +                      self.current_ammo = ammo_none;
 +                      return TRUE;
 +              }
 +              case WR_RESETPLAYER:
 +              {
 +                      self.porto_current = world;
 +                      return TRUE;
 +              }
 +      }
 +      return TRUE;
 +}
 +#endif
 +#ifdef CSQC
 +float w_porto(float req)
 +{
 +      switch(req)
 +      {
 +              case WR_IMPACTEFFECT:
 +              {
 +                      print("Since when does Porto send DamageInfo?\n");
 +                      return TRUE;
 +              }
 +              case WR_INIT:
 +              {
 +                      // nothing to do
 +                      return TRUE;
 +              }
 +      }
 +      return TRUE;
 +}
 +#endif
 +#endif
index 55ad43459ee3b188e5fc0df16047b43c5dcc21d9,0000000000000000000000000000000000000000..aa3d69dc5d455fc2b9b8a42090fd3cd7291bf9eb
mode 100644,000000..100644
--- /dev/null
@@@ -1,779 -1,0 +1,779 @@@
-                                               sound(self, CH_SHOTS, "weapons/tag_impact.wav", 1, ATTN_NORM);
 +#ifdef REGISTER_WEAPON
 +REGISTER_WEAPON(
 +/* WEP_##id */ SEEKER,
 +/* function */ w_seeker,
 +/* ammotype */ IT_ROCKETS,
 +/* impulse  */ 8,
 +/* flags    */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH,
 +/* rating   */ BOT_PICKUP_RATING_MID,
 +/* model    */ "seeker",
 +/* netname  */ "seeker",
 +/* fullname */ _("T.A.G. Seeker")
 +);
 +
 +#define SEEKER_SETTINGS(weapon) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, type) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, flac_ammo) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, flac_animtime) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, flac_damage) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, flac_edgedamage) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, flac_force) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, flac_lifetime) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, flac_lifetime_rand) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, flac_radius) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, flac_refire) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_accel) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_ammo) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_animtime) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_count) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_damage) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_damageforcescale) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_decel) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_delay) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_edgedamage) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_force) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_health) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_lifetime) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_proxy) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_proxy_delay) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_proxy_maxrange) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_radius) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_refire) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_smart) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_smart_mindist) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_smart_trace_max) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_smart_trace_min) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_speed_max) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, missile_turnrate) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, tag_ammo) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, tag_animtime) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, tag_damageforcescale) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, tag_health) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, tag_lifetime) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, tag_refire) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, tag_speed) \
 +      WEP_ADD_CVAR(weapon, MO_NONE, tag_tracker_lifetime) \
 +      WEP_ADD_PROP(weapon, reloading_ammo, reload_ammo) \
 +      WEP_ADD_PROP(weapon, reloading_time, reload_time) \
 +      WEP_ADD_PROP(weapon, switchdelay_raise, switchdelay_raise) \
 +      WEP_ADD_PROP(weapon, switchdelay_drop, switchdelay_drop)
 +
 +#ifdef SVQC
 +SEEKER_SETTINGS(seeker)
 +.entity tag_target, wps_tag_tracker;
 +.float tag_time;
 +#endif
 +#else
 +#ifdef SVQC
 +void spawnfunc_weapon_seeker (void) { weapon_defaultspawnfunc(WEP_SEEKER); }
 +
 +// ============================
 +// Begin: Missile functions, these are general functions to be manipulated by other code
 +// ============================
 +void Seeker_Missile_Explode ()
 +{
 +      self.event_damage = func_null;
 +      RadiusDamage (self, self.realowner, WEP_CVAR(seeker, missile_damage), WEP_CVAR(seeker, missile_edgedamage), WEP_CVAR(seeker, missile_radius), world, world, WEP_CVAR(seeker, missile_force), self.projectiledeathtype, other);
 +
 +      remove (self);
 +}
 +
 +void Seeker_Missile_Touch()
 +{
 +      PROJECTILE_TOUCH;
 +
 +      Seeker_Missile_Explode();
 +}
 +
 +void Seeker_Missile_Think()
 +{
 +      entity e;
 +      vector desireddir, olddir, newdir, eorg;
 +      float turnrate;
 +      float dist;
 +      float spd;
 +
 +      if (time > self.cnt)
 +      {
 +              self.projectiledeathtype |= HITTYPE_SPLASH;
 +              Seeker_Missile_Explode();
 +      }
 +
 +      spd = vlen(self.velocity);
 +      spd = bound(
 +              spd - WEP_CVAR(seeker, missile_decel) * frametime,
 +              WEP_CVAR(seeker, missile_speed_max),
 +              spd + WEP_CVAR(seeker, missile_accel) * frametime
 +      );
 +
 +      if (self.enemy != world)
 +              if (self.enemy.takedamage != DAMAGE_AIM || self.enemy.deadflag != DEAD_NO)
 +                      self.enemy = world;
 +
 +      if (self.enemy != world)
 +      {
 +              e               = self.enemy;
 +              eorg            = 0.5 * (e.absmin + e.absmax);
 +              turnrate        = WEP_CVAR(seeker, missile_turnrate); // how fast to turn
 +              desireddir      = normalize(eorg - self.origin);
 +              olddir          = normalize(self.velocity); // get my current direction
 +              dist            = vlen(eorg - self.origin);
 +
 +              // Do evasive maneuvers for world objects? ( this should be a cpu hog. :P )
 +              if (WEP_CVAR(seeker, missile_smart) && (dist > WEP_CVAR(seeker, missile_smart_mindist)))
 +              {
 +                      // Is it a better idea (shorter distance) to trace to the target itself?
 +                      if ( vlen(self.origin + olddir * self.wait) < dist)
 +                              traceline(self.origin, self.origin + olddir * self.wait, FALSE, self);
 +                      else
 +                              traceline(self.origin, eorg, FALSE, self);
 +
 +                      // Setup adaptive tracelength
 +                      self.wait = bound(WEP_CVAR(seeker, missile_smart_trace_min), vlen(self.origin - trace_endpos), self.wait = WEP_CVAR(seeker, missile_smart_trace_max));
 +
 +                      // Calc how important it is that we turn and add this to the desierd (enemy) dir.
 +                      desireddir  = normalize(((trace_plane_normal * (1 - trace_fraction)) + (desireddir * trace_fraction)) * 0.5);
 +              }
 +              
 +              newdir = normalize(olddir + desireddir * turnrate); // take the average of the 2 directions; not the best method but simple & easy
 +              self.velocity = newdir * spd; // make me fly in the new direction at my flight speed
 +      }
 +      else
 +              dist = 0;
 +
 +      // Proxy
 +      if (WEP_CVAR(seeker, missile_proxy))
 +      {
 +              if ( dist <= WEP_CVAR(seeker, missile_proxy_maxrange))
 +              {
 +                      if (self.autoswitch == 0)
 +                      {
 +                              self.autoswitch = time + WEP_CVAR(seeker, missile_proxy_delay);
 +                      }
 +                      else
 +                      {
 +                              if (self.autoswitch <= time)
 +                              {
 +                                      Seeker_Missile_Explode();
 +                                      self.autoswitch = 0;
 +                              }
 +                      }
 +              }
 +              else
 +              {
 +                      if (self.autoswitch != 0)
 +                              self.autoswitch = 0;
 +              }
 +      }
 +      ///////////////
 +
 +      if (self.enemy.deadflag != DEAD_NO)
 +      {
 +              self.enemy = world;
 +              self.cnt = time + 1 + (random() * 4);
 +              self.nextthink = self.cnt;
 +              return;
 +      }
 +
 +      //self.angles = vectoangles(self.velocity);                     // turn model in the new flight direction
 +      self.nextthink = time;// + 0.05; // csqc projectiles
 +      UpdateCSQCProjectile(self);
 +}
 +
 +
 +
 +void Seeker_Missile_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 +{
 +      if (self.health <= 0)
 +              return;
 +              
 +      if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
 +              return; // g_projectiles_damage says to halt
 +
 +      if (self.realowner == attacker)
 +              self.health = self.health - (damage * 0.25);
 +      else
 +              self.health = self.health - damage;
 +              
 +      if (self.health <= 0)
 +              W_PrepareExplosionByDamage(attacker, Seeker_Missile_Explode);
 +}
 +
 +/*
 +void Seeker_Missile_Animate()
 +{
 +      self.frame = self.frame +1;
 +      self.nextthink = time + 0.05;
 +
 +      if (self.enemy != world)
 +              if (self.enemy.takedamage != DAMAGE_AIM || self.enemy.deadflag != DEAD_NO)
 +                      self.enemy = world;
 +
 +      if(self.frame == 5)
 +      {
 +              self.think           = Seeker_Missile_Think;
 +              self.nextthink       = time;// + cvar("g_balance_seeker_missile_activate_delay"); // cant dealy with csqc projectiles
 +
 +              if (autocvar_g_balance_seeker_missile_proxy)
 +                      self.movetype    = MOVETYPE_BOUNCEMISSILE;
 +              else
 +                      self.movetype    = MOVETYPE_FLYMISSILE;
 +      }
 +
 +      UpdateCSQCProjectile(self);
 +}
 +*/
 +
 +void Seeker_Fire_Missile(vector f_diff, entity m_target)
 +{
 +      entity missile;
 +
 +      W_DecreaseAmmo(ammo_rockets, WEP_CVAR(seeker, missile_ammo), autocvar_g_balance_seeker_reload_ammo);
 +
 +      makevectors(self.v_angle);
 +      W_SetupShot_ProjectileSize (self, '-2 -2 -2', '2 2 2', FALSE, 2, "weapons/seeker_fire.wav", CH_WEAPON_A, 0);
 +      w_shotorg += f_diff;
 +      pointparticles(particleeffectnum("seeker_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
 +
 +      //self.detornator         = FALSE;
 +
 +      missile                 = spawn();
 +      missile.owner           = missile.realowner = self;
 +      missile.classname       = "seeker_missile";
 +      missile.bot_dodge       = TRUE;
 +      missile.bot_dodgerating = WEP_CVAR(seeker, missile_damage);
 +
 +      missile.think           = Seeker_Missile_Think;
 +      missile.touch           = Seeker_Missile_Touch;
 +      missile.event_damage    = Seeker_Missile_Damage;
 +      missile.nextthink       = time;// + 0.2;// + cvar("g_balance_seeker_missile_activate_delay");
 +      missile.cnt             = time + WEP_CVAR(seeker, missile_lifetime);
 +      missile.enemy           = m_target;
 +      missile.solid           = SOLID_BBOX;
 +      missile.scale           = 2;
 +      missile.takedamage      = DAMAGE_YES;
 +      missile.health          = WEP_CVAR(seeker, missile_health);
 +      missile.damageforcescale = WEP_CVAR(seeker, missile_damageforcescale);
 +      missile.damagedbycontents = TRUE;
 +      //missile.think           = Seeker_Missile_Animate; // csqc projectiles.
 +      
 +      if (missile.enemy != world)
 +              missile.projectiledeathtype = WEP_SEEKER | HITTYPE_SECONDARY;
 +      else 
 +              missile.projectiledeathtype = WEP_SEEKER;
 +
 +
 +      setorigin (missile, w_shotorg);
 +      setsize (missile, '-4 -4 -4', '4 4 4');
 +      missile.movetype    = MOVETYPE_FLYMISSILE;
 +      missile.flags       = FL_PROJECTILE;
 +      missile.missile_flags = MIF_SPLASH | MIF_GUIDED_TAG;
 +      
 +      W_SETUPPROJECTILEVELOCITY_UP(missile, g_balance_seeker_missile);
 +
 +      missile.angles = vectoangles (missile.velocity);
 +
 +      CSQCProjectile(missile, FALSE, PROJECTILE_SEEKER, TRUE);
 +
 +      other = missile; MUTATOR_CALLHOOK(EditProjectile);
 +}
 +
 +// ============================
 +// Begin: FLAC, close range attack meant for defeating rockets which are coming at you. 
 +// ============================
 +void Seeker_Flac_Explode ()
 +{
 +      self.event_damage = func_null;
 +
 +      RadiusDamage (self, self.realowner, WEP_CVAR(seeker, flac_damage), WEP_CVAR(seeker, flac_edgedamage), WEP_CVAR(seeker, flac_radius), world, world, WEP_CVAR(seeker, flac_force), self.projectiledeathtype, other);
 +
 +      remove (self);
 +}
 +
 +void Seeker_Flac_Touch()
 +{
 +      PROJECTILE_TOUCH;
 +
 +      Seeker_Flac_Explode();
 +}
 +
 +void Seeker_Fire_Flac()
 +{
 +      entity missile;
 +      vector f_diff;
 +      float c;
 +
 +      W_DecreaseAmmo(ammo_rockets, WEP_CVAR(seeker, flac_ammo), autocvar_g_balance_seeker_reload_ammo);
 +
 +      c = mod(self.bulletcounter, 4);
 +      switch(c)
 +      {
 +              case 0:
 +                      f_diff = '-1.25 -3.75 0';
 +                      break;
 +              case 1:
 +                      f_diff = '+1.25 -3.75 0';
 +                      break;
 +              case 2:
 +                      f_diff = '-1.25 +3.75 0';
 +                      break;
 +              case 3:
 +              default:
 +                      f_diff = '+1.25 +3.75 0';
 +                      break;
 +      }
 +      W_SetupShot_ProjectileSize (self, '-2 -2 -2', '2 2 2', FALSE, 2, "weapons/flac_fire.wav", CH_WEAPON_A, WEP_CVAR(seeker, flac_damage));
 +      w_shotorg += f_diff;
 +
 +      pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
 +
 +      missile                                 = spawn ();
 +      missile.owner                   = missile.realowner = self;
 +      missile.classname               = "missile";
 +      missile.bot_dodge               = TRUE;
 +      missile.bot_dodgerating = WEP_CVAR(seeker, flac_damage);
 +      missile.touch                   = Seeker_Flac_Explode;
 +      missile.use                     = Seeker_Flac_Explode; 
 +      missile.think                   = adaptor_think2use_hittype_splash;
 +      missile.nextthink               = time + WEP_CVAR(seeker, flac_lifetime) + WEP_CVAR(seeker, flac_lifetime_rand);
 +      missile.solid                   = SOLID_BBOX;
 +      missile.movetype                = MOVETYPE_FLY; 
 +      missile.projectiledeathtype = WEP_SEEKER;
 +      missile.projectiledeathtype = WEP_SEEKER | HITTYPE_SECONDARY;
 +      missile.flags                           = FL_PROJECTILE;
 +      missile.missile_flags       = MIF_SPLASH; 
 +      
 +      // csqc projectiles
 +      //missile.angles                                = vectoangles (missile.velocity);       
 +      //missile.scale = 0.4; // BUG: the model is too big 
 +      
 +      setorigin (missile, w_shotorg);
 +      setsize (missile, '-2 -2 -2', '2 2 2');
 +              
 +      W_SETUPPROJECTILEVELOCITY_UP(missile, g_balance_seeker_flac);
 +      CSQCProjectile(missile, TRUE, PROJECTILE_FLAC, TRUE);
 +
 +      other = missile; MUTATOR_CALLHOOK(EditProjectile);
 +}
 +
 +// ============================
 +// Begin: Tag and rocket controllers 
 +// ============================
 +entity Seeker_Tagged_Info(entity isowner, entity istarget)
 +{
 +      entity tag;
 +      for(tag = world; (tag = find(tag, classname, "tag_tracker")); ) 
 +              if ((tag.realowner == isowner) && (tag.tag_target == istarget))
 +                      return tag;
 +              
 +      return world;
 +}
 +
 +void Seeker_Attack()
 +{
 +      entity tracker, closest_target;
 +      
 +      closest_target = world;
 +      for(tracker = world; (tracker = find(tracker, classname, "tag_tracker")); ) if (tracker.realowner == self)
 +      {
 +              if (closest_target)
 +              {
 +                      if (vlen(self.origin - tracker.tag_target.origin) < vlen(self.origin - closest_target.origin))
 +                              closest_target = tracker.tag_target;
 +              }
 +              else 
 +                      closest_target = tracker.tag_target;
 +      }
 +              
 +      traceline(self.origin + self.view_ofs, closest_target.origin, MOVE_NOMONSTERS, self);
 +      if ((!closest_target) || ((trace_fraction < 1) && (trace_ent != closest_target)))
 +              closest_target = world;
 +      
 +      Seeker_Fire_Missile('0 0 0', closest_target);
 +}
 +
 +void Seeker_Vollycontroller_Think() // TODO: Merge this with Seeker_Attack
 +{
 +      float c;
 +      entity oldself,oldenemy;
 +      self.cnt = self.cnt - 1;
 +
 +      if((!(self.realowner.items & IT_UNLIMITED_AMMO) && self.realowner.ammo_rockets < WEP_CVAR(seeker, missile_ammo)) || (self.cnt <= -1) || (self.realowner.deadflag != DEAD_NO) || (self.realowner.switchweapon != WEP_SEEKER))
 +      {
 +              remove(self);
 +              return;
 +      }
 +
 +      self.nextthink = time + WEP_CVAR(seeker, missile_delay) * W_WeaponRateFactor();
 +      
 +      oldself = self;
 +      self = self.realowner;
 +      
 +      oldenemy = self.enemy;
 +      self.enemy = oldself.enemy;
 +      
 +      c = mod(self.cnt, 4);
 +      switch(c)
 +      {
 +              case 0:
 +                      Seeker_Fire_Missile('-1.25 -3.75 0', self.enemy);
 +                      break;
 +              case 1:
 +                      Seeker_Fire_Missile('+1.25 -3.75 0', self.enemy);
 +                      break;
 +              case 2:
 +                      Seeker_Fire_Missile('-1.25 +3.75 0', self.enemy);
 +                      break;
 +              case 3:
 +              default:
 +                      Seeker_Fire_Missile('+1.25 +3.75 0', self.enemy);
 +                      break;
 +      }
 +
 +      self.enemy = oldenemy;
 +      self = oldself;
 +}
 +
 +void Seeker_Tracker_Think() 
 +{
 +      // commit suicide if: You die OR target dies OR you switch away from the seeker OR commit suicide if lifetime is up
 +      if ((self.realowner.deadflag != DEAD_NO) || (self.tag_target.deadflag != DEAD_NO) || (self.realowner.switchweapon != WEP_SEEKER)
 +      || (time > self.tag_time + WEP_CVAR(seeker, tag_tracker_lifetime)))
 +      {
 +              if (self)
 +              {
 +                      WaypointSprite_Kill(self.tag_target.wps_tag_tracker);
 +                      remove(self);
 +              }
 +              return;
 +      }
 +      
 +      // Update the think method information
 +      self.nextthink = time;
 +}
 +
 +// ============================
 +// Begin: Tag projectile 
 +// ============================
 +void Seeker_Tag_Explode ()
 +{
 +      //if(other==self.realowner)
 +      //    return;
 +      Damage_DamageInfo(self.origin, 0, 0, 0, self.velocity, WEP_SEEKER | HITTYPE_BOUNCE, other.species, self);
 +
 +      remove (self);
 +}
 +
 +void Seeker_Tag_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 +{
 +      if (self.health <= 0)
 +              return;
 +      self.health = self.health - damage;
 +      if (self.health <= 0)
 +              Seeker_Tag_Explode();
 +}
 +
 +void Seeker_Tag_Touch()
 +{
 +      vector dir;
 +      vector org2;
 +      entity e;
 +      
 +      PROJECTILE_TOUCH;
 +
 +      dir     = normalize (self.realowner.origin - self.origin);
 +      org2    = findbetterlocation (self.origin, 8);
 +
 +      te_knightspike(org2);
 +
 +      self.event_damage = func_null;
 +      Damage_DamageInfo(self.origin, 0, 0, 0, self.velocity, WEP_SEEKER | HITTYPE_BOUNCE | HITTYPE_SECONDARY, other.species, self);
 +
 +      if (other.takedamage == DAMAGE_AIM && other.deadflag == DEAD_NO)
 +      {
 +              // check to see if this person is already tagged by me
 +              entity tag = Seeker_Tagged_Info(self.realowner, other);
 +              
 +              if (tag != world)
 +              {
 +                      if (other.wps_tag_tracker && (WEP_CVAR(seeker, type) == 1)) // don't attach another waypointsprite without killing the old one first
 +                              WaypointSprite_Kill(other.wps_tag_tracker);
 +                              
 +                      tag.tag_time = time;
 +              }
 +              else
 +              {               
 +                      //sprint(self.realowner, strcat("You just tagged ^2", other.netname, "^7 with a tracking device!\n"));
 +                      e             = spawn();
 +                      e.cnt         = WEP_CVAR(seeker, missile_count);
 +                      e.classname   = "tag_tracker";
 +                      e.owner       = self.owner;
 +                      e.realowner   = self.realowner;
 +                      
 +                      if      (WEP_CVAR(seeker, type) == 1)
 +                      {
 +                              e.tag_target  = other;
 +                              e.tag_time    = time;
 +                              e.think       = Seeker_Tracker_Think;
 +                      }
 +                      else 
 +                      {
 +                              e.enemy     = other;
 +                              e.think     = Seeker_Vollycontroller_Think;
 +                      }
 +                      
 +                      e.nextthink   = time;
 +              }
 +              
 +              if      (WEP_CVAR(seeker, type) == 1)
 +              {
 +                      WaypointSprite_Spawn("tagged-target", WEP_CVAR(seeker, tag_tracker_lifetime), 0, other, '0 0 64', self.realowner, 0, other, wps_tag_tracker, TRUE, RADARICON_TAGGED, '0.5 1 0');
 +                      WaypointSprite_UpdateRule(other.wps_tag_tracker, 0, SPRITERULE_DEFAULT);
 +              }
 +      }
 +
 +      remove(self);
 +      return;
 +}
 +
 +void Seeker_Fire_Tag()
 +{
 +      entity missile;
 +      W_DecreaseAmmo(ammo_rockets, WEP_CVAR(seeker, tag_ammo), autocvar_g_balance_seeker_reload_ammo);
 +
 +      W_SetupShot_ProjectileSize (self, '-2 -2 -2', '2 2 2', FALSE, 2, "weapons/tag_fire.wav", CH_WEAPON_A, WEP_CVAR(seeker, missile_damage) * WEP_CVAR(seeker, missile_count));
 +
 +      missile                 = spawn();
 +      missile.owner           = missile.realowner = self;
 +      missile.classname       = "seeker_tag";
 +      missile.bot_dodge       = TRUE;
 +      missile.bot_dodgerating = 50;
 +      missile.touch           = Seeker_Tag_Touch;
 +      missile.think           = SUB_Remove;
 +      missile.nextthink       = time + WEP_CVAR(seeker, tag_lifetime);
 +      missile.movetype        = MOVETYPE_FLY;
 +      missile.solid           = SOLID_BBOX;
 +
 +      missile.takedamage       = DAMAGE_YES;
 +      missile.event_damage     = Seeker_Tag_Damage;
 +      missile.health           = WEP_CVAR(seeker, tag_health);
 +      missile.damageforcescale = WEP_CVAR(seeker, tag_damageforcescale);
 +
 +      setorigin (missile, w_shotorg);
 +      setsize (missile, '-2 -2 -2', '2 2 2');
 +
 +      missile.flags       = FL_PROJECTILE;
 +      //missile.missile_flags = MIF_..?; 
 +
 +      missile.movetype    = MOVETYPE_FLY;
 +      W_SETUPPROJECTILEVELOCITY(missile, g_balance_seeker_tag);
 +      missile.angles = vectoangles (missile.velocity);
 +
 +      CSQCProjectile(missile, TRUE, PROJECTILE_TAG, FALSE); // has sound
 +
 +      other = missile; MUTATOR_CALLHOOK(EditProjectile);
 +}
 +
 +// ============================
 +// Begin: Genereal weapon functions
 +// ============================
 +
 +float w_seeker(float req)
 +{
 +      float ammo_amount;
 +
 +      switch(req)
 +      {
 +              case WR_AIM:
 +              {
 +                      if (WEP_CVAR(seeker, type) == 1) 
 +                              if (Seeker_Tagged_Info(self, self.enemy) != world)
 +                                      self.BUTTON_ATCK = bot_aim(WEP_CVAR(seeker, missile_speed_max), 0, WEP_CVAR(seeker, missile_lifetime), FALSE);
 +                              else
 +                                      self.BUTTON_ATCK2 = bot_aim(WEP_CVAR(seeker, tag_speed), 0, WEP_CVAR(seeker, tag_lifetime), FALSE);
 +                      else
 +                              self.BUTTON_ATCK = bot_aim(WEP_CVAR(seeker, tag_speed), 0, WEP_CVAR(seeker, tag_lifetime), FALSE);
 +                              
 +                      return TRUE;
 +              }
 +              case WR_THINK:
 +              {
 +                      if(autocvar_g_balance_seeker_reload_ammo && self.clip_load < min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo))) // forced reload
 +                              WEP_ACTION(self.weapon, WR_RELOAD);
 +                              
 +                      else if (self.BUTTON_ATCK)
 +                      {
 +                              if (WEP_CVAR(seeker, type) == 1) 
 +                              {
 +                                      if (weapon_prepareattack(0, WEP_CVAR(seeker, missile_refire)))
 +                                      {
 +                                              Seeker_Attack();
 +                                              weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(seeker, missile_animtime), w_ready);
 +                                      }
 +                              }
 +                              else 
 +                              {
 +                                      if (weapon_prepareattack(0, WEP_CVAR(seeker, tag_refire)))
 +                                      {
 +                                              Seeker_Fire_Tag();
 +                                              weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready);
 +                                      }
 +                              }
 +                      }
 +
 +                      else if (self.BUTTON_ATCK2)
 +                      {
 +                              if (WEP_CVAR(seeker, type) == 1) 
 +                              {
 +                                      if (weapon_prepareattack(0, WEP_CVAR(seeker, tag_refire)))
 +                                      {
 +                                              Seeker_Fire_Tag();
 +                                              weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready);
 +                                      }
 +                              }
 +                              else 
 +                              {
 +                                      if (weapon_prepareattack(0, WEP_CVAR(seeker, flac_refire)))
 +                                      {
 +                                              Seeker_Fire_Flac();
 +                                              weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(seeker, flac_animtime), w_ready);
 +                                      }
 +                              }
 +                      }
 +                      
 +                      return TRUE;
 +              }
 +              case WR_INIT:
 +              {
 +                      precache_model ("models/weapons/g_seeker.md3");
 +                      precache_model ("models/weapons/v_seeker.md3");
 +                      precache_model ("models/weapons/h_seeker.iqm");
 +                      precache_sound ("weapons/tag_fire.wav");
 +                      precache_sound ("weapons/flac_fire.wav");
 +                      precache_sound ("weapons/seeker_fire.wav");
 +                      WEP_SET_PROPS(SEEKER_SETTINGS(seeker), WEP_SEEKER)
 +                      return TRUE;
 +              }
 +              case WR_SETUP:
 +              {
 +                      self.current_ammo = ammo_rockets;
 +                      return TRUE;
 +              }
 +              case WR_CHECKAMMO1:
 +              {
 +                      if (WEP_CVAR(seeker, type) == 1) 
 +                      {
 +                              ammo_amount = self.ammo_rockets >= WEP_CVAR(seeker, missile_ammo);
 +                              ammo_amount += self.(weapon_load[WEP_SEEKER]) >= WEP_CVAR(seeker, missile_ammo);
 +                      }
 +                      else
 +                      {
 +                              ammo_amount = self.ammo_rockets >= WEP_CVAR(seeker, tag_ammo);
 +                              ammo_amount += self.(weapon_load[WEP_SEEKER]) >= WEP_CVAR(seeker, tag_ammo);
 +                      }
 +                      
 +                      return ammo_amount;
 +              }
 +              case WR_CHECKAMMO2:
 +              {
 +                      if (WEP_CVAR(seeker, type) == 1) 
 +                      {
 +                              ammo_amount = self.ammo_rockets >= WEP_CVAR(seeker, tag_ammo);
 +                              ammo_amount += self.(weapon_load[WEP_SEEKER]) >= WEP_CVAR(seeker, tag_ammo);
 +                      }
 +                      else
 +                      {
 +                              ammo_amount = self.ammo_rockets >= WEP_CVAR(seeker, flac_ammo);
 +                              ammo_amount += self.(weapon_load[WEP_SEEKER]) >= WEP_CVAR(seeker, flac_ammo);
 +                      }
 +                      
 +                      return ammo_amount;
 +              }
 +              case WR_CONFIG:
 +              {
 +                      WEP_CONFIG_SETTINGS(SEEKER_SETTINGS(seeker))
 +                      return TRUE;
 +              }
 +              case WR_RELOAD:
 +              {
 +                      W_Reload(min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo)), "weapons/reload.wav");
 +                      return TRUE;
 +              }
 +              case WR_SUICIDEMESSAGE:
 +              {
 +                      return WEAPON_SEEKER_SUICIDE;
 +              }
 +              case WR_KILLMESSAGE:
 +              {
 +                      if(w_deathtype & HITTYPE_SECONDARY)
 +                              return WEAPON_SEEKER_MURDER_TAG;
 +                      else
 +                              return WEAPON_SEEKER_MURDER_SPRAY;
 +              }
 +      }
 +      return TRUE;
 +}
 +#endif
 +#ifdef CSQC
 +float w_seeker(float req)
 +{
 +      switch(req)
 +      {
 +              case WR_IMPACTEFFECT:
 +              {
 +                      vector org2;
 +                      org2 = w_org + w_backoff * 6;
 +                      if(w_deathtype & HITTYPE_BOUNCE)
 +                      {
 +                              if(w_deathtype & HITTYPE_SECONDARY)
 +                              {
 +                                      if(!w_issilent)
-                                                       sound(self, CH_SHOTS, "weapons/tagexp1.wav", 1, ATTN_NORM);
++                                              sound(self, CH_SHOTS, "weapons/tag_impact.wav", 1, ATTEN_NORM);
 +                              }
 +                              else
 +                              {
 +                                      pointparticles(particleeffectnum("hagar_explode"), org2, '0 0 0', 1);
 +                                      if(!w_issilent)
 +                                      {
 +                                              if (w_random<0.15)
-                                                       sound(self, CH_SHOTS, "weapons/tagexp2.wav", 1, ATTN_NORM);
++                                                      sound(self, CH_SHOTS, "weapons/tagexp1.wav", 1, ATTEN_NORM);
 +                                              else if (w_random<0.7)
-                                                       sound(self, CH_SHOTS, "weapons/tagexp3.wav", 1, ATTN_NORM);
++                                                      sound(self, CH_SHOTS, "weapons/tagexp2.wav", 1, ATTEN_NORM);
 +                                              else
-                                               sound(self, CH_SHOTS, "weapons/seekerexp1.wav", 1, ATTN_NORM);
++                                                      sound(self, CH_SHOTS, "weapons/tagexp3.wav", 1, ATTEN_NORM);
 +                                      }
 +                              }
 +                      }
 +                      else
 +                      {
 +                              pointparticles(particleeffectnum("hagar_explode"), org2, '0 0 0', 1);
 +                              if(!w_issilent)
 +                              {
 +                                      if (w_random<0.15)
-                                               sound(self, CH_SHOTS, "weapons/seekerexp2.wav", 1, ATTN_NORM);
++                                              sound(self, CH_SHOTS, "weapons/seekerexp1.wav", 1, ATTEN_NORM);
 +                                      else if (w_random<0.7)
-                                               sound(self, CH_SHOTS, "weapons/seekerexp3.wav", 1, ATTN_NORM);
++                                              sound(self, CH_SHOTS, "weapons/seekerexp2.wav", 1, ATTEN_NORM);
 +                                      else
++                                              sound(self, CH_SHOTS, "weapons/seekerexp3.wav", 1, ATTEN_NORM);
 +                              }
 +                      }
 +                      
 +                      return TRUE;
 +              }
 +              case WR_INIT:
 +              {
 +                      precache_sound("weapons/seekerexp1.wav");
 +                      precache_sound("weapons/seekerexp2.wav");
 +                      precache_sound("weapons/seekerexp3.wav");
 +                      precache_sound("weapons/tagexp1.wav");
 +                      precache_sound("weapons/tagexp2.wav");
 +                      precache_sound("weapons/tagexp3.wav");
 +                      precache_sound("weapons/tag_impact.wav");
 +                      return TRUE;
 +              }
 +      }
 +      
 +      return TRUE;
 +}
 +#endif
 +#endif
index 333a9ffafee544d50a437030ae9da6231accfb76,0000000000000000000000000000000000000000..a926a7a78600b68e2f7c8b470406f3172df78444
mode 100644,000000..100644
--- /dev/null
@@@ -1,320 -1,0 +1,320 @@@
-       sound (self, CH_WEAPON_A, "weapons/shotgun_melee.wav", VOL_BASE, ATTN_NORM);
 +#ifdef REGISTER_WEAPON
 +REGISTER_WEAPON(
 +/* WEP_##id */ SHOTGUN,
 +/* function */ w_shotgun,
 +/* ammotype */ IT_SHELLS,
 +/* impulse  */ 2,
 +/* flags    */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN,
 +/* rating   */ BOT_PICKUP_RATING_LOW,
 +/* model    */ "shotgun",
 +/* netname  */ "shotgun",
 +/* fullname */ _("Shotgun")
 +);
 +#else
 +#ifdef SVQC
 +void spawnfunc_weapon_shotgun()
 +{
 +      if(autocvar_sv_q3acompat_machineshotgunswap)
 +      if(self.classname != "droppedweapon")
 +      {
 +              weapon_defaultspawnfunc(WEP_UZI);
 +              return;
 +      }
 +      weapon_defaultspawnfunc(WEP_SHOTGUN);
 +}
 +
 +
 +void W_Shotgun_Attack (void)
 +{
 +      float   sc;
 +      float   ammoamount;
 +      float   bullets;
 +      float   d;
 +      float   f;
 +      float   spread;
 +      float   bulletspeed;
 +      float   bulletconstant;
 +      entity flash;
 +
 +      ammoamount = autocvar_g_balance_shotgun_primary_ammo;
 +      bullets = autocvar_g_balance_shotgun_primary_bullets;
 +      d = autocvar_g_balance_shotgun_primary_damage;
 +      f = autocvar_g_balance_shotgun_primary_force;
 +      spread = autocvar_g_balance_shotgun_primary_spread;
 +      bulletspeed = autocvar_g_balance_shotgun_primary_speed;
 +      bulletconstant = autocvar_g_balance_shotgun_primary_bulletconstant;
 +
 +      W_DecreaseAmmo(ammo_shells, ammoamount, autocvar_g_balance_shotgun_reload_ammo);
 +
 +      W_SetupShot (self, autocvar_g_antilag_bullets && bulletspeed >= autocvar_g_antilag_bullets, 5, "weapons/shotgun_fire.wav", CH_WEAPON_A, d * bullets);
 +      for (sc = 0;sc < bullets;sc = sc + 1)
 +              fireBallisticBullet(w_shotorg, w_shotdir, spread, bulletspeed, 5, d, f, WEP_SHOTGUN, 0, 1, bulletconstant);
 +      endFireBallisticBullet();
 +
 +      pointparticles(particleeffectnum("shotgun_muzzleflash"), w_shotorg, w_shotdir * 1000, autocvar_g_balance_shotgun_primary_ammo);
 +
 +      // casing code
 +      if (autocvar_g_casings >= 1)
 +              for (sc = 0;sc < ammoamount;sc = sc + 1)
 +                      SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 30) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 1, self);
 +
 +      // muzzle flash for 1st person view
 +      flash = spawn();
 +      setmodel(flash, "models/uziflash.md3"); // precision set below
 +      flash.think = SUB_Remove;
 +      flash.nextthink = time + 0.06;
 +      flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
 +      W_AttachToShotorg(flash, '5 0 0');
 +}
 +
 +.float swing_prev;
 +.entity swing_alreadyhit;
 +void shotgun_meleethink (void)
 +{
 +      // declarations
 +      float i, f, swing, swing_factor, swing_damage, meleetime, is_player;
 +      entity target_victim;
 +      vector targpos;
 +
 +      if(!self.cnt) // set start time of melee
 +      {
 +              self.cnt = time; 
 +              W_PlayStrengthSound(self.realowner);
 +      }
 +
 +      makevectors(self.realowner.v_angle); // update values for v_* vectors
 +      
 +      // calculate swing percentage based on time
 +      meleetime = autocvar_g_balance_shotgun_secondary_melee_time * W_WeaponRateFactor();
 +      swing = bound(0, (self.cnt + meleetime - time) / meleetime, 10);
 +      f = ((1 - swing) * autocvar_g_balance_shotgun_secondary_melee_traces);
 +      
 +      // check to see if we can still continue, otherwise give up now
 +      if((self.realowner.deadflag != DEAD_NO) && autocvar_g_balance_shotgun_secondary_melee_no_doubleslap)
 +      {
 +              remove(self);
 +              return;
 +      }
 +      
 +      // if okay, perform the traces needed for this frame 
 +      for(i=self.swing_prev; i < f; ++i)
 +      {
 +              swing_factor = ((1 - (i / autocvar_g_balance_shotgun_secondary_melee_traces)) * 2 - 1);
 +              
 +              targpos = (self.realowner.origin + self.realowner.view_ofs 
 +                      + (v_forward * autocvar_g_balance_shotgun_secondary_melee_range)
 +                      + (v_up * swing_factor * autocvar_g_balance_shotgun_secondary_melee_swing_up)
 +                      + (v_right * swing_factor * autocvar_g_balance_shotgun_secondary_melee_swing_side));
 +
 +              WarpZone_traceline_antilag(self, self.realowner.origin + self.realowner.view_ofs, targpos, FALSE, self, ANTILAG_LATENCY(self.realowner));
 +              
 +              // draw lightning beams for debugging
 +              //te_lightning2(world, targpos, self.realowner.origin + self.realowner.view_ofs + v_forward * 5 - v_up * 5); 
 +              //te_customflash(targpos, 40,  2, '1 1 1');
 +              
 +              is_player = (IS_PLAYER(trace_ent) || trace_ent.classname == "body");
 +
 +              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_shotgun_secondary_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_shotgun_secondary_damage * min(1, swing_factor + 1));
 +                      else
 +                              swing_damage = (autocvar_g_balance_shotgun_secondary_melee_nonplayerdamage * min(1, swing_factor + 1));
 +                      
 +                      //print(strcat(self.realowner.netname, " hitting ", target_victim.netname, " with ", strcat(ftos(swing_damage), " damage (factor: ", ftos(swing_factor), ") at "), ftos(time), " seconds.\n"));
 +                      
 +                      Damage(target_victim, self.realowner, self.realowner, 
 +                              swing_damage, WEP_SHOTGUN | HITTYPE_SECONDARY, 
 +                              self.realowner.origin + self.realowner.view_ofs, 
 +                              v_forward * autocvar_g_balance_shotgun_secondary_force);
 +                              
 +                      if(accuracy_isgooddamage(self.realowner, target_victim)) { accuracy_add(self.realowner, WEP_SHOTGUN, 0, swing_damage); }
 +                              
 +                      // draw large red flash for debugging
 +                      //te_customflash(targpos, 200, 2, '15 0 0');
 +                      
 +                      if(autocvar_g_balance_shotgun_secondary_melee_multihit) // allow multiple hits with one swing, but not against the same player twice.
 +                      {
 +                              self.swing_alreadyhit = target_victim;
 +                              continue; // move along to next trace
 +                      }
 +                      else
 +                      {
 +                              remove(self);
 +                              return;
 +                      }
 +              }
 +      }
 +      
 +      if(time >= self.cnt + meleetime)
 +      {
 +              // melee is finished
 +              remove(self);
 +              return;
 +      }
 +      else
 +      {
 +              // set up next frame 
 +              self.swing_prev = i;
 +              self.nextthink = time;
 +      }
 +}
 +
 +void W_Shotgun_Attack2 (void)
 +{
-                                       sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
++      sound (self, CH_WEAPON_A, "weapons/shotgun_melee.wav", VOL_BASE, ATTEN_NORM);
 +      weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_shotgun_secondary_animtime, w_ready);
 +
 +      entity meleetemp;
 +      meleetemp = spawn();
 +      meleetemp.realowner = self;
 +      meleetemp.think = shotgun_meleethink;
 +      meleetemp.nextthink = time + autocvar_g_balance_shotgun_secondary_melee_delay * W_WeaponRateFactor();
 +      W_SetupShot_Range(self, TRUE, 0, "", 0, autocvar_g_balance_shotgun_secondary_damage, autocvar_g_balance_shotgun_secondary_melee_range);
 +}
 +
 +void spawnfunc_weapon_shotgun(); // defined in t_items.qc
 +
 +.float shotgun_primarytime;
 +
 +float w_shotgun(float req)
 +{
 +      float ammo_amount;
 +      
 +      switch(req)
 +      {
 +              case WR_AIM:
 +              {
 +                      if(vlen(self.origin-self.enemy.origin) <= autocvar_g_balance_shotgun_secondary_melee_range)
 +                              self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE);
 +                      else
 +                      {
 +                              if(autocvar_g_antilag_bullets)
 +                                      self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE);
 +                              else
 +                                      self.BUTTON_ATCK = bot_aim(autocvar_g_balance_shotgun_primary_speed, 0, 0.001, FALSE);
 +                      }
 +                      
 +                      return TRUE;
 +              }
 +              case WR_THINK:
 +              {
 +                      if(autocvar_g_balance_shotgun_reload_ammo && self.clip_load < autocvar_g_balance_shotgun_primary_ammo) // forced reload
 +                      {
 +                              // don't force reload an empty shotgun if its melee attack is active
 +                              if not(autocvar_g_balance_shotgun_secondary && self.ammo_shells < autocvar_g_balance_shotgun_primary_ammo)
 +                                      WEP_ACTION(self.weapon, WR_RELOAD);
 +                      }
 +                      else
 +                      {
 +                              if (self.BUTTON_ATCK)
 +                              {
 +                                      if (time >= self.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary
 +                                      {
 +                                              if(weapon_prepareattack(0, autocvar_g_balance_shotgun_primary_animtime))
 +                                              {
 +                                                      W_Shotgun_Attack();
 +                                                      self.shotgun_primarytime = time + autocvar_g_balance_shotgun_primary_refire * W_WeaponRateFactor();
 +                                                      weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_shotgun_primary_animtime, w_ready);
 +                                              }
 +                                      }
 +                              }
 +                      }
 +                      if (self.clip_load >= 0) // we are not currently reloading
 +                      if (!self.crouch) // no crouchmelee please
 +                      if (self.BUTTON_ATCK2 && autocvar_g_balance_shotgun_secondary)
 +                      if (weapon_prepareattack(1, autocvar_g_balance_shotgun_secondary_refire))
 +                      {
 +                              // attempt forcing playback of the anim by switching to another anim (that we never play) here...
 +                              weapon_thinkf(WFRAME_FIRE1, 0, W_Shotgun_Attack2);
 +                      }
 +                      
 +                      return TRUE;
 +              }
 +              case WR_INIT:
 +              {
 +                      precache_model ("models/uziflash.md3");
 +                      precache_model ("models/weapons/g_shotgun.md3");
 +                      precache_model ("models/weapons/v_shotgun.md3");
 +                      precache_model ("models/weapons/h_shotgun.iqm");
 +                      precache_sound ("misc/itempickup.wav");
 +                      precache_sound ("weapons/shotgun_fire.wav");
 +                      precache_sound ("weapons/shotgun_melee.wav");
 +                      return TRUE;
 +              }
 +              case WR_SETUP:
 +              {
 +                      self.current_ammo = ammo_shells;
 +                      return TRUE;
 +              }
 +              case WR_CHECKAMMO1:
 +              {
 +                      ammo_amount = self.ammo_shells >= autocvar_g_balance_shotgun_primary_ammo;
 +                      ammo_amount += self.(weapon_load[WEP_SHOTGUN]) >= autocvar_g_balance_shotgun_primary_ammo;
 +                      return ammo_amount;
 +              }
 +              case WR_CHECKAMMO2:
 +              {
 +                      // melee attack is always available
 +                      return TRUE;
 +              }
 +              case WR_RELOAD:
 +              {
 +                      W_Reload(autocvar_g_balance_shotgun_primary_ammo, "weapons/reload.wav");
 +                      return TRUE;
 +              }
 +              case WR_SUICIDEMESSAGE:
 +              {
 +                      return WEAPON_THINKING_WITH_PORTALS;
 +              }
 +              case WR_KILLMESSAGE:
 +              {
 +                      if(w_deathtype & HITTYPE_SECONDARY)
 +                              return WEAPON_SHOTGUN_MURDER_SLAP;
 +                      else
 +                              return WEAPON_SHOTGUN_MURDER;
 +              }
 +      }
 +      return TRUE;
 +}
 +#endif
 +#ifdef CSQC
 +.float prevric;
 +float w_shotgun(float req)
 +{
 +      switch(req)
 +      {
 +              case WR_IMPACTEFFECT:
 +              {
 +                      vector org2;
 +                      org2 = w_org + w_backoff * 2;
 +                      pointparticles(particleeffectnum("shotgun_impact"), org2, w_backoff * 1000, 1);
 +                      if(!w_issilent && time - self.prevric > 0.25)
 +                      {
 +                              if(w_random < 0.0165)
-                                       sound(self, CH_SHOTS, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
++                                      sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTEN_NORM);
 +                              else if(w_random < 0.033)
-                                       sound(self, CH_SHOTS, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
++                                      sound(self, CH_SHOTS, "weapons/ric2.wav", VOL_BASE, ATTEN_NORM);
 +                              else if(w_random < 0.05)
++                                      sound(self, CH_SHOTS, "weapons/ric3.wav", VOL_BASE, ATTEN_NORM);
 +                              self.prevric = time;
 +                      }
 +                      return TRUE;
 +              }
 +              case WR_INIT:
 +              {
 +                      precache_sound("weapons/ric1.wav");
 +                      precache_sound("weapons/ric2.wav");
 +                      precache_sound("weapons/ric3.wav");
 +                      return TRUE;
 +              }
 +      }
 +      return TRUE;
 +}
 +#endif
 +#endif
index 72c081be9b796e161234b920dc53a36f4ce89703,0000000000000000000000000000000000000000..2f9dd2c92eb3714beb25cfcb8df950db0ab5cad4
mode 100644,000000..100644
--- /dev/null
@@@ -1,193 -1,0 +1,260 @@@
- void register_weapon(float id, float(float) func, float ammotype, float i, float weapontype, float pickupbasevalue, string modelname, string shortname, string wname)
 +#ifndef MENUQC
 +#include "calculations.qc"
 +#endif
 +#include "all.qh"
 +
 +// WEAPON PLUGIN SYSTEM
 +entity weapon_info[WEP_MAXCOUNT];
 +entity dummy_weapon_info;
 +
-       WEPSET_COPY_EW(e, id);
++#if WEP_MAXCOUNT > 72
++# error Kein Weltraum links auf dem Gerät
++#endif
++
++WepSet WepSet_FromWeapon(float a) {
++      a -= WEP_FIRST;
++#if WEP_MAXCOUNT > 24
++      if(a >= 24) {
++              a -= 24;
++#if WEP_MAXCOUNT > 48
++              if(a >= 24) {
++                      a -= 24;
++                      return '0 0 1' * power2of(a);
++              }
++#endif
++              return '0 1 0' * power2of(a);
++      }
++#endif
++      return '1 0 0' * power2of(a);
++}
++#ifdef SVQC
++void WepSet_AddStat()
++{
++      addstat(STAT_WEAPONS, AS_INT, weapons_x);
++#if WEP_MAXCOUNT > 24
++      addstat(STAT_WEAPONS2, AS_INT, weapons_y);
++#if WEP_MAXCOUNT > 48
++      addstat(STAT_WEAPONS3, AS_INT, weapons_z);
++#endif
++#endif
++}
++void WriteWepSet(float dst, WepSet w)
++{
++#if WEP_MAXCOUNT > 48
++      WriteInt72_t(dst, w);
++#elif WEP_MAXCOUNT > 24
++      WriteInt48_t(dst, w);
++#else
++      WriteInt24_t(dst, w_x);
++#endif
++}
++#endif
++#ifdef CSQC
++WepSet WepSet_GetFromStat()
++{
++      WepSet w = '0 0 0';
++      w_x = getstati(STAT_WEAPONS);
++#if WEP_MAXCOUNT > 24
++      w_y = getstati(STAT_WEAPONS2);
++#if WEP_MAXCOUNT > 48
++      w_z = getstati(STAT_WEAPONS3);
++#endif
++#endif
++      return w;
++}
++WepSet ReadWepSet()
++{
++#if WEP_MAXCOUNT > 48
++      return ReadInt72_t();
++#elif WEP_MAXCOUNT > 24
++      return ReadInt48_t();
++#else
++      return ReadInt24_t() * '1 0 0';
++#endif
++}
++#endif
++
++void register_weapon(float id, WepSet bit, float(float) func, float ammotype, float i, float weapontype, float pickupbasevalue, string modelname, string shortname, string wname)
 +{
 +      entity e;
 +      weapon_info[id - 1] = e = spawn();
 +      e.classname = "weapon_info";
 +      e.weapon = id;
-       WEPSET_CLEAR_E(dummy_weapon_info);
++      e.weapons = bit;
 +      e.netname = shortname;
 +      e.message = wname;
 +      e.items = ammotype;
 +      e.weapon_func = func;
 +      e.mdl = modelname;
 +      e.model = strzone(strcat("models/weapons/g_", modelname, ".md3"));
 +      e.spawnflags = weapontype;
 +      e.model2 = strzone(strcat("wpn-", e.mdl));
 +      e.impulse = i;
 +      e.bot_pickupbasevalue = pickupbasevalue;
 +      if(ammotype & IT_SHELLS)
 +              e.ammo_field = ammo_shells;
 +      else if(ammotype & IT_NAILS)
 +              e.ammo_field = ammo_nails;
 +      else if(ammotype & IT_ROCKETS)
 +              e.ammo_field = ammo_rockets;
 +      else if(ammotype & IT_CELLS)
 +              e.ammo_field = ammo_cells;
 +      else if(ammotype & IT_FUEL)
 +              e.ammo_field = ammo_fuel;
 +      else
 +              e.ammo_field = ammo_batteries;
 +
 +      #ifndef MENUQC
 +      func(WR_INIT);
 +      #endif
 +}
 +float w_null(float dummy)
 +{
 +      return 0;
 +}
 +void register_weapons_done()
 +{
 +      dummy_weapon_info = spawn();
 +      dummy_weapon_info.classname = "weapon_info";
 +      dummy_weapon_info.weapon = 0; // you can recognize dummies by this
-       WEPSET_DECLARE_A(remaining);
-       WEPSET_DECLARE_A(result);
-       WEPSET_COPY_AE(remaining, e);
-       WEPSET_CLEAR_A(result);
++      dummy_weapon_info.weapons = '0 0 0';
 +      dummy_weapon_info.netname = "";
 +      dummy_weapon_info.message = "AOL CD Thrower";
 +      dummy_weapon_info.items = 0;
 +      dummy_weapon_info.weapon_func = w_null;
 +      dummy_weapon_info.mdl = "";
 +      dummy_weapon_info.model = "";
 +      dummy_weapon_info.spawnflags = 0;
 +      dummy_weapon_info.model2 = "";
 +      dummy_weapon_info.impulse = -1;
 +      dummy_weapon_info.bot_pickupbasevalue = 0;
 +
 +      float i;
 +      weaponorder_byid = "";
 +      for(i = WEP_MAXCOUNT; i >= 1; --i)
 +              if(weapon_info[i-1])
 +                      weaponorder_byid = strcat(weaponorder_byid, " ", ftos(i));
 +      weaponorder_byid = strzone(substring(weaponorder_byid, 1, strlen(weaponorder_byid) - 1));
 +}
 +entity get_weaponinfo(float id)
 +{
 +      entity w;
 +      if(id < WEP_FIRST || id > WEP_LAST)
 +              return dummy_weapon_info;
 +      w = weapon_info[id - 1];
 +      if(w)
 +              return w;
 +      return dummy_weapon_info;
 +}
 +string W_FixWeaponOrder(string order, float complete)
 +{
 +      return fixPriorityList(order, WEP_FIRST, WEP_LAST, 230 - WEP_FIRST, complete);
 +}
 +string W_NameWeaponOrder_MapFunc(string s)
 +{
 +      entity wi;
 +      if(s == "0" || stof(s))
 +      {
 +              wi = get_weaponinfo(stof(s));
 +              if(wi != dummy_weapon_info)
 +                      return wi.netname;
 +      }
 +      return s;
 +}
 +string W_NameWeaponOrder(string order)
 +{
 +      return mapPriorityList(order, W_NameWeaponOrder_MapFunc);
 +}
 +string W_NumberWeaponOrder_MapFunc(string s)
 +{
 +      float i;
 +      if(s == "0" || stof(s))
 +              return s;
 +      for(i = WEP_FIRST; i <= WEP_LAST; ++i)
 +              if(s == get_weaponinfo(i).netname)
 +                      return ftos(i);
 +      return s;
 +}
 +string W_NumberWeaponOrder(string order)
 +{
 +      return mapPriorityList(order, W_NumberWeaponOrder_MapFunc);
 +}
 +
 +float W_FixWeaponOrder_BuildImpulseList_buf[WEP_MAXCOUNT];
 +string W_FixWeaponOrder_BuildImpulseList_order;
 +void W_FixWeaponOrder_BuildImpulseList_swap(float i, float j, entity pass)
 +{
 +      float h;
 +      h = W_FixWeaponOrder_BuildImpulseList_buf[i];
 +      W_FixWeaponOrder_BuildImpulseList_buf[i] = W_FixWeaponOrder_BuildImpulseList_buf[j];
 +      W_FixWeaponOrder_BuildImpulseList_buf[j] = h;
 +}
 +float W_FixWeaponOrder_BuildImpulseList_cmp(float i, float j, entity pass)
 +{
 +      entity e1, e2;
 +      float d;
 +      e1 = get_weaponinfo(W_FixWeaponOrder_BuildImpulseList_buf[i]);
 +      e2 = get_weaponinfo(W_FixWeaponOrder_BuildImpulseList_buf[j]);
 +      d = mod(e1.impulse + 9, 10) - mod(e2.impulse + 9, 10);
 +      if(d != 0)
 +              return -d; // high impulse first!
 +      return
 +              strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "), sprintf(" %d ", W_FixWeaponOrder_BuildImpulseList_buf[i]), 0)
 +              -
 +              strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "), sprintf(" %d ", W_FixWeaponOrder_BuildImpulseList_buf[j]), 0)
 +              ; // low char index first!
 +}
 +string W_FixWeaponOrder_BuildImpulseList(string o)
 +{
 +      float i;
 +      W_FixWeaponOrder_BuildImpulseList_order = o;
 +      for(i = WEP_FIRST; i <= WEP_LAST; ++i)
 +              W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST] = i;
 +      heapsort(WEP_LAST - WEP_FIRST + 1, W_FixWeaponOrder_BuildImpulseList_swap, W_FixWeaponOrder_BuildImpulseList_cmp, world);
 +      o = "";
 +      for(i = WEP_FIRST; i <= WEP_LAST; ++i)
 +              o = strcat(o, " ", ftos(W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST]));
 +      W_FixWeaponOrder_BuildImpulseList_order = string_null;
 +      return substring(o, 1, -1);
 +}
 +
 +string W_FixWeaponOrder_AllowIncomplete(string order)
 +{
 +      return W_FixWeaponOrder(order, 0);
 +}
 +
 +string W_FixWeaponOrder_ForceComplete(string order)
 +{
 +      if(order == "")
 +              order = W_NumberWeaponOrder(cvar_defstring("cl_weaponpriority"));
 +      return W_FixWeaponOrder(order, 1);
 +}
 +
 +void W_RandomWeapons(entity e, float n)
 +{
 +      float i, j;
-                       if(WEPSET_CONTAINS_AW(remaining, j))
++      WepSet remaining;
++      WepSet result;
++      remaining = e.weapons;
++      result = '0 0 0';
 +      for(i = 0; i < n; ++i)
 +      {
 +              RandomSelection_Init();
 +              for(j = WEP_FIRST; j <= WEP_LAST; ++j)
-               WEPSET_OR_AW(result, RandomSelection_chosen_float);
-               WEPSET_ANDNOT_AW(remaining, RandomSelection_chosen_float);
++                      if(remaining & WepSet_FromWeapon(j))
 +                              RandomSelection_Add(world, j, string_null, 1, 1);
-       WEPSET_COPY_EA(e, result);
++              result |= WepSet_FromWeapon(RandomSelection_chosen_float);
++              remaining &= ~WepSet_FromWeapon(RandomSelection_chosen_float);
 +      }
++      e.weapons = result;
 +}
 +
 +string W_Name(float weaponid)
 +{
 +      return (get_weaponinfo(weaponid)).message;
 +}
 +
 +float W_AmmoItemCode(float wpn)
 +{
 +      return (get_weaponinfo(wpn)).items & IT_AMMO;
 +}
index a4ced87a9b990055ba8a30a55ef7b3916187f720,0000000000000000000000000000000000000000..362a3d84a113c50fba09e147eab50ccafcbb38f4
mode 100644,000000..100644
--- /dev/null
@@@ -1,337 -1,0 +1,238 @@@
- float BOT_PICKUP_RATING_LOW   = 2500;
- float BOT_PICKUP_RATING_MID   = 5000;
- float BOT_PICKUP_RATING_HIGH  = 10000;
- float WEP_TYPE_OTHER        =  0x00; // not for damaging people
- float WEP_TYPE_SPLASH       =  0x01; // splash damage
- float WEP_TYPE_HITSCAN              =  0x02; // hitscan
- float WEP_TYPEMASK            =  0x0F;
- float WEP_FLAG_CANCLIMB       =  0x10; // can be used for movement
- float WEP_FLAG_NORMAL         =  0x20; // in "most weapons" set
- float WEP_FLAG_HIDDEN         =  0x40; // hides from menu
- float WEP_FLAG_RELOADABLE     =  0x80; // can has reload
- float WEP_FLAG_SUPERWEAPON    = 0x100; // powerup timer
- float WEP_FLAG_MUTATORBLOCKED = 0x200; // hides from impulse 99 etc. (mutators are allowed to clear this flag)
- float MAX_SHOT_DISTANCE = 32768;
 +#ifndef MENUQC
 +#include "calculations.qh"
 +#endif
 +
- float IT_UNLIMITED_WEAPON_AMMO     = 1;
++const float BOT_PICKUP_RATING_LOW     = 2500;
++const float BOT_PICKUP_RATING_MID     = 5000;
++const float BOT_PICKUP_RATING_HIGH    = 10000;
++
++const float WEP_TYPE_OTHER          =  0x00; // not for damaging people
++const float WEP_TYPE_SPLASH         =  0x01; // splash damage
++const float WEP_TYPE_HITSCAN        =  0x02; // hitscan
++const float WEP_TYPEMASK            =  0x0F;
++const float WEP_FLAG_CANCLIMB       =  0x10; // can be used for movement
++const float WEP_FLAG_NORMAL         =  0x20; // in "most weapons" set
++const float WEP_FLAG_HIDDEN         =  0x40; // hides from menu
++const float WEP_FLAG_RELOADABLE     =  0x80; // can has reload
++const float WEP_FLAG_SUPERWEAPON    = 0x100; // powerup timer
++const float WEP_FLAG_MUTATORBLOCKED = 0x200; // hides from impulse 99 etc. (mutators are allowed to clear this flag)
++
++const float MAX_SHOT_DISTANCE = 32768;
 +
 +// weapon requests // WEAPONTODO
 +#define WR_SETUP          1 // (SERVER) setup weapon data
 +#define WR_THINK          2 // (SERVER) logic to run every frame
 +#define WR_CHECKAMMO1     3 // (SERVER) checks ammo for weapon
 +#define WR_CHECKAMMO2     4 // (SERVER) checks ammo for weapon
 +#define WR_AIM            5 // (SERVER) runs bot aiming code for this weapon
 +#define WR_INIT           6 // (BOTH) precaches models/sounds used by this weapon
 +#define WR_SUICIDEMESSAGE 7 // (SERVER) notification number for suicide message (may inspect w_deathtype for details)
 +#define WR_KILLMESSAGE    8 // (SERVER) notification number for kill message (may inspect w_deathtype for details)
 +#define WR_RELOAD         9 // (SERVER) does not need to do anything
 +#define WR_RESETPLAYER    10 // (SERVER) does not need to do anything
 +#define WR_IMPACTEFFECT   11 // (CLIENT) impact effect
 +#define WR_SWITCHABLE     12 // (CLIENT) impact effect
 +#define WR_PLAYERDEATH    13 // (SERVER) does not need to do anything
 +#define WR_GONETHINK      14 // (SERVER) logic to run every frame, also if no longer having the weapon as long as the switch away has not been performed
 +#define WR_CONFIG         15 // (ALL) 
 +
 +// WEAPONTODO
- float IT_UNLIMITED_SUPERWEAPONS    = 2;
++const float   IT_UNLIMITED_WEAPON_AMMO     = 1;
 +// when this bit is set, using a weapon does not reduce ammo. Checkpoints can give this powerup.
- float   IT_CTF_SHIELDED              = 4; // set for the flag shield
- float   IT_USING_JETPACK             = 8; // confirmation that button is pressed
- float   IT_JETPACK                   = 16; // actual item
- float   IT_FUEL_REGEN                = 32; // fuel regeneration trigger
- float   IT_SHELLS                    = 256;
- float   IT_NAILS                     = 512;
- float   IT_ROCKETS                   = 1024;
- float   IT_CELLS                     = 2048;
- float   IT_SUPERWEAPON               = 4096;
- float   IT_FUEL                      = 128;
- float   IT_STRENGTH                  = 8192;
- float   IT_INVINCIBLE                = 16384;
- float   IT_HEALTH                    = 32768;
++const float   IT_UNLIMITED_SUPERWEAPONS    = 2;
 +// when this bit is set, superweapons don't expire. Checkpoints can give this powerup.
-       float   IT_KEY1                                 = 131072;
-       float   IT_KEY2                                 = 262144;
++const float   IT_CTF_SHIELDED              = 4; // set for the flag shield
++const float   IT_USING_JETPACK             = 8; // confirmation that button is pressed
++const float   IT_JETPACK                   = 16; // actual item
++const float   IT_FUEL_REGEN                = 32; // fuel regeneration trigger
++WANT_CONST float   IT_SHELLS                    = 256;
++WANT_CONST float   IT_NAILS                     = 512;
++WANT_CONST float   IT_ROCKETS                   = 1024;
++WANT_CONST float   IT_CELLS                     = 2048;
++const float   IT_SUPERWEAPON               = 4096;
++const float   IT_FUEL                      = 128;
++const float   IT_STRENGTH                  = 8192;
++const float   IT_INVINCIBLE                = 16384;
++const float   IT_HEALTH                    = 32768;
 +// union:
 +      // for items:
-       float   IT_RED_FLAG_TAKEN               = 32768;
-       float   IT_RED_FLAG_LOST                = 65536;
-       float   IT_RED_FLAG_CARRYING            = 98304;
-       float   IT_BLUE_FLAG_TAKEN              = 131072;
-       float   IT_BLUE_FLAG_LOST               = 262144;
-       float   IT_BLUE_FLAG_CARRYING   = 393216;
++      WANT_CONST float        IT_KEY1                                 = 131072;
++      WANT_CONST float        IT_KEY2                                 = 262144;
 +      // for players:
- float   IT_5HP                       = 524288;
- float   IT_25HP                      = 1048576;
- float   IT_ARMOR_SHARD               = 2097152;
- float   IT_ARMOR                     = 4194304;
++      const float     IT_RED_FLAG_TAKEN               = 32768;
++      const float     IT_RED_FLAG_LOST                = 65536;
++      const float     IT_RED_FLAG_CARRYING            = 98304;
++      const float     IT_BLUE_FLAG_TAKEN              = 131072;
++      const float     IT_BLUE_FLAG_LOST               = 262144;
++      const float     IT_BLUE_FLAG_CARRYING   = 393216;
 +// end
- float   IT_AMMO                      = 3968; // IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS | IT_FUEL;
- float   IT_PICKUPMASK                = 51; // IT_FUEL_REGEN | IT_JETPACK | IT_UNLIMITED_AMMO; // strength and invincible are handled separately
- float   IT_UNLIMITED_AMMO            = 3; // IT_UNLIMITED_SUPERWEAPONS | IT_UNLIMITED_WEAPON_AMMO;
++const float   IT_5HP                       = 524288;
++const float   IT_25HP                      = 1048576;
++const float   IT_ARMOR_SHARD               = 2097152;
++const float   IT_ARMOR                     = 4194304;
 +
- float AMMO_COUNT = 4; // amount of ammo types to show in the inventory panel
++const float   IT_AMMO                      = 3968; // IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS | IT_FUEL;
++const float   IT_PICKUPMASK                = 51; // IT_FUEL_REGEN | IT_JETPACK | IT_UNLIMITED_AMMO; // strength and invincible are handled separately
++const float   IT_UNLIMITED_AMMO            = 3; // IT_UNLIMITED_SUPERWEAPONS | IT_UNLIMITED_WEAPON_AMMO;
 +
- // also, weaponinfo ents can act as a WEPSET
- // ===================
- //  Weapon Operations
- // ===================
- #if 1
- # define WEP_MAXCOUNT 24
- // default storage
- .float _WS_weapons;
- # define WEPSET_BIT(a)                  power2of((a) - WEP_FIRST)
- # define WEPSET_DECLARE_A(a)            float _WS_##a
- # define WEPSET_CLEAR_E(e)              ((e)._WS_weapons = 0)
- # define WEPSET_CLEAR_A(a)              (_WS_##a = 0)
- # define WEPSET_EMPTY_E(e)              ((e)._WS_weapons == 0)
- # define WEPSET_EMPTY_A(a)              (_WS_##a == 0)
- # define WEPSET_COPY_AS(a)              (_WS_##a = getstati(STAT_WEAPONS))
- # define WEPSET_ADDSTAT()               addstat(STAT_WEAPONS, AS_INT, _WS_weapons)
- # define WEPSET_WRITE_E(dest,a)         WriteInt24_t(dest, (a)._WS_weapons)
- # define WEPSET_WRITE_A(dest,a)         WriteInt24_t(dest, _WS_##a)
- # define WEPSET_WRITE_W(dest,a)         WriteInt24_t(dest, WEPSET_BIT(a))
- # define WEPSET_READ_E(a)               (a)._WS_weapons = ReadInt24_t()
- # define WEPSET_READ_A(a)               (_WS_##a) = ReadInt24_t()
- # define WEPSET_OP1_EE(a,b,mergeop,x)   ((a)._WS_weapons x (b)._WS_weapons)
- # define WEPSET_OP2_EE(a,b,mergeop,x,y) ((a)._WS_weapons x (b)._WS_weapons y (a)._WS_weapons)
- # define WEPSET_OP1_EA(a,b,mergeop,x)   ((a)._WS_weapons x _WS_##b)
- # define WEPSET_OP2_EA(a,b,mergeop,x,y) ((a)._WS_weapons x _WS_##b y (a)._WS_weapons)
- # define WEPSET_OP1_EW(a,b,mergeop,x)   ((a)._WS_weapons x WEPSET_BIT(b))
- # define WEPSET_OP2_EW(a,b,mergeop,x,y) ((a)._WS_weapons x WEPSET_BIT(b) y (a)._WS_weapons)
- # define WEPSET_OP1_AE(a,b,mergeop,x)   (_WS_##a x (b)._WS_weapons)
- # define WEPSET_OP2_AE(a,b,mergeop,x,y) (_WS_##a x (b)._WS_weapons y _WS_##a)
- # define WEPSET_OP1_AA(a,b,mergeop,x)   (_WS_##a x _WS_##b)
- # define WEPSET_OP2_AA(a,b,mergeop,x,y) (_WS_##a x _WS_##b y _WS_##a)
- # define WEPSET_OP1_AW(a,b,mergeop,x)   (_WS_##a x WEPSET_BIT(b))
- # define WEPSET_OP2_AW(a,b,mergeop,x,y) (_WS_##a x WEPSET_BIT(b) y _WS_##a)
- #else
- # define WEP_MAXCOUNT 48
- # define WEP_FIRST2 25
- .float _WS1_weapons;
- .float _WS2_weapons;
- # define WEPSET_BIT1(a)                 (((a) < WEP_FIRST2) ? power2of((a) - WEP_FIRST) : 0)
- # define WEPSET_BIT2(a)                 (((a) >= WEP_FIRST2) ? power2of((a) - WEP_FIRST2) : 0)
- # define WEPSET_DECLARE_A(a)            float _WS1_##a, _WS2_##a
- # define WEPSET_CLEAR_E(e)              ((e)._WS1_weapons = (e)._WS2_weapons = 0)
- # define WEPSET_CLEAR_A(a)              ((_WS1_##a) = (_WS2_##a) = 0)
- # define WEPSET_EMPTY_E(e)              ((e)._WS1_weapons == 0 && (e)._WS2_weapons == 0)
- # define WEPSET_EMPTY_A(a)              ((_WS1_##a) == 0 && (_WS2_##a) == 0)
- # define WEPSET_COPY_AS(a)              ((_WS1_##a) = getstati(STAT_WEAPONS), (_WS2_##a) = getstati(STAT_WEAPONS2))
- # define WEPSET_ADDSTAT()               addstat(STAT_WEAPONS, AS_INT, _WS1_weapons); addstat(STAT_WEAPONS2, AS_INT, _WS2_weapons)
- # define WEPSET_WRITE_E(dest,a)         WriteInt24_t(dest, (a)._WS1_weapons); WriteInt24_t(dest, (a)._WS2_weapons)
- # define WEPSET_WRITE_A(dest,a)         WriteInt24_t(dest, _WS1_##a); WriteInt24_t(dest, _WS2_##a)
- # define WEPSET_WRITE_W(dest,a)         WriteInt24_t(dest, WEPSET_BIT1(a)); WriteInt24_t(dest, WEPSET_BIT2(a))
- # define WEPSET_READ_E(a)               (a)._WS1_weapons = ReadInt24_t(); (a)._WS2_weapons = ReadInt24_t()
- # define WEPSET_READ_A(a)               (_WS1_##a) = ReadInt24_t(); (_WS2_##a) = ReadInt24_t()
- # define WEPSET_OP1_EE(a,b,mergeop,x)   (((a)._WS1_weapons x (b)._WS1_weapons) mergeop ((a)._WS2_weapons x (b)._WS2_weapons))
- # define WEPSET_OP2_EE(a,b,mergeop,x,y) (((a)._WS1_weapons x (b)._WS1_weapons y (a)._WS1_weapons) mergeop ((a)._WS2_weapons x (b)._WS2_weapons y (a)._WS2_weapons))
- # define WEPSET_OP1_EA(a,b,mergeop,x)   (((a)._WS1_weapons x _WS1_##b) mergeop ((a)._WS2_weapons x _WS2_##b))
- # define WEPSET_OP2_EA(a,b,mergeop,x,y) (((a)._WS1_weapons x _WS1_##b y (a)._WS1_weapons) mergeop ((a)._WS2_weapons x _WS2_##b y (a)._WS2_weapons))
- # define WEPSET_OP1_EW(a,b,mergeop,x)   (((a)._WS1_weapons x WEPSET_BIT1(b)) mergeop ((a)._WS2_weapons x WEPSET_BIT2(b)))
- # define WEPSET_OP2_EW(a,b,mergeop,x,y) (((a)._WS1_weapons x WEPSET_BIT1(b) y (a)._WS1_weapons) mergeop ((a)._WS2_weapons x WEPSET_BIT2(b) y (a)._WS2_weapons))
- # define WEPSET_OP1_AE(a,b,mergeop,x)   ((_WS1_##a x (b)._WS1_weapons) mergeop (_WS2_##a x (b)._WS2_weapons))
- # define WEPSET_OP2_AE(a,b,mergeop,x,y) ((_WS1_##a x (b)._WS1_weapons y _WS1_##a) mergeop (_WS2_##a x (b)._WS2_weapons y _WS2_##a))
- # define WEPSET_OP1_AA(a,b,mergeop,x)   ((_WS1_##a x _WS1_##b) mergeop (_WS2_##a x _WS2_##b))
- # define WEPSET_OP2_AA(a,b,mergeop,x,y) ((_WS1_##a x _WS1_##b y _WS1_##a) mergeop (_WS2_##a x _WS2_##b y _WS2_##a))
- # define WEPSET_OP1_AW(a,b,mergeop,x)   ((_WS1_##a x WEPSET_BIT1(b)) mergeop (_WS2_##a x WEPSET_BIT2(b)))
- # define WEPSET_OP2_AW(a,b,mergeop,x,y) ((_WS1_##a x WEPSET_BIT1(b) y _WS1_##a) mergeop (_WS2_##a x WEPSET_BIT2(b) y _WS2_##a))
- #endif
- #define XX ,
- #define WEPSET_COPY_EE(a,b)            WEPSET_OP1_EE(a,b,XX,=)
- #define WEPSET_EQ_EE(a,b)              WEPSET_OP1_EE(a,b,&&,==)
- #define WEPSET_OR_EE(a,b)              WEPSET_OP1_EE(a,b,XX,|=)
- #define WEPSET_AND_EE(a,b)             WEPSET_OP2_EE(a,b,XX,=,&)
- #define WEPSET_ANDNOT_EE(a,b)          WEPSET_OP1_EE(a,b,XX,&~=)
- #define WEPSET_CONTAINS_ANY_EE(a,b) !!(WEPSET_OP1_EE(a,b,||,&))
- #define WEPSET_CONTAINS_ALL_EE(a,b)    WEPSET_OP2_EE(b,a,&&,==,&)
- #define WEPSET_COPY_EA(a,b)            WEPSET_OP1_EA(a,b,XX,=)
- #define WEPSET_EQ_EA(a,b)              WEPSET_OP1_EA(a,b,&&,==)
- #define WEPSET_OR_EA(a,b)              WEPSET_OP1_EA(a,b,XX,|=)
- #define WEPSET_AND_EA(a,b)             WEPSET_OP2_EA(a,b,XX,=,&)
- #define WEPSET_ANDNOT_EA(a,b)          WEPSET_OP1_EA(a,b,XX,&~=)
- #define WEPSET_CONTAINS_ANY_EA(a,b) !!(WEPSET_OP1_EA(a,b,||,&))
- #define WEPSET_CONTAINS_ALL_EA(a,b)    WEPSET_OP2_EA(b,a,&&,==,&)
- #define WEPSET_COPY_EW(a,b)            WEPSET_OP1_EW(a,b,XX,=)
- #define WEPSET_EQ_EW(a,b)              WEPSET_OP1_EW(a,b,&&,==)
- #define WEPSET_OR_EW(a,b)              WEPSET_OP1_EW(a,b,XX,|=)
- #define WEPSET_AND_EW(a,b)             WEPSET_OP2_EW(a,b,XX,=,&)
- #define WEPSET_ANDNOT_EW(a,b)          WEPSET_OP1_EW(a,b,XX,&~=)
- #define WEPSET_CONTAINS_EW(a,b)     !!(WEPSET_OP1_EW(a,b,||,&))
- #define WEPSET_COPY_AE(a,b)            WEPSET_OP1_AE(a,b,XX,=)
- #define WEPSET_EQ_AE(a,b)              WEPSET_OP1_AE(a,b,&&,==)
- #define WEPSET_OR_AE(a,b)              WEPSET_OP1_AE(a,b,XX,|=)
- #define WEPSET_AND_AE(a,b)             WEPSET_OP2_AE(a,b,XX,=,&)
- #define WEPSET_ANDNOT_AE(a,b)          WEPSET_OP1_AE(a,b,XX,&~=)
- #define WEPSET_CONTAINS_ANY_AE(a,b) !!(WEPSET_OP1_AE(a,b,||,&))
- #define WEPSET_CONTAINS_ALL_AE(a,b)    WEPSET_OP2_AE(b,a,&&,==,&)
- #define WEPSET_COPY_AA(a,b)            WEPSET_OP1_AA(a,b,XX,=)
- #define WEPSET_EQ_AA(a,b)              WEPSET_OP1_AA(a,b,&&,==)
- #define WEPSET_OR_AA(a,b)              WEPSET_OP1_AA(a,b,XX,|=)
- #define WEPSET_AND_AA(a,b)             WEPSET_OP2_AA(a,b,XX,=,&)
- #define WEPSET_ANDNOT_AA(a,b)          WEPSET_OP1_AA(a,b,XX,&~=)
- #define WEPSET_CONTAINS_ANY_AA(a,b) !!(WEPSET_OP1_AA(a,b,||,&))
- #define WEPSET_CONTAINS_ALL_AA(a,b)    WEPSET_OP2_AA(b,a,&&,==,&)
- #define WEPSET_COPY_AW(a,b)            WEPSET_OP1_AW(a,b,XX,=)
- #define WEPSET_EQ_AW(a,b)              WEPSET_OP1_AW(a,b,&&,==)
- #define WEPSET_OR_AW(a,b)              WEPSET_OP1_AW(a,b,XX,|=)
- #define WEPSET_AND_AW(a,b)             WEPSET_OP2_AW(a,b,XX,=,&)
- #define WEPSET_ANDNOT_AW(a,b)          WEPSET_OP1_AW(a,b,XX,&~=)
- #define WEPSET_CONTAINS_AW(a,b)     !!(WEPSET_OP1_AW(a,b,||,&))
- WEPSET_DECLARE_A(WEPBIT_ALL);
- WEPSET_DECLARE_A(WEPBIT_SUPERWEAPONS);
++const float AMMO_COUNT = 4; // amount of ammo types to show in the inventory panel
 +
 +// variables:
 +string weaponorder_byid;
 +
++// Weapon sets
++typedef vector WepSet;
++WepSet WepSet_FromWeapon(float a);
++#ifdef SVQC
++void WepSet_AddStat();
++void WriteWepSet(float dest, WepSet w);
++#endif
++#ifdef CSQC
++WepSet WepSet_GetFromStat();
++WepSet ReadWepSet();
++#endif
++
++// Weapon name macros
++#define WEP_FIRST 1
++#define WEP_MAXCOUNT 24 // Increase as needed. Can be up to three times as much.
++float WEP_COUNT;
++float WEP_LAST;
++WepSet WEPSET_ALL;
++WepSet WEPSET_SUPERWEAPONS;
++
 +// functions:
 +entity get_weaponinfo(float id);
 +string W_FixWeaponOrder(string order, float complete);
 +string W_NameWeaponOrder(string order);
 +string W_NumberWeaponOrder(string order);
 +
 +// ammo types
 +.float ammo_shells;
 +.float ammo_nails;
 +.float ammo_rockets;
 +.float ammo_cells;
 +.float ammo_fuel;
 +.float ammo_batteries; // dummy
 +
 +// entity properties of weaponinfo:
 +.float weapon; // WEP_...
++.WepSet weapons; // WEPSET_...
 +.string netname; // short name
 +.string message; // human readable name
 +.float items; // IT_...
 +.float(float) weapon_func; // w_...
 +.string mdl; // modelname without g_, v_, w_
 +.string model; // full name of g_ model
 +.float spawnflags; // WEPSPAWNFLAG_... combined
 +.float impulse; // weapon impulse
 +.float bot_pickupbasevalue; // bot weapon priority
 +.string model2; // wpn- sprite name
 +..float ammo_field; // main ammo field
- void register_weapon(float id, float(float) func, float ammotype, float i, float weapontype, float pickupbasevalue, string modelname, string shortname, string wname);
 +
 +// other useful macros
 +#define WEP_ACTION(wpn,wrequest) (get_weaponinfo(wpn)).weapon_func(wrequest)
 +
 +// =====================
 +//  Weapon Registration
 +// =====================
 +
 +float w_null(float dummy);
- #define WEP_FIRST 1
- float WEP_COUNT;
- float WEP_LAST;
++void register_weapon(float id, WepSet bit, float(float) func, float ammotype, float i, float weapontype, float pickupbasevalue, string modelname, string shortname, string wname);
 +void register_weapons_done();
 +
- #define REGISTER_WEAPON_2(id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \
 +// note: the fabs call is just there to hide "if result is constant" warning
-               WEPSET_OR_AW(WEPBIT_ALL, id); \
-               if(fabs(weapontype & WEP_FLAG_SUPERWEAPON)) \
-                       WEPSET_OR_AW(WEPBIT_SUPERWEAPONS, id); \
++#define REGISTER_WEAPON_2(id,bit,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \
 +      float id; \
++      WepSet bit; \
 +      float func(float); \
 +      void RegisterWeapons_##id() \
 +      { \
 +              WEP_LAST = (id = WEP_FIRST + WEP_COUNT); \
-               register_weapon(id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname); \
++              bit = WepSet_FromWeapon(id); \
++              WEPSET_ALL |= bit; \
++              if((weapontype) & WEP_FLAG_SUPERWEAPON) \
++                      WEPSET_SUPERWEAPONS |= bit; \
 +              ++WEP_COUNT; \
-       REGISTER_WEAPON_2(WEP_##id,w_null,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname)
++              register_weapon(id,bit,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname); \
 +      } \
 +      ACCUMULATE_FUNCTION(RegisterWeapons, RegisterWeapons_##id)
 +#ifdef MENUQC
 +#define REGISTER_WEAPON(id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \
-       REGISTER_WEAPON_2(WEP_##id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname)
++      REGISTER_WEAPON_2(WEP_##id,WEPSET_##id,w_null,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname)
 +#else
 +#define REGISTER_WEAPON(id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \
++      REGISTER_WEAPON_2(WEP_##id,WEPSET_##id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname)
 +#endif
 +
 +#define MO_NONE 0
 +#define MO_PRI 1
 +#define MO_SEC 2
 +#define MO_BOTH 3
 +
 +#define WEP_DUPECHECK(dupecheck,cvar) \
 +      #ifndef dupecheck \
 +              #define dupecheck \
 +              float cvar; \
 +      #else \
 +              #error DUPLICATE WEAPON CVAR: cvar \
 +      #endif
 +
 +/*
 +#define WEP_CLEAN_DUPECHECK(dupecheck) \
 +      #ifdef WEP_CVAR_##weapon##_##name \
 +              #undef WEP_CVAR_##weapon##_##name \
 +      #endif
 +*/
 +
 +#define WEP_ADD_CVAR(weapon,mode,name) \
 +      #if mode == MO_PRI \
 +              WEP_DUPECHECK(WEP_CVAR_P_##weapon##_##name, autocvar_g_balance_##weapon##_primary_##name) \
 +      #endif \
 +      #if mode == MO_SEC \
 +              WEP_DUPECHECK(WEP_CVAR_S_##weapon##_##name, autocvar_g_balance_##weapon##_secondary_##name) \
 +      #endif \
 +      #if mode == MO_BOTH \
 +              WEP_DUPECHECK(WEP_CVAR_P_##weapon##_##name, autocvar_g_balance_##weapon##_primary_##name) \
 +              WEP_DUPECHECK(WEP_CVAR_S_##weapon##_##name, autocvar_g_balance_##weapon##_secondary_##name) \
 +      #endif \
 +      #if mode == MO_NONE \
 +              WEP_DUPECHECK(WEP_CVAR_##weapon##_##name, autocvar_g_balance_##weapon##_##name) \
 +      #endif
 +
 +#define WEP_CVAR(weapon,name) autocvar_g_balance_##weapon##_##name
 +#define WEP_CVAR_PRI(weapon,name) WEP_CVAR(weapon, primary_##name)
 +#define WEP_CVAR_SEC(weapon,name) WEP_CVAR(weapon, secondary_##name)
 +#define WEP_CVAR_BOTH(weapon,mode,name) ((mode == MO_PRI) ? WEP_CVAR_PRI(weapon, name) : WEP_CVAR_SEC(weapon, name))
 +
 +#define WEP_ADD_PROP(weapon,prop,name) \
 +      .float ##prop; \
 +      WEP_DUPECHECK(WEP_CVAR_##weapon##_##name, autocvar_g_balance_##weapon##_##name)
 +
 +#define WEP_SET_PROP(wepid,weapon,prop,name) get_weaponinfo(##wepid).##prop = autocvar_g_balance_##weapon##_##name;
 +
 +#define WEP_SET_PROPS(wepsettings,wepid) \
 +      #define WEP_ADD_CVAR(weapon,mode,name) \
 +      #define WEP_ADD_PROP(weapon,prop,name) WEP_SET_PROP(wepid,weapon,prop,name) \
 +      wepsettings \
 +      #undef WEP_ADD_CVAR \
 +      #undef WEP_ADD_PROP
 +
 +#include "all.qh"
 +
 +#undef WEP_ADD_CVAR
 +#undef WEP_ADD_PROP
 +#undef REGISTER_WEAPON
 +ACCUMULATE_FUNCTION(RegisterWeapons, register_weapons_done)
 +
 +string W_FixWeaponOrder(string order, float complete);
 +string W_NumberWeaponOrder(string order);
 +string W_NameWeaponOrder(string order);
 +string W_FixWeaponOrder_BuildImpulseList(string o);
 +string W_FixWeaponOrder_AllowIncomplete(string order);
 +string W_FixWeaponOrder_ForceComplete(string order);
 +
 +void W_RandomWeapons(entity e, float n);
 +
 +string W_Name(float weaponid);
 +
 +float W_AmmoItemCode(float wpn);
index 5296b0ea7d71958f239d530d9f9945283cb36401,7e23903ed27548a2c3df90d821b84fd5cfbdfb82..31631454e842489bea282c86eadf1a2ca78174e9
@@@ -88,11 -88,11 +88,11 @@@ void havocbot_ai(
        if (self.bot_aimtarg)
        {
                self.aistatus |= AI_STATUS_ATTACKING;
-               self.aistatus &~= AI_STATUS_ROAMING;
+               self.aistatus &= ~AI_STATUS_ROAMING;
  
-               if(!WEPSET_EMPTY_E(self))
+               if(self.weapons)
                {
 -                      weapon_action(self.weapon, WR_AIM);
 +                      WEP_ACTION(self.weapon, WR_AIM);
                        if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(self))
                        {
                                self.BUTTON_ATCK = FALSE;
@@@ -542,9 -542,9 +542,9 @@@ void havocbot_movetogoal(
                }
        }
        else if(self.aistatus & AI_STATUS_OUT_JUMPPAD)
-               self.aistatus &~= AI_STATUS_OUT_JUMPPAD;
+               self.aistatus &= ~AI_STATUS_OUT_JUMPPAD;
  
 -      // If there is a trigger_hurt right below try to use the jetpack or make a rocketjump
 +      // If there is a trigger_hurt right below try to use the jetpack or make a rocketjump // WEAPONTODO: move this to bot think!
        if(skill>6)
        if not(self.flags & FL_ONGROUND)
        {
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 5228e575c60d849a94c6166a134dfc37dc842b89,17b75aeab646cdf039192b08a029d69b514a7681..95f121713442991c4d3df604c7a83351066cb46a
@@@ -191,12 -190,16 +191,13 @@@ void w_ready()
  .float weapon_nextthink;
  .void() weapon_think;
  
 -//float       PLAYER_WEAPONSELECTION_DELAY = );
 -const float   PLAYER_WEAPONSELECTION_SPEED = 18;
 -const vector  PLAYER_WEAPONSELECTION_RANGE = '0 20 -40';
  // weapon states (self.weaponentity.state)
float WS_CLEAR                        = 0; // no weapon selected
float WS_RAISE                        = 1; // raise frame
float WS_DROP                 = 2; // deselecting frame
float WS_INUSE                        = 3; // fire state
float WS_READY                        = 4; // idle frame
const float WS_CLEAR                  = 0; // no weapon selected
const float WS_RAISE                  = 1; // raise frame
const float WS_DROP                   = 2; // deselecting frame
const float WS_INUSE                  = 3; // fire state
const float WS_READY                  = 4; // idle frame
  
  // there is 2 weapon tics that can run in one server frame
  #define W_TICSPERFRAME 2
index 1537cd8d431297571855a8573eac3bbe4df479f7,8dca538f1a417a46b271458557e8ecda139a3870..142bd7a8b8c2246a2493f2091996ab9f4f3cb643
@@@ -164,10 -164,10 +164,10 @@@ void func_breakable_destroy() 
        func_breakable_destroyed();
  
        if(self.noise)
-               sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NORM);
+               sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
  
        if(self.dmg)
 -              RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, self.dmg_force, DEATH_HURTTRIGGER, world);
 +              RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, world, self.dmg_force, DEATH_HURTTRIGGER, world);
  
        if(self.cnt)
                pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count);
Simple merge
Simple merge
Simple merge
index 771595b7f5c3a7465cbfb000360b7a23e97bcb1e,6a2458000fce42b1fb023821497a09b84efffe37..921d0b46451f8e478393daf8ed9998e0f78b2828
@@@ -860,8 -861,8 +860,8 @@@ void readplayerstartcvars(
        for (i = WEP_FIRST; i <= WEP_LAST; ++i)
        {
                e = get_weaponinfo(i);
-               if(WEPSET_CONTAINS_AW(start_weapons, i) || WEPSET_CONTAINS_AW(warmup_start_weapons, i))
+               if((start_weapons | warmup_start_weapons) & WepSet_FromWeapon(i))
 -                      weapon_action(i, WR_PRECACHE);
 +                      WEP_ACTION(i, WR_INIT);
        }
  
        start_ammo_shells = max(0, start_ammo_shells);
index db0de8b3556510b70763ec9ba2a744609a9eedee,5831250d2d4b29cf144821413fc4028d7239483f..731927a771d61e474438d700d4534bd50989d743
@@@ -139,10 -139,10 +139,10 @@@ void GiveBall(entity plyr, entity ball
        
        ownr = self;
        self = plyr;    
-       WEPSET_COPY_EE(self.weaponentity, self);
+       self.weaponentity.weapons = self.weapons;
        self.weaponentity.switchweapon = self.weapon;
-       WEPSET_COPY_EW(self, WEP_PORTO);
+       self.weapons = WEPSET_PORTO;
 -      weapon_action(WEP_PORTO, WR_RESETPLAYER);
 +      WEP_ACTION(WEP_PORTO, WR_RESETPLAYER);
        self.switchweapon = WEP_PORTO;
        W_SwitchWeapon(WEP_PORTO);
        self = ownr;
@@@ -917,10 -917,10 +917,10 @@@ MUTATOR_HOOKFUNCTION(nexball_PlayerPreT
                }
                else
                {                       
-                       if(!WEPSET_EMPTY_E(self.weaponentity))
+                       if(self.weaponentity.weapons)
                        {
-                               WEPSET_COPY_EE(self, self.weaponentity);
+                               self.weapons = self.weaponentity.weapons;
 -                              weapon_action(WEP_PORTO, WR_RESETPLAYER);
 +                              WEP_ACTION(WEP_PORTO, WR_RESETPLAYER);
                                self.switchweapon = self.weaponentity.switchweapon;
                                W_SwitchWeapon(self.switchweapon);
                                
Simple merge
index ea34a427e2160b9cc2a0585bad2ae2156fae5f30,31aa5caf791b29bbe8fb69e489aa935e167bb4d9..69223cc020b80411e9c3fad67e12fcd0dac8fd9d
@@@ -1096,7 -1139,183 +1096,6 @@@ void StartItem (string itemmodel, strin
                return;
        }
  }
 -
 -float weaponswapping;
 -float internalteam;
 -
 -void weapon_defaultspawnfunc(float wpn)
 -{
 -      entity e;
 -      float t;
 -      var .float ammofield;
 -      string s;
 -      entity oldself;
 -      float i, j;
 -      float f;
 -
 -      if(self.classname != "droppedweapon" && self.classname != "replacedweapon")
 -      {
 -              e = get_weaponinfo(wpn);
 -
 -              if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
 -              {
 -                      objerror("Attempted to spawn a mutator-blocked weapon rejected");
 -                      startitem_failed = TRUE;
 -                      return;
 -              }
 -
 -              s = W_Apply_Weaponreplace(e.netname);
 -              ret_string = s;
 -              other = e;
 -              MUTATOR_CALLHOOK(SetWeaponreplace);
 -              s = ret_string;
 -              if(s == "")
 -              {
 -                      remove(self);
 -                      startitem_failed = TRUE;
 -                      return;
 -              }
 -              t = tokenize_console(s);
 -              if(t >= 2)
 -              {
 -                      self.team = --internalteam;
 -                      oldself = self;
 -                      for(i = 1; i < t; ++i)
 -                      {
 -                              s = argv(i);
 -                              for(j = WEP_FIRST; j <= WEP_LAST; ++j)
 -                              {
 -                                      e = get_weaponinfo(j);
 -                                      if(e.netname == s)
 -                                      {
 -                                              self = spawn();
 -                                              copyentity(oldself, self);
 -                                              self.classname = "replacedweapon";
 -                                              weapon_defaultspawnfunc(j);
 -                                              break;
 -                                      }
 -                              }
 -                              if(j > WEP_LAST)
 -                              {
 -                                      print("The weapon replace list for ", oldself.classname, " contains an unknown weapon ", s, ". Skipped.\n");
 -                              }
 -                      }
 -                      self = oldself;
 -              }
 -              if(t >= 1) // always the case!
 -              {
 -                      s = argv(0);
 -                      wpn = 0;
 -                      for(j = WEP_FIRST; j <= WEP_LAST; ++j)
 -                      {
 -                              e = get_weaponinfo(j);
 -                              if(e.netname == s)
 -                              {
 -                                      wpn = j;
 -                                      break;
 -                              }
 -                      }
 -                      if(j > WEP_LAST)
 -                      {
 -                              print("The weapon replace list for ", self.classname, " contains an unknown weapon ", s, ". Skipped.\n");
 -                      }
 -              }
 -              if(wpn == 0)
 -              {
 -                      remove(self);
 -                      startitem_failed = TRUE;
 -                      return;
 -              }
 -      }
 -
 -      e = get_weaponinfo(wpn);
 -
 -      if(!self.respawntime)
 -      {
 -              if(e.weapons & WEPSET_SUPERWEAPONS)
 -              {
 -                      self.respawntime = g_pickup_respawntime_superweapon;
 -                      self.respawntimejitter = g_pickup_respawntimejitter_superweapon;
 -              }
 -              else
 -              {
 -                      self.respawntime = g_pickup_respawntime_weapon;
 -                      self.respawntimejitter = g_pickup_respawntimejitter_weapon;
 -              }
 -      }
 -
 -      if(e.weapons & WEPSET_SUPERWEAPONS)
 -              if(!self.superweapons_finished)
 -                      self.superweapons_finished = autocvar_g_balance_superweapons_time;
 -
 -      if(e.items)
 -      {
 -              for(i = 0, j = 1; i < 24; ++i, j *= 2)
 -              {
 -                      if(e.items & j)
 -                      {
 -                              ammofield = Item_CounterField(j);
 -                              if(!self.ammofield)
 -                                      self.ammofield = cvar(strcat("g_pickup_", Item_CounterFieldName(j), "_weapon"));
 -                      }
 -              }
 -      }
 -
 -      // pickup anyway
 -      if(g_pickup_weapons_anyway)
 -              self.pickup_anyway = TRUE;
 -
 -      f = FL_WEAPON;
 -
 -      // no weapon-stay on superweapons
 -      if(e.weapons & WEPSET_SUPERWEAPONS)
 -              f |= FL_NO_WEAPON_STAY;
 -
 -      // weapon stay isn't supported for teamed weapons
 -      if(self.team)
 -              f |= FL_NO_WEAPON_STAY;
 -
 -      StartItem(e.model, "weapons/weaponpickup.wav", self.respawntime, self.respawntimejitter, e.message, 0, e.weapon, f, weapon_pickupevalfunc, e.bot_pickupbasevalue);
 -      if (self.modelindex) // don't precache if self was removed
 -              weapon_action(e.weapon, WR_PRECACHE);
 -}
 -
 -void spawnfunc_weapon_shotgun (void);
 -void spawnfunc_weapon_uzi (void) {
 -      if(autocvar_sv_q3acompat_machineshotgunswap)
 -      if(self.classname != "droppedweapon")
 -      {
 -              weapon_defaultspawnfunc(WEP_SHOTGUN);
 -              return;
 -      }
 -      weapon_defaultspawnfunc(WEP_UZI);
 -}
 -
 -void spawnfunc_weapon_shotgun (void) {
 -      if(autocvar_sv_q3acompat_machineshotgunswap)
 -      if(self.classname != "droppedweapon")
 -      {
 -              weapon_defaultspawnfunc(WEP_UZI);
 -              return;
 -      }
 -      weapon_defaultspawnfunc(WEP_SHOTGUN);
 -}
 -
 -void spawnfunc_weapon_nex (void)
 -{
 -      weapon_defaultspawnfunc(WEP_NEX);
 -}
 -
 -void spawnfunc_weapon_minstanex (void)
 -{
 -      weapon_defaultspawnfunc(WEP_MINSTANEX);
 -}
 -
 -void spawnfunc_weapon_rocketlauncher (void)
 -{
 -      weapon_defaultspawnfunc(WEP_ROCKET_LAUNCHER);
 -}
--
  void spawnfunc_item_rockets (void) {
        if(!self.ammo_rockets)
                self.ammo_rockets = g_pickup_rockets;
@@@ -1321,9 -1541,9 +1320,9 @@@ void spawnfunc_target_items (void
                                        e = get_weaponinfo(j);
                                        if(argv(i) == e.netname)
                                        {
-                                               WEPSET_OR_EW(self, j);
+                                               self.weapons |= WepSet_FromWeapon(j);
                                                if(self.spawnflags == 0 || self.spawnflags == 2)
 -                                                      weapon_action(e.weapon, WR_PRECACHE);
 +                                                      WEP_ACTION(e.weapon, WR_INIT);
                                                break;
                                        }
                                }
@@@ -1435,10 -1655,17 +1434,10 @@@ void spawnfunc_item_jetpack(void
        StartItem ("models/items/g_jetpack.md3", "misc/itempickup.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Jet pack", IT_JETPACK, 0, FL_POWERUP, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
  }
  
 -
 -#define OP_SET 0
 -#define OP_MIN 1
 -#define OP_MAX 2
 -#define OP_PLUS 3
 -#define OP_MINUS 4
 -
  float GiveWeapon(entity e, float wpn, float op, float val)
  {
-       float v0, v1;
-       v0 = WEPSET_CONTAINS_EW(e, wpn);
+       WepSet v0, v1;
+       v0 = (e.weapons & WepSet_FromWeapon(wpn));
        switch(op)
        {
                case OP_SET:
@@@ -1544,7 -1771,14 +1543,6 @@@ void GiveRot(entity e, float v0, float 
        else if(v0 > v1)
                e.regenfield = max(e.regenfield, time + regentime);
  }
 -
 -#define PREGIVE_WEAPONS(e) WepSet save_weapons; save_weapons = e.weapons
 -#define PREGIVE(e,f) float save_##f; save_##f = (e).f
 -#define POSTGIVE_WEAPON(e,b,snd_incr,snd_decr) GiveSound((e), !!(save_weapons & WepSet_FromWeapon(b)), !!(e.weapons & WepSet_FromWeapon(b)), 0, snd_incr, snd_decr)
 -#define POSTGIVE_BIT(e,f,b,snd_incr,snd_decr) GiveSound((e), save_##f & (b), (e).f & (b), 0, snd_incr, snd_decr)
 -#define POSTGIVE_VALUE(e,f,t,snd_incr,snd_decr) GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr)
 -#define POSTGIVE_VALUE_ROT(e,f,t,rotfield,rottime,regenfield,regentime,snd_incr,snd_decr) GiveRot((e), save_##f, (e).f, rotfield, rottime, regenfield, regentime); GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr)
--
  float GiveItems(entity e, float beginarg, float endarg)
  {
        float got, i, j, val, op;
                if(wi.weapon)
                {
                        POSTGIVE_WEAPON(e, j, "weapons/weaponpickup.wav", string_null);
-                       if not(WEPSET_CONTAINS_AW(save_weapons, j))
-                               if(WEPSET_CONTAINS_EW(e, j))
+                       if not(save_weapons & WepSet_FromWeapon(j))
+                               if(e.weapons & WepSet_FromWeapon(j))
 -                                      weapon_action(wi.weapon, WR_PRECACHE);
 +                                      WEP_ACTION(wi.weapon, WR_INIT);
                }
        }
        POSTGIVE_VALUE(e, strength_finished, 1, "misc/powerup.wav", "misc/poweroff.wav");
index 89bbbc5d1400d49d642073b9fad19393680e515f,0000000000000000000000000000000000000000..a64c8396a88a2c666821bba7ee368585f99f280a
mode 100644,000000..100644
--- /dev/null
@@@ -1,125 -1,0 +1,125 @@@
- #define PREGIVE_WEAPONS(e) WEPSET_DECLARE_A(save_weapons); WEPSET_COPY_AE(save_weapons, e)
 +#define ISF_LOCATION 2
 +#define ISF_MODEL    4
 +#define ISF_STATUS   8
 +    #define ITS_STAYWEP   1
 +    #define ITS_ANIMATE1  2
 +    #define ITS_ANIMATE2  4
 +    #define ITS_AVAILABLE 8
 +    #define ITS_ALLOWFB   16
 +    #define ITS_ALLOWSI   32
 +    #define ITS_POWERUP   64
 +#define ISF_COLORMAP 16
 +#define ISF_DROP 32
 +#define ISF_ANGLES 64
 +
 +.float ItemStatus;
 +
 +#ifdef CSQC
 +
 +var float  autocvar_cl_animate_items = 1;
 +var float  autocvar_cl_ghost_items = 0.45;
 +var vector autocvar_cl_ghost_items_color = '-1 -1 -1';
 +var float  autocvar_cl_fullbright_items = 0;
 +var vector autocvar_cl_weapon_stay_color = '2 0.5 0.5';
 +var float  autocvar_cl_weapon_stay_alpha = 0.75;
 +var float  autocvar_cl_simple_items = 0;
 +var string autocvr_cl_simpleitems_postfix = "_simple";
 +.float  spawntime;
 +.float  gravity;
 +.vector colormod;
 +void ItemDraw();
 +
 +void ItemDrawSimple();
 +
 +void ItemRead(float _IsNew);
 +
 +#endif
 +
 +#ifdef SVQC
 +float autocvar_sv_simple_items;
 +float ItemSend(entity to, float sf);
 +
 +
 +float have_pickup_item(void);
 +
 +#define ITEM_RESPAWN_TICKS 10
 +
 +#define ITEM_RESPAWNTIME(i)         ((i).respawntime + crandom() * (i).respawntimejitter)
 +      // range: respawntime - respawntimejitter .. respawntime + respawntimejitter
 +#define ITEM_RESPAWNTIME_INITIAL(i) (ITEM_RESPAWN_TICKS + random() * ((i).respawntime + (i).respawntimejitter - ITEM_RESPAWN_TICKS))
 +      // range: 10 .. respawntime + respawntimejitter
 +
 +string Item_CounterFieldName(float it);
 +
 +.float max_armorvalue;
 +.float pickup_anyway;
 +
 +void Item_Show (entity e, float mode);
 +
 +void Item_Respawn (void);
 +
 +void Item_RespawnCountdown (void);
 +void Item_ScheduleRespawnIn(entity e, float t);
 +
 +void Item_ScheduleRespawn(entity e);
 +
 +void Item_ScheduleInitialRespawn(entity e);
 +float ITEM_MODE_NONE = 0;
 +float ITEM_MODE_HEALTH = 1;
 +float ITEM_MODE_ARMOR = 2;
 +float ITEM_MODE_FUEL = 3;
 +float Item_GiveAmmoTo(entity item, entity player, .float ammofield, float ammomax, float mode);
 +
 +float Item_GiveTo(entity item, entity player);
 +
 +void Item_Touch (void);
 +
 +void Item_Reset();
 +
 +void Item_FindTeam();
 +// Savage: used for item garbage-collection
 +// TODO: perhaps nice special effect?
 +void RemoveItem(void); // WEAPONTODO
 +
 +// pickup evaluation functions
 +// these functions decide how desirable an item is to the bots
 +
 +float generic_pickupevalfunc(entity player, entity item);// {return item.bot_pickupbasevalue;} // WEAPONTODO
 +
 +float weapon_pickupevalfunc(entity player, entity item);
 +
 +float commodity_pickupevalfunc(entity player, entity item);
 +
 +void Item_Damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force); // WEAPONTODO
 +
 +.float is_item;
 +void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, float defaultrespawntimejitter, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue);
 +
 +
 +void target_items_use (void);
 +
 +#define OP_SET 0
 +#define OP_MIN 1
 +#define OP_MAX 2
 +#define OP_PLUS 3
 +#define OP_MINUS 4
 +
 +float GiveWeapon(entity e, float wpn, float op, float val);
 +
 +float GiveBit(entity e, .float fld, float bit, float op, float val);
 +
 +float GiveValue(entity e, .float fld, float op, float val);
 +
 +void GiveSound(entity e, float v0, float v1, float t, string snd_incr, string snd_decr);
 +
 +void GiveRot(entity e, float v0, float v1, .float rotfield, float rottime, .float regenfield, float regentime);
 +
- #define POSTGIVE_WEAPON(e,b,snd_incr,snd_decr) GiveSound((e), WEPSET_CONTAINS_AW(save_weapons, b), WEPSET_CONTAINS_EW(e, b), 0, snd_incr, snd_decr)
++#define PREGIVE_WEAPONS(e) WepSet save_weapons; save_weapons = e.weapons
 +#define PREGIVE(e,f) float save_##f; save_##f = (e).f
++#define POSTGIVE_WEAPON(e,b,snd_incr,snd_decr) GiveSound((e), !!(save_weapons & WepSet_FromWeapon(b)), !!(e.weapons & WepSet_FromWeapon(b)), 0, snd_incr, snd_decr)
 +#define POSTGIVE_BIT(e,f,b,snd_incr,snd_decr) GiveSound((e), save_##f & (b), (e).f & (b), 0, snd_incr, snd_decr)
 +#define POSTGIVE_VALUE(e,f,t,snd_incr,snd_decr) GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr)
 +#define POSTGIVE_VALUE_ROT(e,f,t,rotfield,rottime,regenfield,regentime,snd_incr,snd_decr) GiveRot((e), save_##f, (e).f, rotfield, rottime, regenfield, regentime); GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr)
 +
 +float GiveItems(entity e, float beginarg, float endarg);
 +#endif
index c9312b34fb89927157c66420964742100d877c8e,5e0feea35bd77edd8d98186a7f2c4e31e4495ae1..30a4c60ebc9dd95e823b82ede89bf6836605074f
@@@ -515,8 -514,8 +515,8 @@@ void walker_postthink(
  
  void walker_attack()
  {
-     sound (self, CH_WEAPON_A, "weapons/uzi_fire.wav", VOL_BASE, ATTN_NORM);
+     sound (self, CH_WEAPON_A, "weapons/uzi_fire.wav", VOL_BASE, ATTEN_NORM);
 -    fireBallisticBullet (self.tur_shotorg, self.tur_shotdir_updated, self.shot_spread, self.shot_speed, 5, self.shot_dmg, self.shot_force, DEATH_TURRET_WALK_GUN, 0, 1, autocvar_g_balance_uzi_bulletconstant);
 +    fireBallisticBullet (self.tur_shotorg, self.tur_shotdir_updated, self.shot_spread, self.shot_speed, 5, self.shot_dmg, self.shot_force, DEATH_TURRET_WALK_GUN, 0, 1, WEP_CVAR(uzi, bulletconstant)); // WEAPONTODO
      endFireBallisticBullet();
      pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
  }
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 98fd533535fe129d04202c310711e4321cc56891,0000000000000000000000000000000000000000..6108c38a28ef02451e8cd45f4afeb6d6c725359a
mode 100644,000000..100644
--- /dev/null
@@@ -1,89 -1,0 +1,89 @@@
-       WEPSET_OR_EW(e, wep);
 +
 +void W_GiveWeapon (entity e, float wep)
 +{
 +      entity oldself;
 +
 +      if (!wep)
 +              return;
 +
-                       sound(player, CH_TRIGGER, "weapons/strength_fire.wav", VOL_BASE, ATTN_NORM);
++      e.weapons |= WepSet_FromWeapon(wep);
 +
 +      oldself = self;
 +      self = e;
 +
 +      if(IS_PLAYER(other))
 +              { Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_WEAPON_GOT, wep); }
 +
 +      self = oldself;
 +}
 +
 +void W_PlayStrengthSound(entity player) // void W_PlayStrengthSound
 +{
 +      if((player.items & IT_STRENGTH)
 +              && ((time > player.prevstrengthsound + autocvar_sv_strengthsound_antispam_time) // prevent insane sound spam
 +              || (time > player.prevstrengthsoundattempt + autocvar_sv_strengthsound_antispam_refire_threshold)))
 +              {
++                      sound(player, CH_TRIGGER, "weapons/strength_fire.wav", VOL_BASE, ATTEN_NORM);
 +                      player.prevstrengthsound = time;
 +              }
 +              player.prevstrengthsoundattempt = time;
 +}
 +
 +float W_CheckProjectileDamage(entity inflictor, entity projowner, float deathtype, float exception)
 +{
 +      float is_from_contents = (deathtype == DEATH_SLIME || deathtype == DEATH_LAVA);
 +      float is_from_owner = (inflictor == projowner);
 +      float is_from_exception = (exception != -1);
 +      
 +      //dprint(strcat("W_CheckProjectileDamage: from_contents ", ftos(is_from_contents), " : from_owner ", ftos(is_from_owner), " : exception ", strcat(ftos(is_from_exception), " (", ftos(exception), "). \n")));
 +
 +      if(autocvar_g_projectiles_damage <= -2)
 +      {
 +              return FALSE; // no damage to projectiles at all, not even with the exceptions
 +      }
 +      else if(autocvar_g_projectiles_damage == -1)
 +      {
 +              if(is_from_exception)
 +                      return (exception); // if exception is detected, allow it to override
 +              else
 +                      return FALSE; // otherwise, no other damage is allowed
 +      }
 +      else if(autocvar_g_projectiles_damage == 0)
 +      {
 +              if(is_from_exception)
 +                      return (exception); // if exception is detected, allow it to override
 +              else if not(is_from_contents)
 +                      return FALSE; // otherwise, only allow damage from contents
 +      }       
 +      else if(autocvar_g_projectiles_damage == 1)
 +      {
 +              if(is_from_exception)
 +                      return (exception); // if exception is detected, allow it to override
 +              else if not(is_from_contents || is_from_owner)
 +                      return FALSE; // otherwise, only allow self damage and damage from contents
 +      }
 +      else if(autocvar_g_projectiles_damage == 2) // allow any damage, but override for exceptions
 +      {
 +              if(is_from_exception)
 +                      return (exception); // if exception is detected, allow it to override
 +      }
 +
 +      return TRUE; // if none of these return, then allow damage anyway.
 +}
 +
 +void W_PrepareExplosionByDamage(entity attacker, void() explode)
 +{
 +      self.takedamage = DAMAGE_NO;
 +      self.event_damage = func_null;
 +      
 +      if(IS_CLIENT(attacker) && !autocvar_g_projectiles_keep_owner)
 +      {
 +              self.owner = attacker;
 +              self.realowner = attacker;
 +      }
 +      
 +      // do not explode NOW but in the NEXT FRAME!
 +      // because recursive calls to RadiusDamage are not allowed
 +      self.nextthink = time;
 +      self.think = explode;
 +}
index dda999ac4712d86cbf52789f601a43c7dec0256b,0000000000000000000000000000000000000000..c232795919afc01e45aeb2c39903740f6127d417
mode 100644,000000..100644
--- /dev/null
@@@ -1,273 -1,0 +1,273 @@@
-       if(wpn == WEP_HOOK && !g_grappling_hook && autocvar_g_nades && !WEPSET_CONTAINS_EW(cl, wpn) && !WEPSET_CONTAINS_AW(weaponsInMap, wpn))
 +// switch between weapons
 +void Send_WeaponComplain(entity e, float wpn, string wpnname, float type)
 +{
 +      msg_entity = e;
 +      WriteByte(MSG_ONE, SVC_TEMPENTITY);
 +      WriteByte(MSG_ONE, TE_CSQC_WEAPONCOMPLAIN);
 +      WriteByte(MSG_ONE, wpn);
 +      WriteString(MSG_ONE, wpnname);
 +      WriteByte(MSG_ONE, type);
 +}
 +
 +float client_hasweapon(entity cl, float wpn, float andammo, float complain)
 +{
 +      float f;
 +      entity oldself;
 +
 +      if(time < self.hasweapon_complain_spam)
 +              complain = 0;
 +
-       if (WEPSET_CONTAINS_EW(cl, wpn))
++      if(wpn == WEP_HOOK && !g_grappling_hook && autocvar_g_nades && !((cl.weapons | weaponsInMap) & WepSet_FromWeapon(wpn)))
 +              complain = 0;
 +              
 +      if(complain)
 +              self.hasweapon_complain_spam = time + 0.2;
 +
 +      if (wpn < WEP_FIRST || wpn > WEP_LAST)
 +      {
 +              if (complain)
 +                      sprint(self, "Invalid weapon\n");
 +              return FALSE;
 +      }
-               if (WEPSET_CONTAINS_AW(weaponsInMap, wpn))
++      if (cl.weapons & WepSet_FromWeapon(wpn))
 +      {
 +              if (andammo)
 +              {
 +                      if(cl.items & IT_UNLIMITED_WEAPON_AMMO)
 +                      {
 +                              f = 1;
 +                      }
 +                      else
 +                      {
 +                              oldself = self;
 +                              self = cl;
 +                              f = WEP_ACTION(wpn, WR_CHECKAMMO1);
 +                              f = f + WEP_ACTION(wpn, WR_CHECKAMMO2);
 +
 +                              // always allow selecting the Mine Layer if we placed mines, so that we can detonate them
 +                              entity mine;
 +                              if(wpn == WEP_MINE_LAYER)
 +                              for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
 +                                      f = 1;
 +
 +                              self = oldself;
 +                      }
 +                      if (!f)
 +                      {
 +                              if (complain)
 +                              if(IS_REAL_CLIENT(cl))
 +                              {
 +                                      play2(cl, "weapons/unavailable.wav");
 +                                      Send_WeaponComplain (cl, wpn, W_Name(wpn), 0);
 +                              }
 +                              return FALSE;
 +                      }
 +              }
 +              return TRUE;
 +      }
 +      if (complain)
 +      {
 +              // DRESK - 3/16/07
 +              // Report Proper Weapon Status / Modified Weapon Ownership Message
-       if(WEPSET_CONTAINS_EW(pl, w))
++              if (weaponsInMap & WepSet_FromWeapon(wpn))
 +              {
 +                      Send_WeaponComplain(cl, wpn, W_Name(wpn), 1);
 +
 +                      if(autocvar_g_showweaponspawns)
 +                      {
 +                              entity e;
 +                              string s;
 +
 +                              e = get_weaponinfo(wpn);
 +                              s = e.model2;
 +
 +                              for(e = world; (e = findfloat(e, weapon, wpn)); )
 +                              {
 +                                      if(e.classname == "droppedweapon")
 +                                              continue;
 +                                      if not(e.flags & FL_ITEM)
 +                                              continue;
 +                                      WaypointSprite_Spawn(
 +                                              s,
 +                                              1, 0,
 +                                              world, e.origin,
 +                                              self, 0,
 +                                              world, enemy,
 +                                              0,
 +                                              RADARICON_NONE, '0 0 0'
 +                                      );
 +                              }
 +                      }
 +              }
 +              else
 +              {
 +                      Send_WeaponComplain (cl, wpn, W_Name(wpn), 2);
 +              }
 +
 +              play2(cl, "weapons/unavailable.wav");
 +      }
 +      return FALSE;
 +}
 +
 +float W_GetCycleWeapon(entity pl, string weaponorder, float dir, float imp, float complain, float skipmissing)
 +{
 +      // We cannot tokenize in this function, as GiveItems calls this
 +      // function. Thus we must use car/cdr.
 +      float weaponwant, first_valid, prev_valid, switchtonext, switchtolast, c;
 +      string rest;
 +      switchtonext = switchtolast = 0;
 +      first_valid = prev_valid = 0;
 +      float weaponcur;
 +
 +      if(skipmissing || pl.selectweapon == 0)
 +              weaponcur = pl.switchweapon;
 +      else
 +              weaponcur = pl.selectweapon;
 +
 +      if(dir == 0)
 +              switchtonext = 1;
 +
 +      c = 0;
 +
 +      rest = weaponorder;
 +      while(rest != "")
 +      {
 +              weaponwant = stof(car(rest)); rest = cdr(rest);
 +              if(imp >= 0)
 +                      if((get_weaponinfo(weaponwant)).impulse != imp)
 +                              continue;
 +
 +              ++c;
 +
 +              if(!skipmissing || client_hasweapon(pl, weaponwant, TRUE, FALSE))
 +              {
 +                      if(switchtonext)
 +                              return weaponwant;
 +                      if(!first_valid)
 +                              first_valid = weaponwant;
 +                      if(weaponwant == weaponcur)
 +                      {
 +                              if(dir >= 0)
 +                                      switchtonext = 1;
 +                              else if(prev_valid)
 +                                      return prev_valid;
 +                              else
 +                                      switchtolast = 1;
 +                      }
 +                      prev_valid = weaponwant;
 +              }
 +      }
 +      if(first_valid)
 +      {
 +              if(switchtolast)
 +                      return prev_valid;
 +              else
 +                      return first_valid;
 +      }
 +      // complain (but only for one weapon on the button that has been pressed)
 +      if(complain)
 +      {
 +              self.weaponcomplainindex += 1;
 +              c = mod(self.weaponcomplainindex, c) + 1;
 +              rest = weaponorder;
 +              while(rest != "")
 +              {
 +                      weaponwant = stof(car(rest)); rest = cdr(rest);
 +                      if(imp >= 0)
 +                              if((get_weaponinfo(weaponwant)).impulse != imp)
 +                                      continue;
 +
 +                      --c;
 +                      if(c == 0)
 +                      {
 +                              client_hasweapon(pl, weaponwant, TRUE, TRUE);
 +                              break;
 +                      }
 +              }
 +      }
 +      return 0;
 +}
 +
 +void W_SwitchWeapon_Force(entity e, float w)
 +{
 +      e.cnt = e.switchweapon;
 +      e.switchweapon = w;
 +      e.selectweapon = w;
 +}
 +
 +// perform weapon to attack (weaponstate and attack_finished check is here)
 +void W_SwitchToOtherWeapon(entity pl)
 +{
 +      // hack to ensure it switches to an OTHER weapon (in case the other fire mode still has ammo, we want that anyway)
 +      float w, ww;
 +      w = pl.weapon;
-               WEPSET_ANDNOT_EW(pl, w);
++      if(pl.weapons & WepSet_FromWeapon(w))
 +      {
-               WEPSET_OR_EW(pl, w);
++              pl.weapons &= ~WepSet_FromWeapon(w);
 +              ww = w_getbestweapon(pl);
++              pl.weapons |= WepSet_FromWeapon(w);
 +      }
 +      else
 +              ww = w_getbestweapon(pl);
 +      if(ww)
 +              W_SwitchWeapon_Force(pl, ww);
 +}
 +
 +void W_SwitchWeapon(float imp)
 +{
 +      if (self.switchweapon != imp)
 +      {
 +              if (client_hasweapon(self, imp, TRUE, TRUE))
 +                      W_SwitchWeapon_Force(self, imp);
 +              else
 +                      self.selectweapon = imp; // update selectweapon ANYWAY
 +      }
 +      else { WEP_ACTION(self.weapon, WR_RELOAD); }
 +}
 +
 +void W_CycleWeapon(string weaponorder, float dir)
 +{
 +      float w;
 +      w = W_GetCycleWeapon(self, weaponorder, dir, -1, 1, TRUE);
 +      if(w > 0)
 +              W_SwitchWeapon(w);
 +}
 +
 +void W_NextWeaponOnImpulse(float imp)
 +{
 +      float w;
 +      w = W_GetCycleWeapon(self, self.cvar_cl_weaponpriority, +1, imp, 1, (self.cvar_cl_weaponimpulsemode == 0));
 +      if(w > 0)
 +              W_SwitchWeapon(w);
 +}
 +
 +// next weapon
 +void W_NextWeapon(float list)
 +{
 +      if(list == 0)
 +              W_CycleWeapon(weaponorder_byid, -1);
 +      else if(list == 1)
 +              W_CycleWeapon(self.weaponorder_byimpulse, -1);
 +      else if(list == 2)
 +              W_CycleWeapon(self.cvar_cl_weaponpriority, -1);
 +}
 +
 +// prev weapon
 +void W_PreviousWeapon(float list)
 +{
 +      if(list == 0)
 +              W_CycleWeapon(weaponorder_byid, +1);
 +      else if(list == 1)
 +              W_CycleWeapon(self.weaponorder_byimpulse, +1);
 +      else if(list == 2)
 +              W_CycleWeapon(self.cvar_cl_weaponpriority, +1);
 +}
 +
 +// previously used if exists and has ammo, (second) best otherwise
 +void W_LastWeapon(void)
 +{
 +      if(client_hasweapon(self, self.cnt, TRUE, FALSE))
 +              W_SwitchWeapon(self.cnt);
 +      else
 +              W_SwitchToOtherWeapon(self);
 +}
index 30b7b0bebcd9f9b7102e4ba837c77e1c3c94591b,0000000000000000000000000000000000000000..65188fef327696ad3917b0b5d01bf36e5f646d64
mode 100644,000000..100644
--- /dev/null
@@@ -1,153 -1,0 +1,153 @@@
-               if(WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS))
 +string W_Apply_Weaponreplace(string in)
 +{
 +      float n = tokenize_console(in);
 +      string out = "";
 +      float i;
 +      for(i = 0; i < n; ++i)
 +      {
 +              string s = argv(i);
 +              string r = cvar_string(strcat("g_weaponreplace_", s));
 +              if(r == "")
 +                      out = strcat(out, " ", s);
 +              else if(r != "0")
 +                      out = strcat(out, " ", r);
 +      }
 +      return substring(out, 1, -1);
 +}
 +
 +void weapon_defaultspawnfunc(float wpn)
 +{
 +      entity e;
 +      float t;
 +      var .float ammofield;
 +      string s;
 +      entity oldself;
 +      float i, j;
 +      float f;
 +
 +      if(self.classname != "droppedweapon" && self.classname != "replacedweapon")
 +      {
 +              e = get_weaponinfo(wpn);
 +
 +              if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
 +              {
 +                      objerror("Attempted to spawn a mutator-blocked weapon rejected");
 +                      startitem_failed = TRUE;
 +                      return;
 +              }
 +
 +              s = W_Apply_Weaponreplace(e.netname);
 +              ret_string = s;
 +              other = e;
 +              MUTATOR_CALLHOOK(SetWeaponreplace);
 +              s = ret_string;
 +              if(s == "")
 +              {
 +                      remove(self);
 +                      startitem_failed = TRUE;
 +                      return;
 +              }
 +              t = tokenize_console(s);
 +              if(t >= 2)
 +              {
 +                      self.team = --internalteam;
 +                      oldself = self;
 +                      for(i = 1; i < t; ++i)
 +                      {
 +                              s = argv(i);
 +                              for(j = WEP_FIRST; j <= WEP_LAST; ++j)
 +                              {
 +                                      e = get_weaponinfo(j);
 +                                      if(e.netname == s)
 +                                      {
 +                                              self = spawn();
 +                                              copyentity(oldself, self);
 +                                              self.classname = "replacedweapon";
 +                                              weapon_defaultspawnfunc(j);
 +                                              break;
 +                                      }
 +                              }
 +                              if(j > WEP_LAST)
 +                              {
 +                                      print("The weapon replace list for ", oldself.classname, " contains an unknown weapon ", s, ". Skipped.\n");
 +                              }
 +                      }
 +                      self = oldself;
 +              }
 +              if(t >= 1) // always the case!
 +              {
 +                      s = argv(0);
 +                      wpn = 0;
 +                      for(j = WEP_FIRST; j <= WEP_LAST; ++j)
 +                      {
 +                              e = get_weaponinfo(j);
 +                              if(e.netname == s)
 +                              {
 +                                      wpn = j;
 +                                      break;
 +                              }
 +                      }
 +                      if(j > WEP_LAST)
 +                      {
 +                              print("The weapon replace list for ", self.classname, " contains an unknown weapon ", s, ". Skipped.\n");
 +                      }
 +              }
 +              if(wpn == 0)
 +              {
 +                      remove(self);
 +                      startitem_failed = TRUE;
 +                      return;
 +              }
 +      }
 +
 +      e = get_weaponinfo(wpn);
 +
 +      if(!self.respawntime)
 +      {
-       if(WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS))
++              if(e.weapons & WEPSET_SUPERWEAPONS)
 +              {
 +                      self.respawntime = g_pickup_respawntime_superweapon;
 +                      self.respawntimejitter = g_pickup_respawntimejitter_superweapon;
 +              }
 +              else
 +              {
 +                      self.respawntime = g_pickup_respawntime_weapon;
 +                      self.respawntimejitter = g_pickup_respawntimejitter_weapon;
 +              }
 +      }
 +
-       if(WEPSET_CONTAINS_ANY_EA(e, WEPBIT_SUPERWEAPONS))
++      if(e.weapons & WEPSET_SUPERWEAPONS)
 +              if(!self.superweapons_finished)
 +                      self.superweapons_finished = autocvar_g_balance_superweapons_time;
 +
 +      if(e.items)
 +      {
 +              for(i = 0, j = 1; i < 24; ++i, j *= 2)
 +              {
 +                      if(e.items & j)
 +                      {
 +                              ammofield = Item_CounterField(j);
 +                              if(!self.ammofield)
 +                                      self.ammofield = cvar(strcat("g_pickup_", Item_CounterFieldName(j), "_weapon"));
 +                      }
 +              }
 +      }
 +
 +      // pickup anyway
 +      if(g_pickup_weapons_anyway)
 +              self.pickup_anyway = TRUE;
 +
 +      f = FL_WEAPON;
 +
 +      // no weapon-stay on superweapons
++      if(e.weapons & WEPSET_SUPERWEAPONS)
 +              f |= FL_NO_WEAPON_STAY;
 +
 +      // weapon stay isn't supported for teamed weapons
 +      if(self.team)
 +              f |= FL_NO_WEAPON_STAY;
 +
 +      StartItem(e.model, "weapons/weaponpickup.wav", self.respawntime, self.respawntimejitter, e.message, 0, e.weapon, f, weapon_pickupevalfunc, e.bot_pickupbasevalue);
 +      if (self.modelindex) // don't precache if self was removed
 +              WEP_ACTION(e.weapon, WR_INIT);
 +}
index 31c8f89be8b347ec5d0f62ef26f2a8b8c5c2912b,0000000000000000000000000000000000000000..80a97d1e2f4c1921fdcd275a81c7ab4a5b24f701
mode 100644,000000..100644
--- /dev/null
@@@ -1,190 -1,0 +1,190 @@@
-       if(WEPSET_CONTAINS_AW(WEPBIT_SUPERWEAPONS, wpn))
 +void thrown_wep_think()
 +{
 +      self.owner = world;
 +      float timeleft = self.savenextthink - time;
 +      if(timeleft > 1)
 +              SUB_SetFade(self, self.savenextthink - 1, 1);
 +      else if(timeleft > 0)
 +              SUB_SetFade(self, time, timeleft);
 +      else
 +              SUB_VanishOrRemove(self);
 +}
 +
 +// returns amount of ammo used as string, or -1 for failure, or 0 for no ammo count
 +string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo)
 +{
 +      entity oldself, wep;
 +      float wa, thisammo, i, j;
 +      string s;
 +      var .float ammofield;
 +
 +      wep = spawn();
 +
 +      setorigin(wep, org);
 +      wep.classname = "droppedweapon";
 +      wep.velocity = velo;
 +      wep.owner = wep.enemy = own;
 +      wep.flags |= FL_TOSSED;
 +      wep.colormap = own.colormap;
 +
-                               if(WEPSET_CONTAINS_AW(WEPBIT_SUPERWEAPONS, i))
-                                       if(WEPSET_CONTAINS_EW(own, i))
++      if(WepSet_FromWeapon(wpn) & WEPSET_SUPERWEAPONS)
 +      {
 +              if(own.items & IT_UNLIMITED_SUPERWEAPONS)
 +              {
 +                      wep.superweapons_finished = time + autocvar_g_balance_superweapons_time;
 +              }
 +              else
 +              {
 +                      float superweapons = 1;
 +                      for(i = WEP_FIRST; i <= WEP_LAST; ++i)
-       if(WEPSET_CONTAINS_AW(start_weapons, w))
++                              if(WepSet_FromWeapon(i) & WEPSET_SUPERWEAPONS)
++                                      if(own.weapons & WepSet_FromWeapon(i))
 +                                              ++superweapons;
 +                      if(superweapons <= 1)
 +                      {
 +                              wep.superweapons_finished = own.superweapons_finished;
 +                              own.superweapons_finished = 0;
 +                      }
 +                      else
 +                      {
 +                              float timeleft = own.superweapons_finished - time;
 +                              float weptimeleft = timeleft / superweapons;
 +                              wep.superweapons_finished = time + weptimeleft;
 +                              own.superweapons_finished -= weptimeleft;
 +                      }
 +              }
 +      }
 +
 +      wa = W_AmmoItemCode(wpn);
 +      if(wa == 0)
 +      {
 +              oldself = self;
 +              self = wep;
 +              weapon_defaultspawnfunc(wpn);
 +              self = oldself;
 +              if(startitem_failed)
 +                      return string_null;
 +              wep.glowmod = own.weaponentity_glowmod;
 +              wep.think = thrown_wep_think;
 +              wep.savenextthink = wep.nextthink;
 +              wep.nextthink = min(wep.nextthink, time + 0.5);
 +              wep.pickup_anyway = TRUE; // these are ALWAYS pickable
 +              return "";
 +      }
 +      else
 +      {
 +              s = "";
 +              oldself = self;
 +              self = wep;
 +              weapon_defaultspawnfunc(wpn);
 +              self = oldself;
 +              if(startitem_failed)
 +                      return string_null;
 +              if(doreduce && g_weapon_stay == 2)
 +              {
 +                      for(i = 0, j = 1; i < 24; ++i, j *= 2)
 +                      {
 +                              if(wa & j)
 +                              {
 +                                      ammofield = Item_CounterField(j);
 +
 +                                      // if our weapon is loaded, give its load back to the player
 +                                      if(self.(weapon_load[self.weapon]) > 0)
 +                                      {
 +                                              own.ammofield += self.(weapon_load[self.weapon]);
 +                                              self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading
 +                                      }
 +
 +                                      wep.ammofield = 0;
 +                              }
 +                      }
 +              }
 +              else if(doreduce)
 +              {
 +                      for(i = 0, j = 1; i < 24; ++i, j *= 2)
 +                      {
 +                              if(wa & j)
 +                              {
 +                                      ammofield = Item_CounterField(j);
 +
 +                                      // if our weapon is loaded, give its load back to the player
 +                                      if(self.(weapon_load[self.weapon]) > 0)
 +                                      {
 +                                              own.ammofield += self.(weapon_load[self.weapon]);
 +                                              self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading
 +                                      }
 +
 +                                      thisammo = min(own.ammofield, wep.ammofield);
 +                                      wep.ammofield = thisammo;
 +                                      own.ammofield -= thisammo;
 +                                      s = strcat(s, " and ", ftos(thisammo), " ", Item_CounterFieldName(j));
 +                              }
 +                      }
 +                      s = substring(s, 5, -1);
 +              }
 +              wep.glowmod = own.weaponentity_glowmod;
 +              wep.think = thrown_wep_think;
 +              wep.savenextthink = wep.nextthink;
 +              wep.nextthink = min(wep.nextthink, time + 0.5);
 +              wep.pickup_anyway = TRUE; // these are ALWAYS pickable
 +
 +              return s;
 +      }
 +}
 +
 +float W_IsWeaponThrowable(float w)
 +{
 +      float wa;
 +
 +      if (!autocvar_g_pickup_items)
 +              return 0;
 +      if (g_weaponarena)
 +              return 0;
 +      if (g_cts)
 +              return 0;
 +      if (g_nexball && w == WEP_GRENADE_LAUNCHER)
 +              return 0;
 +    if(w == 0)
 +        return 0;
 +      
 +      wa = W_AmmoItemCode(w);
-       if(!WEPSET_CONTAINS_EW(self, w))
++      if(start_weapons & WepSet_FromWeapon(w))
 +      {
 +              // start weapons that take no ammo can't be dropped (this prevents dropping the laser, as long as it continues to use no ammo)
 +              if(start_items & IT_UNLIMITED_WEAPON_AMMO)
 +                      return 0;
 +              if(wa == 0)
 +                      return 0;
 +      }
 +
 +      return 1;
 +}
 +
 +// toss current weapon
 +void W_ThrowWeapon(vector velo, vector delta, float doreduce)
 +{
 +      float w;
 +      string a;
 +
 +      w = self.weapon;
 +      if (w == 0)
 +              return; // just in case
 +      if(MUTATOR_CALLHOOK(ForbidThrowCurrentWeapon))
 +              return;
 +      if(!autocvar_g_weapon_throwable)
 +              return;
 +      if(self.weaponentity.state != WS_READY)
 +              return;
 +      if(!W_IsWeaponThrowable(w))
 +              return;
 +
-       WEPSET_ANDNOT_EW(self, w);
++      if(!(self.weapons & WepSet_FromWeapon(w)))
 +              return;
++      self.weapons &= ~WepSet_FromWeapon(w);
 +
 +      W_SwitchWeapon_Force(self, w_getbestweapon(self));
 +      a = W_ThrowNewWeapon(self, w, doreduce, self.origin + delta, velo);
 +      
 +      if not(a) return;
 +      Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_WEAPON_DROP, a, w);
 +}
index dd215ba14c246c973c3419ff1ac51004d71b3f28,0000000000000000000000000000000000000000..6fcab0dc7782fbe26a8fea326b0b903931c41370
mode 100644,000000..100644
--- /dev/null
@@@ -1,717 -1,0 +1,717 @@@
-                       soundtoat(MSG_ONE, pseudoprojectile, beampos, CH_SHOTS, snd, VOL_BASE * f, ATTN_NONE);
 +// this function calculates w_shotorg and w_shotdir based on the weapon model
 +// offset, trueaim and antilag, and won't put w_shotorg inside a wall.
 +// make sure you call makevectors first (FIXME?)
 +void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector mi, vector ma, float antilag, float recoil, string snd, float chan, float maxdamage, float range)
 +{
 +      float nudge = 1; // added to traceline target and subtracted from result
 +      float oldsolid;
 +      vector vecs, dv;
 +      oldsolid = ent.dphitcontentsmask;
 +      if(ent.weapon == WEP_RIFLE)
 +              ent.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_CORPSE;
 +      else
 +              ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
 +      if(antilag)
 +              WarpZone_traceline_antilag(world, ent.origin + ent.view_ofs, ent.origin + ent.view_ofs + s_forward * range, MOVE_NORMAL, ent, ANTILAG_LATENCY(ent));
 +              // passing world, because we do NOT want it to touch dphitcontentsmask
 +      else
 +              WarpZone_TraceLine(ent.origin + ent.view_ofs, ent.origin + ent.view_ofs + s_forward * range, MOVE_NOMONSTERS, ent);
 +      ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
 +
 +      vector vf, vr, vu;
 +      vf = v_forward;
 +      vr = v_right;
 +      vu = v_up;
 +      w_shotend = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); // warpzone support
 +      v_forward = vf;
 +      v_right = vr;
 +      v_up = vu;
 +
 +      // un-adjust trueaim if shotend is too close
 +      if(vlen(w_shotend - (ent.origin + ent.view_ofs)) < autocvar_g_trueaim_minrange)
 +              w_shotend = ent.origin + ent.view_ofs + s_forward * autocvar_g_trueaim_minrange;
 +
 +      // track max damage
 +      if(accuracy_canbegooddamage(ent))
 +              accuracy_add(ent, ent.weapon, maxdamage, 0);
 +
 +      W_HitPlotAnalysis(ent, v_forward, v_right, v_up);
 +
 +      if(ent.weaponentity.movedir_x > 0)
 +              vecs = ent.weaponentity.movedir;
 +      else
 +              vecs = '0 0 0';
 +
 +      dv = v_right * -vecs_y + v_up * vecs_z;
 +      w_shotorg = ent.origin + ent.view_ofs + dv;
 +
 +      // now move the shotorg forward as much as requested if possible
 +      if(antilag)
 +      {
 +              if(ent.antilag_debug)
 +                      tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, ent, ent.antilag_debug);
 +              else
 +                      tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, ent, ANTILAG_LATENCY(ent));
 +      }
 +      else
 +              tracebox(w_shotorg, mi, ma, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, ent);
 +      w_shotorg = trace_endpos - v_forward * nudge;
 +      // calculate the shotdir from the chosen shotorg
 +      w_shotdir = normalize(w_shotend - w_shotorg);
 +
 +      if (antilag)
 +      if (!ent.cvar_cl_noantilag)
 +      {
 +              if (autocvar_g_antilag == 1) // switch to "ghost" if not hitting original
 +              {
 +                      traceline(w_shotorg, w_shotorg + w_shotdir * range, MOVE_NORMAL, ent);
 +                      if (!trace_ent.takedamage)
 +                      {
 +                              traceline_antilag_force (ent, w_shotorg, w_shotorg + w_shotdir * range, MOVE_NORMAL, ent, ANTILAG_LATENCY(ent));
 +                              if (trace_ent.takedamage && IS_PLAYER(trace_ent))
 +                              {
 +                                      entity e;
 +                                      e = trace_ent;
 +                                      traceline(w_shotorg, e.origin, MOVE_NORMAL, ent);
 +                                      if(trace_ent == e)
 +                                              w_shotdir = normalize(trace_ent.origin - w_shotorg);
 +                              }
 +                      }
 +              }
 +              else if(autocvar_g_antilag == 3) // client side hitscan
 +              {
 +                      // this part MUST use prydon cursor
 +                      if (ent.cursor_trace_ent)                 // client was aiming at someone
 +                      if (ent.cursor_trace_ent != ent)         // just to make sure
 +                      if (ent.cursor_trace_ent.takedamage)      // and that person is killable
 +                      if (IS_PLAYER(ent.cursor_trace_ent)) // and actually a player
 +                      {
 +                              // verify that the shot would miss without antilag
 +                              // (avoids an issue where guns would always shoot at their origin)
 +                              traceline(w_shotorg, w_shotorg + w_shotdir * range, MOVE_NORMAL, ent);
 +                              if (!trace_ent.takedamage)
 +                              {
 +                                      // verify that the shot would hit if altered
 +                                      traceline(w_shotorg, ent.cursor_trace_ent.origin, MOVE_NORMAL, ent);
 +                                      if (trace_ent == ent.cursor_trace_ent)
 +                                              w_shotdir = normalize(ent.cursor_trace_ent.origin - w_shotorg);
 +                                      else
 +                                              print("antilag fail\n");
 +                              }
 +                      }
 +              }
 +      }
 +
 +      ent.dphitcontentsmask = oldsolid; // restore solid type (generally SOLID_SLIDEBOX)
 +
 +      if (!autocvar_g_norecoil)
 +              ent.punchangle_x = recoil * -1;
 +
 +      if (snd != "")
 +      {
 +              sound (ent, chan, snd, VOL_BASE, ATTN_NORM);
 +              W_PlayStrengthSound(ent);
 +      }
 +
 +      // nudge w_shotend so a trace to w_shotend hits
 +      w_shotend = w_shotend + normalize(w_shotend - w_shotorg) * nudge;
 +}
 +
 +vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity, float forceAbsolute)
 +{
 +      vector mdirection;
 +      float mspeed;
 +      vector outvelocity;
 +
 +      mvelocity = mvelocity * g_weaponspeedfactor;
 +
 +      mdirection = normalize(mvelocity);
 +      mspeed = vlen(mvelocity);
 +
 +      outvelocity = get_shotvelocity(pvelocity, mdirection, mspeed, (forceAbsolute ? 0 : autocvar_g_projectiles_newton_style), autocvar_g_projectiles_newton_style_2_minfactor, autocvar_g_projectiles_newton_style_2_maxfactor);
 +
 +      return outvelocity;
 +}
 +
 +#if 0
 +float mspercallsum;
 +float mspercallsstyle;
 +float mspercallcount;
 +#endif
 +void W_SetupProjectileVelocityEx(entity missile, vector dir, vector upDir, float pSpeed, float pUpSpeed, float pZSpeed, float spread, float forceAbsolute)
 +{
 +      if(missile.owner == world)
 +              error("Unowned missile");
 +
 +      dir = dir + upDir * (pUpSpeed / pSpeed);
 +      dir_z += pZSpeed / pSpeed;
 +      pSpeed *= vlen(dir);
 +      dir = normalize(dir);
 +
 +#if 0
 +      if(autocvar_g_projectiles_spread_style != mspercallsstyle)
 +      {
 +              mspercallsum = mspercallcount = 0;
 +              mspercallsstyle = autocvar_g_projectiles_spread_style;
 +      }
 +      mspercallsum -= gettime(GETTIME_HIRES);
 +#endif
 +      dir = W_CalculateSpread(dir, spread, g_weaponspreadfactor, autocvar_g_projectiles_spread_style);
 +#if 0
 +      mspercallsum += gettime(GETTIME_HIRES);
 +      mspercallcount += 1;
 +      print("avg: ", ftos(mspercallcount / mspercallsum), " per sec\n");
 +#endif
 +
 +      missile.velocity = W_CalculateProjectileVelocity(missile.owner.velocity, pSpeed * dir, forceAbsolute);
 +}
 +
 +void W_SetupProjectileVelocity(entity missile, float pSpeed, float spread) // WEAPONTODO
 +{
 +      W_SetupProjectileVelocityEx(missile, w_shotdir, v_up, pSpeed, 0, 0, spread, FALSE);
 +}
 +
 +
 +// ====================
 +//  Ballistics Tracing
 +// ====================
 +
 +void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, float deathtype)
 +{
 +      vector hitloc, force, endpoint, dir;
 +      entity ent, endent;
 +      float endq3surfaceflags;
 +      float totaldmg;
 +      entity o;
 +
 +      float length;
 +      vector beampos;
 +      string snd;
 +      entity pseudoprojectile;
 +      float f, ffs;
 +
 +      pseudoprojectile = world;
 +
 +      railgun_start = start;
 +      railgun_end = end;
 +
 +      dir = normalize(end - start);
 +      length = vlen(end - start);
 +      force = dir * bforce;
 +
 +      // go a little bit into the wall because we need to hit this wall later
 +      end = end + dir;
 +
 +      totaldmg = 0;
 +
 +      // trace multiple times until we hit a wall, each obstacle will be made
 +      // non-solid so we can hit the next, while doing this we spawn effects and
 +      // note down which entities were hit so we can damage them later
 +      o = self;
 +      while (1)
 +      {
 +              if(self.antilag_debug)
 +                      WarpZone_traceline_antilag (self, start, end, FALSE, o, self.antilag_debug);
 +              else
 +                      WarpZone_traceline_antilag (self, start, end, FALSE, o, ANTILAG_LATENCY(self));
 +              if(o && WarpZone_trace_firstzone)
 +              {
 +                      o = world;
 +                      continue;
 +              }
 +
 +              if(trace_ent.solid == SOLID_BSP || trace_ent.solid == SOLID_SLIDEBOX)
 +                      Damage_DamageInfo(trace_endpos, bdamage, 0, 0, force, deathtype, trace_ent.species, self);
 +
 +              // if it is world we can't hurt it so stop now
 +              if (trace_ent == world || trace_fraction == 1)
 +                      break;
 +
 +              // make the entity non-solid so we can hit the next one
 +              trace_ent.railgunhit = TRUE;
 +              trace_ent.railgunhitloc = end;
 +              trace_ent.railgunhitsolidbackup = trace_ent.solid;
 +              trace_ent.railgundistance = vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos) - start);
 +              trace_ent.railgunforce = WarpZone_TransformVelocity(WarpZone_trace_transform, force);
 +
 +              // stop if this is a wall
 +              if (trace_ent.solid == SOLID_BSP)
 +                      break;
 +
 +              // make the entity non-solid
 +              trace_ent.solid = SOLID_NOT;
 +      }
 +
 +      endpoint = trace_endpos;
 +      endent = trace_ent;
 +      endq3surfaceflags = trace_dphitq3surfaceflags;
 +
 +      // find all the entities the railgun hit and restore their solid state
 +      ent = findfloat(world, railgunhit, TRUE);
 +      while (ent)
 +      {
 +              // restore their solid type
 +              ent.solid = ent.railgunhitsolidbackup;
 +              ent = findfloat(ent, railgunhit, TRUE);
 +      }
 +
 +      // spawn a temporary explosion entity for RadiusDamage calls
 +      //explosion = spawn();
 +
 +      // Find all non-hit players the beam passed close by
 +      if(deathtype == WEP_MINSTANEX || deathtype == WEP_NEX)
 +      {
 +              FOR_EACH_REALCLIENT(msg_entity) if(msg_entity != self) if(!msg_entity.railgunhit) if not(IS_SPEC(msg_entity) && msg_entity.enemy == self) // we use realclient, so spectators can hear the whoosh too
 +              {
 +                      // nearest point on the beam
 +                      beampos = start + dir * bound(0, (msg_entity.origin - start) * dir, length);
 +
 +                      f = bound(0, 1 - vlen(beampos - msg_entity.origin) / 512, 1);
 +                      if(f <= 0)
 +                              continue;
 +
 +                      snd = strcat("weapons/nexwhoosh", ftos(floor(random() * 3) + 1), ".wav");
 +
 +                      if(!pseudoprojectile)
 +                              pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume
-       self.flags &~= FL_ONGROUND;
++                      soundtoat(MSG_ONE, pseudoprojectile, beampos, CH_SHOTS, snd, VOL_BASE * f, ATTEN_NONE);
 +              }
 +
 +              if(pseudoprojectile)
 +                      remove(pseudoprojectile);
 +      }
 +
 +      // find all the entities the railgun hit and hurt them
 +      ent = findfloat(world, railgunhit, TRUE);
 +      while (ent)
 +      {
 +              // get the details we need to call the damage function
 +              hitloc = ent.railgunhitloc;
 +
 +              f = ExponentialFalloff(mindist, maxdist, halflifedist, ent.railgundistance);
 +              ffs = ExponentialFalloff(mindist, maxdist, forcehalflifedist, ent.railgundistance);
 +
 +              if(accuracy_isgooddamage(self.realowner, ent))
 +                      totaldmg += bdamage * f;
 +
 +              // apply the damage
 +              if (ent.takedamage)
 +                      Damage (ent, self, self, bdamage * f, deathtype, hitloc, ent.railgunforce * ffs);
 +
 +              // create a small explosion to throw gibs around (if applicable)
 +              //setorigin (explosion, hitloc);
 +              //RadiusDamage (explosion, self, 10, 0, 50, world, world, 300, deathtype);
 +
 +              ent.railgunhitloc = '0 0 0';
 +              ent.railgunhitsolidbackup = SOLID_NOT;
 +              ent.railgunhit = FALSE;
 +              ent.railgundistance = 0;
 +
 +              // advance to the next entity
 +              ent = findfloat(ent, railgunhit, TRUE);
 +      }
 +
 +      // calculate hits and fired shots for hitscan
 +      accuracy_add(self, self.weapon, 0, min(bdamage, totaldmg));
 +
 +      trace_endpos = endpoint;
 +      trace_ent = endent;
 +      trace_dphitq3surfaceflags = endq3surfaceflags;
 +}
 +
 +void W_BallisticBullet_Hit (void)
 +{
 +      float f, q, g;
 +
 +      f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
 +      q = 1 + self.dmg_edge / self.dmg;
 +
 +      if(other.solid == SOLID_BSP || other.solid == SOLID_SLIDEBOX)
 +              Damage_DamageInfo(self.origin, self.dmg * f, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * f, self.projectiledeathtype, other.species, self);
 +
 +      if(other && other != self.enemy)
 +      {
 +              endzcurveparticles();
 +
 +              yoda = 0;
 +              railgun_start = self.origin - 2 * frametime * self.velocity;
 +              railgun_end = self.origin + 2 * frametime * self.velocity;
 +              g = accuracy_isgooddamage(self.realowner, other);
 +              Damage(other, self, self.realowner, self.dmg * f, self.projectiledeathtype, self.origin, self.dmg_force * normalize(self.velocity) * f);
 +
 +              /*if(yoda && (time > (self.last_yoda + 5)))
 +              {
 +                      Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA);
 +                      self.last_yoda = time; 
 +              }*/
 +
 +              // calculate hits for ballistic weapons
 +              if(g)
 +              {
 +                      // do not exceed 100%
 +                      q = min(self.dmg * q, self.dmg_total + f * self.dmg) - self.dmg_total;
 +                      self.dmg_total += f * self.dmg;
 +                      accuracy_add(self.realowner, self.realowner.weapon, 0, q);
 +              }
 +      }
 +
 +      self.enemy = other; // don't hit the same player twice with the same bullet
 +}
 +
 +void W_BallisticBullet_LeaveSolid_think()
 +{
 +      setorigin(self, self.W_BallisticBullet_LeaveSolid_origin);
 +      self.velocity = self.W_BallisticBullet_LeaveSolid_velocity;
 +
 +      self.think = self.W_BallisticBullet_LeaveSolid_think_save;
 +      self.nextthink = max(time, self.W_BallisticBullet_LeaveSolid_nextthink_save);
 +      self.W_BallisticBullet_LeaveSolid_think_save = func_null;
 +
++      self.flags &= ~FL_ONGROUND;
 +
 +      if(self.enemy.solid == SOLID_BSP)
 +      {
 +              float f;
 +              f = pow(bound(0, vlen(self.velocity) / vlen(self.oldvelocity), 1), 2); // energy multiplier
 +              Damage_DamageInfo(self.origin, 0, 0, 0, max(1, self.dmg_force) * normalize(self.velocity) * -f, self.projectiledeathtype, 0, self);
 +      }
 +
 +      UpdateCSQCProjectile(self);
 +}
 +
 +float W_BallisticBullet_LeaveSolid(float eff)
 +{
 +      // move the entity along its velocity until it's out of solid, then let it resume
 +      vector vel = self.velocity;
 +      float dt, dst, velfactor, v0, vs;
 +      float maxdist;
 +      float E0_m, Es_m;
 +      float constant = self.dmg_radius * (other.ballistics_density ? other.ballistics_density : 1);
 +
 +      // outside the world? forget it
 +      if(self.origin_x > world.maxs_x || self.origin_y > world.maxs_y || self.origin_z > world.maxs_z || self.origin_x < world.mins_x || self.origin_y < world.mins_y || self.origin_z < world.mins_z)
 +              return 0;
 +
 +      // special case for zero density and zero bullet constant: 
 +
 +      if(self.dmg_radius == 0)
 +      {
 +              if(other.ballistics_density < 0)
 +                      constant = 0; // infinite travel distance
 +              else
 +                      return 0; // no penetration
 +      }
 +      else
 +      {
 +              if(other.ballistics_density < 0)
 +                      constant = 0; // infinite travel distance
 +              else if(other.ballistics_density == 0)
 +                      constant = self.dmg_radius;
 +              else
 +                      constant = self.dmg_radius * other.ballistics_density;
 +      }
 +
 +      // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
 +      v0 = vlen(vel);
 +
 +      E0_m = 0.5 * v0 * v0;
 +
 +      if(constant)
 +      {
 +              maxdist = E0_m / constant;
 +              // maxdist = 0.5 * v0 * v0 / constant
 +              // dprint("max dist = ", ftos(maxdist), "\n");
 +
 +              if(maxdist <= autocvar_g_ballistics_mindistance)
 +                      return 0;
 +      }
 +      else
 +      {
 +              maxdist = vlen(other.maxs - other.mins) + 1; // any distance, as long as we leave the entity
 +      }
 +
 +      traceline_inverted (self.origin, self.origin + normalize(vel) * maxdist, MOVE_NORMAL, self, TRUE);
 +      if(trace_fraction == 1) // 1: we never got out of solid
 +              return 0;
 +
 +      self.W_BallisticBullet_LeaveSolid_origin = trace_endpos;
 +
 +      dst = max(autocvar_g_ballistics_mindistance, vlen(trace_endpos - self.origin));
 +      // E(s) = E0 - constant * s, constant = area of bullet circle * material constant / mass
 +      Es_m = E0_m - constant * dst;
 +      if(Es_m <= 0)
 +      {
 +              // roundoff errors got us
 +              return 0;
 +      }
 +      vs = sqrt(2 * Es_m);
 +      velfactor = vs / v0;
 +
 +      dt = dst / (0.5 * (v0 + vs));
 +      // this is not correct, but the differential equations have no analytic
 +      // solution - and these times are very small anyway
 +      //print("dt = ", ftos(dt), "\n");
 +
 +      self.W_BallisticBullet_LeaveSolid_think_save = self.think;
 +      self.W_BallisticBullet_LeaveSolid_nextthink_save = self.nextthink;
 +      self.think = W_BallisticBullet_LeaveSolid_think;
 +      self.nextthink = time + dt;
 +
 +      vel = vel * velfactor;
 +
 +      self.velocity = '0 0 0';
 +      self.flags |= FL_ONGROUND; // prevent moving
 +      self.W_BallisticBullet_LeaveSolid_velocity = vel;
 +
 +      if(eff >= 0)
 +              if(vlen(trace_endpos - self.origin) > 4)
 +              {
 +                      endzcurveparticles();
 +                      trailparticles(self, eff, self.origin, trace_endpos);
 +              }
 +
 +      return 1;
 +}
 +
 +void W_BallisticBullet_Touch (void)
 +{
 +      //float density;
 +
 +      if(self.think == W_BallisticBullet_LeaveSolid_think) // skip this!
 +              return;
 +
 +      PROJECTILE_TOUCH;
 +      W_BallisticBullet_Hit ();
 +
 +      if(self.dmg_radius < 0) // these NEVER penetrate solid
 +      {
 +              remove(self);
 +              return;
 +      }
 +
 +      // if we hit "weapclip", bail out
 +      //
 +      // rationale of this check:
 +      //
 +      // any shader that is solid, nodraw AND trans is meant to clip weapon
 +      // shots and players, but has no other effect!
 +      //
 +      // if it is not trans, it is caulk and should not have this side effect
 +      //
 +      // matching shaders:
 +      //   common/weapclip (intended)
 +      //   common/noimpact (is supposed to eat projectiles, but is erased farther above)
 +      if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW)
 +      if not(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)
 +      if not(trace_dphitcontents & DPCONTENTS_OPAQUE)
 +      {
 +              remove(self);
 +              return;
 +      }
 +
 +      // go through solid!
 +      if(!W_BallisticBullet_LeaveSolid(-1))
 +      {
 +              remove(self);
 +              return;
 +      }
 +
 +      self.projectiledeathtype |= HITTYPE_BOUNCE;
 +}
 +
 +void endFireBallisticBullet() // WEAPONTODO
 +{
 +      endzcurveparticles();
 +}
 +
 +void fireBallisticBullet_trace_callback(vector start, vector hit, vector end)
 +{
 +      if(vlen(trace_endpos - fireBallisticBullet_trace_callback_ent.origin) > 16)
 +              zcurveparticles_from_tracetoss(fireBallisticBullet_trace_callback_eff, fireBallisticBullet_trace_callback_ent.origin, trace_endpos, fireBallisticBullet_trace_callback_ent.velocity);
 +      WarpZone_trace_forent = world;
 +      self.owner = world;
 +}
 +
 +void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, float lifetime, float damage, float force, float dtype, float tracereffects, float gravityfactor, float bulletconstant)
 +{
 +      float lag, dt, savetime; //, density;
 +      entity pl, oldself;
 +      float antilagging;
 +
 +      antilagging = (autocvar_g_antilag_bullets && (pSpeed >= autocvar_g_antilag_bullets));
 +
 +      entity proj;
 +      proj = spawn();
 +      proj.classname = "bullet";
 +      proj.owner = proj.realowner = self;
 +      PROJECTILE_MAKETRIGGER(proj);
 +      if(gravityfactor > 0)
 +      {
 +              proj.movetype = MOVETYPE_TOSS;
 +              proj.gravity = gravityfactor;
 +      }
 +      else
 +              proj.movetype = MOVETYPE_FLY;
 +      proj.think = SUB_Remove;
 +      proj.nextthink = time + lifetime; // min(pLifetime, vlen(world.maxs - world.mins) / pSpeed);
 +      W_SetupProjectileVelocityEx(proj, dir, v_up, pSpeed, 0, 0, spread, antilagging);
 +      proj.angles = vectoangles(proj.velocity);
 +      if(bulletconstant > 0)
 +              proj.dmg_radius = autocvar_g_ballistics_materialconstant / bulletconstant;
 +      else if(bulletconstant == 0)
 +              proj.dmg_radius = 0;
 +      else
 +              proj.dmg_radius = -1;
 +      // so: bulletconstant = bullet mass / area of bullet circle
 +      setorigin(proj, start);
 +      proj.flags = FL_PROJECTILE;
 +
 +      proj.touch = W_BallisticBullet_Touch;
 +      proj.dmg = damage;
 +      proj.dmg_force = force;
 +      proj.projectiledeathtype = dtype;
 +
 +      proj.oldvelocity = proj.velocity;
 +
 +      other = proj; MUTATOR_CALLHOOK(EditProjectile);
 +
 +      if(antilagging)
 +      {
 +              float eff;
 +
 +              if(tracereffects & EF_RED)
 +                      eff = particleeffectnum("tr_rifle");
 +              else if(tracereffects & EF_BLUE)
 +                      eff = particleeffectnum("tr_rifle_weak");
 +              else
 +                      eff = particleeffectnum("tr_bullet");
 +
 +              // NOTE: this may severely throw off weapon balance
 +              lag = ANTILAG_LATENCY(self);
 +              if(lag < 0.001)
 +                      lag = 0;
 +              if not(IS_REAL_CLIENT(self))
 +                      lag = 0;
 +              if(autocvar_g_antilag == 0 || self.cvar_cl_noantilag)
 +                      lag = 0; // only do hitscan, but no antilag
 +
 +              if(lag)
 +                      FOR_EACH_PLAYER(pl)
 +                              if(pl != self)
 +                                      antilag_takeback(pl, time - lag);
 +
 +              oldself = self;
 +              self = proj;
 +
 +              savetime = frametime;
 +              frametime = 0.05;
 +
 +              for(;;)
 +              {
 +                      // DP tracetoss is stupid and always traces in 0.05s
 +                      // ticks. This makes it trace in 0.05*0.125s ticks
 +                      // instead.
 +                      vector v0;
 +                      float g0;
 +                      v0 = self.velocity;
 +                      g0 = self.gravity;
 +                      self.velocity = self.velocity * 0.125;
 +                      self.gravity *= 0.125 * 0.125;
 +                      trace_fraction = 0;
 +                      fireBallisticBullet_trace_callback_ent = self;
 +                      fireBallisticBullet_trace_callback_eff = eff;
 +                      WarpZone_TraceToss_ThroughZone(self, self.owner, world, fireBallisticBullet_trace_callback);
 +                      self.velocity = v0;
 +                      self.gravity = g0;
 +
 +                      if(trace_fraction == 1)
 +                              break;
 +                              // won't hit anything anytime soon (DP's
 +                              // tracetoss does 200 tics of, here,
 +                              // 0.05*0.125s, that is, 1.25 seconds
 +
 +                      other = trace_ent;
 +                      dt = WarpZone_tracetoss_time * 0.125; // this is only approximate!
 +                      setorigin(self, trace_endpos);
 +                      self.velocity = WarpZone_tracetoss_velocity * (1 / 0.125);
 +
 +                      if(!SUB_OwnerCheck())
 +                      {
 +                              if(SUB_NoImpactCheck())
 +                                      break;
 +
 +                              // hit the player
 +                              W_BallisticBullet_Hit();
 +                      }
 +
 +                      if(proj.dmg_radius < 0) // these NEVER penetrate solid
 +                              break;
 +
 +                      // if we hit "weapclip", bail out
 +                      //
 +                      // rationale of this check:
 +                      //
 +                      // any shader that is solid, nodraw AND trans is meant to clip weapon
 +                      // shots and players, but has no other effect!
 +                      //
 +                      // if it is not trans, it is caulk and should not have this side effect
 +                      //
 +                      // matching shaders:
 +                      //   common/weapclip (intended)
 +                      //   common/noimpact (is supposed to eat projectiles, but is erased farther above)
 +                      if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW)
 +                      if not(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)
 +                      if not(trace_dphitcontents & DPCONTENTS_OPAQUE)
 +                              break;
 +
 +                      // go through solid!
 +                      if(!W_BallisticBullet_LeaveSolid((other && (other.solid != SOLID_BSP)) ? eff : -1))
 +                              break;
 +
 +                      W_BallisticBullet_LeaveSolid_think();
 +
 +                      self.projectiledeathtype |= HITTYPE_BOUNCE;
 +              }
 +              frametime = savetime;
 +              self = oldself;
 +
 +              if(lag)
 +                      FOR_EACH_PLAYER(pl)
 +                              if(pl != self)
 +                                      antilag_restore(pl);
 +
 +              remove(proj);
 +
 +              return;
 +      }
 +
 +      if(tracereffects & EF_RED)
 +              CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING_TRACER, TRUE);
 +      else if(tracereffects & EF_BLUE)
 +              CSQCProjectile(proj, TRUE, PROJECTILE_BULLET_GLOWING, TRUE);
 +      else
 +              CSQCProjectile(proj, TRUE, PROJECTILE_BULLET, TRUE);
 +}
 +
 +void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer)
 +{
 +      vector  end;
 +
 +      dir = normalize(dir + randomvec() * spread);
 +      end = start + dir * MAX_SHOT_DISTANCE;
 +      if(self.antilag_debug)
 +              traceline_antilag (self, start, end, FALSE, self, self.antilag_debug);
 +      else
 +              traceline_antilag (self, start, end, FALSE, self, ANTILAG_LATENCY(self));
 +
 +      end = trace_endpos;
 +
 +      if (pointcontents (trace_endpos) != CONTENT_SKY)
 +      {
 +              if not (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
 +                      Damage_DamageInfo(trace_endpos, damage, 0, 0, dir * max(1, force), dtype, trace_ent.species, self);                    
 +
 +              Damage (trace_ent, self, self, damage, dtype, trace_endpos, dir * force);
 +      }
 +      trace_endpos = end;
 +}
index 0d9082703fee51ec1a32136cfda53315235ccdb8,0000000000000000000000000000000000000000..2586c2ee49a2c23dcdcae491efdb8911fb7f9511
mode 100644,000000..100644
--- /dev/null
@@@ -1,943 -1,0 +1,943 @@@
- float WFRAME_DONTCHANGE = -1;
- float WFRAME_FIRE1 = 0;
- float WFRAME_FIRE2 = 1;
- float WFRAME_IDLE = 2;
- float WFRAME_RELOAD = 3;
 +/*
 +===========================================================================
 +
 +  CLIENT WEAPONSYSTEM CODE
 +  Bring back W_Weaponframe
 +
 +===========================================================================
 +*/
 +
 +.float weapon_frametime;
 +
 +float W_WeaponRateFactor()
 +{
 +      float t;
 +      t = 1.0 / g_weaponratefactor;
 +
 +      return t;
 +}
 +
 +// VorteX: static frame globals
-       self.effects &~= EF_LOWPRECISION;
-       self.effects &~= EF_FULLBRIGHT; // can mask team color, so get rid of it
-       self.effects &~= EF_TELEPORT_BIT;
-       self.effects &~= EF_RESTARTANIM_BIT;
++const float WFRAME_DONTCHANGE = -1;
++const float WFRAME_FIRE1 = 0;
++const float WFRAME_FIRE2 = 1;
++const float WFRAME_IDLE = 2;
++const float WFRAME_RELOAD = 3;
 +.float wframe;
 +
 +void(float fr, float t, void() func) weapon_thinkf;
 +
 +float CL_Weaponentity_CustomizeEntityForClient()
 +{
 +      self.viewmodelforclient = self.owner;
 +      if(IS_SPEC(other))
 +              if(other.enemy == self.owner)
 +                      self.viewmodelforclient = other;
 +      return TRUE;
 +}
 +
 +/*
 + * supported formats:
 + *
 + * 1. simple animated model, muzzle flash handling on h_ model:
 + *    h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation
 + *      tags:
 + *        shot = muzzle end (shot origin, also used for muzzle flashes)
 + *        shell = casings ejection point (must be on the right hand side of the gun)
 + *        weapon = attachment for v_tuba.md3
 + *    v_tuba.md3 - first and third person model
 + *    g_tuba.md3 - pickup model
 + *
 + * 2. simple animated model, muzzle flash handling on v_ model:
 + *    h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation
 + *      tags:
 + *        weapon = attachment for v_tuba.md3
 + *    v_tuba.md3 - first and third person model
 + *      tags:
 + *        shot = muzzle end (shot origin, also used for muzzle flashes)
 + *        shell = casings ejection point (must be on the right hand side of the gun)
 + *    g_tuba.md3 - pickup model
 + *
 + * 3. fully animated model, muzzle flash handling on h_ model:
 + *    h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model
 + *      tags:
 + *        shot = muzzle end (shot origin, also used for muzzle flashes)
 + *        shell = casings ejection point (must be on the right hand side of the gun)
 + *        handle = corresponding to the origin of v_tuba.md3 (used for muzzle flashes)
 + *    v_tuba.md3 - third person model
 + *    g_tuba.md3 - pickup model
 + *
 + * 4. fully animated model, muzzle flash handling on v_ model:
 + *    h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model
 + *      tags:
 + *        shot = muzzle end (shot origin)
 + *        shell = casings ejection point (must be on the right hand side of the gun)
 + *    v_tuba.md3 - third person model
 + *      tags:
 + *        shot = muzzle end (for muzzle flashes)
 + *    g_tuba.md3 - pickup model
 + */
 +
 +// writes:
 +//   self.origin, self.angles
 +//   self.weaponentity
 +//   self.movedir, self.view_ofs
 +//   attachment stuff
 +//   anim stuff
 +// to free:
 +//   call again with ""
 +//   remove the ent
 +void CL_WeaponEntity_SetModel(string name)
 +{
 +      float v_shot_idx;
 +      if (name != "")
 +      {
 +              // if there is a child entity, hide it until we're sure we use it
 +              if (self.weaponentity)
 +                      self.weaponentity.model = "";
 +              setmodel(self, strcat("models/weapons/v_", name, ".md3")); // precision set below
 +              v_shot_idx = gettagindex(self, "shot"); // used later
 +              if(!v_shot_idx)
 +                      v_shot_idx = gettagindex(self, "tag_shot");
 +
 +              setmodel(self, strcat("models/weapons/h_", name, ".iqm")); // precision set below
 +              // preset some defaults that work great for renamed zym files (which don't need an animinfo)
 +              self.anim_fire1  = animfixfps(self, '0 1 0.01', '0 0 0');
 +              self.anim_fire2  = animfixfps(self, '1 1 0.01', '0 0 0');
 +              self.anim_idle   = animfixfps(self, '2 1 0.01', '0 0 0');
 +              self.anim_reload = animfixfps(self, '3 1 0.01', '0 0 0');
 +
 +              // if we have a "weapon" tag, let's attach the v_ model to it ("invisible hand" style model)
 +              // if we don't, this is a "real" animated model
 +              if(gettagindex(self, "weapon"))
 +              {
 +                      if (!self.weaponentity)
 +                              self.weaponentity = spawn();
 +                      setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter
 +                      setattachment(self.weaponentity, self, "weapon");
 +              }
 +              else if(gettagindex(self, "tag_weapon"))
 +              {
 +                      if (!self.weaponentity)
 +                              self.weaponentity = spawn();
 +                      setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter
 +                      setattachment(self.weaponentity, self, "tag_weapon");
 +              }
 +              else
 +              {
 +                      if(self.weaponentity)
 +                              remove(self.weaponentity);
 +                      self.weaponentity = world;
 +              }
 +
 +              setorigin(self,'0 0 0');
 +              self.angles = '0 0 0';
 +              self.frame = 0;
 +              self.viewmodelforclient = world;
 +
 +              float idx;
 +
 +              if(v_shot_idx) // v_ model attached to invisible h_ model
 +              {
 +                      self.movedir = gettaginfo(self.weaponentity, v_shot_idx);
 +              }
 +              else
 +              {
 +                      idx = gettagindex(self, "shot");
 +                      if(!idx)
 +                              idx = gettagindex(self, "tag_shot");
 +                      if(idx)
 +                              self.movedir = gettaginfo(self, idx);
 +                      else
 +                      {
 +                              print("WARNING: weapon model ", self.model, " does not support the 'shot' tag, will display shots TOTALLY wrong\n");
 +                              self.movedir = '0 0 0';
 +                      }
 +              }
 +
 +              if(self.weaponentity) // v_ model attached to invisible h_ model
 +              {
 +                      idx = gettagindex(self.weaponentity, "shell");
 +                      if(!idx)
 +                              idx = gettagindex(self.weaponentity, "tag_shell");
 +                      if(idx)
 +                              self.spawnorigin = gettaginfo(self.weaponentity, idx);
 +              }
 +              else
 +                      idx = 0;
 +              if(!idx)
 +              {
 +                      idx = gettagindex(self, "shell");
 +                      if(!idx)
 +                              idx = gettagindex(self, "tag_shell");
 +                      if(idx)
 +                              self.spawnorigin = gettaginfo(self, idx);
 +                      else
 +                      {
 +                              print("WARNING: weapon model ", self.model, " does not support the 'shell' tag, will display casings wrong\n");
 +                              self.spawnorigin = self.movedir;
 +                      }
 +              }
 +
 +              if(v_shot_idx)
 +              {
 +                      self.oldorigin = '0 0 0'; // use regular attachment
 +              }
 +              else
 +              {
 +                      if(self.weaponentity)
 +                      {
 +                              idx = gettagindex(self, "weapon");
 +                              if(!idx)
 +                                      idx = gettagindex(self, "tag_weapon");
 +                      }
 +                      else
 +                      {
 +                              idx = gettagindex(self, "handle");
 +                              if(!idx)
 +                                      idx = gettagindex(self, "tag_handle");
 +                      }
 +                      if(idx)
 +                      {
 +                              self.oldorigin = self.movedir - gettaginfo(self, idx);
 +                      }
 +                      else
 +                      {
 +                              print("WARNING: weapon model ", self.model, " does not support the 'handle' tag and neither does the v_ model support the 'shot' tag, will display muzzle flashes TOTALLY wrong\n");
 +                              self.oldorigin = '0 0 0'; // there is no way to recover from this
 +                      }
 +              }
 +
 +              self.viewmodelforclient = self.owner;
 +      }
 +      else
 +      {
 +              self.model = "";
 +              if(self.weaponentity)
 +                      remove(self.weaponentity);
 +              self.weaponentity = world;
 +              self.movedir = '0 0 0';
 +              self.spawnorigin = '0 0 0';
 +              self.oldorigin = '0 0 0';
 +              self.anim_fire1  = '0 1 0.01';
 +              self.anim_fire2  = '0 1 0.01';
 +              self.anim_idle   = '0 1 0.01';
 +              self.anim_reload = '0 1 0.01';
 +      }
 +
 +      self.view_ofs = '0 0 0';
 +
 +      if(self.movedir_x >= 0)
 +      {
 +              vector v0;
 +              v0 = self.movedir;
 +              self.movedir = shotorg_adjust(v0, FALSE, FALSE);
 +              self.view_ofs = shotorg_adjust(v0, FALSE, TRUE) - v0;
 +      }
 +      self.owner.stat_shotorg = compressShotOrigin(self.movedir);
 +      self.movedir = decompressShotOrigin(self.owner.stat_shotorg); // make them match perfectly
 +
 +      self.spawnorigin += self.view_ofs; // offset the casings origin by the same amount
 +
 +      // check if an instant weapon switch occurred
 +      setorigin(self, self.view_ofs);
 +      // reset animstate now
 +      self.wframe = WFRAME_IDLE;
 +      setanim(self, self.anim_idle, TRUE, FALSE, TRUE);
 +}
 +
 +vector CL_Weapon_GetShotOrg(float wpn)
 +{
 +      entity wi, oldself;
 +      vector ret;
 +      wi = get_weaponinfo(wpn);
 +      oldself = self;
 +      self = spawn();
 +      CL_WeaponEntity_SetModel(wi.mdl);
 +      ret = self.movedir;
 +      CL_WeaponEntity_SetModel("");
 +      remove(self);
 +      self = oldself;
 +      return ret;
 +}
 +
 +void CL_Weaponentity_Think()
 +{
 +      float tb;
 +      self.nextthink = time;
 +      if (intermission_running)
 +              self.frame = self.anim_idle_x;
 +      if (self.owner.weaponentity != self)
 +      {
 +              if (self.weaponentity)
 +                      remove(self.weaponentity);
 +              remove(self);
 +              return;
 +      }
 +      if (self.owner.deadflag != DEAD_NO)
 +      {
 +              self.model = "";
 +              if (self.weaponentity)
 +                      self.weaponentity.model = "";
 +              return;
 +      }
 +      if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
 +      {
 +              self.weaponname = self.owner.weaponname;
 +              self.dmg = self.owner.modelindex;
 +              self.deadflag = self.owner.deadflag;
 +
 +              CL_WeaponEntity_SetModel(self.owner.weaponname);
 +      }
 +
 +      tb = (self.effects & (EF_TELEPORT_BIT | EF_RESTARTANIM_BIT));
 +      self.effects = self.owner.effects & EFMASK_CHEAP;
-                       sound (self, CH_WEAPON_A, "weapons/dryfire.wav", VOL_BASE, ATTN_NORM);
++      self.effects &= ~EF_LOWPRECISION;
++      self.effects &= ~EF_FULLBRIGHT; // can mask team color, so get rid of it
++      self.effects &= ~EF_TELEPORT_BIT;
++      self.effects &= ~EF_RESTARTANIM_BIT;
 +      self.effects |= tb;
 +
 +      if(self.owner.alpha == default_player_alpha)
 +              self.alpha = default_weapon_alpha;
 +      else if(self.owner.alpha != 0)
 +              self.alpha = self.owner.alpha;
 +      else
 +              self.alpha = 1;
 +
 +      self.glowmod = self.owner.weaponentity_glowmod;
 +      self.colormap = self.owner.colormap;
 +      if (self.weaponentity)
 +      {
 +              self.weaponentity.effects = self.effects;
 +              self.weaponentity.alpha = self.alpha;
 +              self.weaponentity.colormap = self.colormap;
 +              self.weaponentity.glowmod = self.glowmod;
 +      }
 +
 +      self.angles = '0 0 0';
 +      
 +      float f = (self.owner.weapon_nextthink - time);
 +      if (self.state == WS_RAISE && !intermission_running)
 +      {
 +              entity newwep = get_weaponinfo(self.owner.switchweapon);
 +              f = f * g_weaponratefactor / max(f, newwep.switchdelay_raise);
 +              //print(sprintf("CL_Weaponentity_Think(): cvar: %s, value: %f, nextthink: %f\n", sprintf("g_balance_%s_switchdelay_raise", newwep.netname), cvar(sprintf("g_balance_%s_switchdelay_raise", newwep.netname)), (self.owner.weapon_nextthink - time)));
 +              self.angles_x = -90 * f * f;
 +      }
 +      else if (self.state == WS_DROP && !intermission_running)
 +      {
 +              entity oldwep = get_weaponinfo(self.owner.weapon);
 +              f = 1 - f * g_weaponratefactor / max(f, oldwep.switchdelay_drop);
 +              //print(sprintf("CL_Weaponentity_Think(): cvar: %s, value: %f, nextthink: %f\n", sprintf("g_balance_%s_switchdelay_drop", oldwep.netname), cvar(sprintf("g_balance_%s_switchdelay_drop", oldwep.netname)), (self.owner.weapon_nextthink - time)));
 +              self.angles_x = -90 * f * f;
 +      }
 +      else if (self.state == WS_CLEAR)
 +      {
 +              f = 1;
 +              self.angles_x = -90 * f * f;
 +      }
 +}
 +
 +void CL_ExteriorWeaponentity_Think()
 +{
 +      float tag_found;
 +      self.nextthink = time;
 +      if (self.owner.exteriorweaponentity != self)
 +      {
 +              remove(self);
 +              return;
 +      }
 +      if (self.owner.deadflag != DEAD_NO)
 +      {
 +              self.model = "";
 +              return;
 +      }
 +      if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
 +      {
 +              self.weaponname = self.owner.weaponname;
 +              self.dmg = self.owner.modelindex;
 +              self.deadflag = self.owner.deadflag;
 +              if (self.owner.weaponname != "")
 +                      setmodel(self, strcat("models/weapons/v_", self.owner.weaponname, ".md3")); // precision set below
 +              else
 +                      self.model = "";
 +
 +              if((tag_found = gettagindex(self.owner, "tag_weapon")))
 +              {
 +                      self.tag_index = tag_found;
 +                      self.tag_entity = self.owner;
 +              }
 +              else
 +                      setattachment(self, self.owner, "bip01 r hand");
 +      }
 +      self.effects = self.owner.effects;
 +      self.effects |= EF_LOWPRECISION;
 +      self.effects = self.effects & EFMASK_CHEAP; // eat performance
 +      if(self.owner.alpha == default_player_alpha)
 +              self.alpha = default_weapon_alpha;
 +      else if(self.owner.alpha != 0)
 +              self.alpha = self.owner.alpha;
 +      else
 +              self.alpha = 1;
 +
 +      self.glowmod = self.owner.weaponentity_glowmod;
 +      self.colormap = self.owner.colormap;
 +
 +      CSQCMODEL_AUTOUPDATE();
 +}
 +
 +// spawning weaponentity for client
 +void CL_SpawnWeaponentity()
 +{
 +      self.weaponentity = spawn();
 +      self.weaponentity.classname = "weaponentity";
 +      self.weaponentity.solid = SOLID_NOT;
 +      self.weaponentity.owner = self;
 +      setmodel(self.weaponentity, ""); // precision set when changed
 +      setorigin(self.weaponentity, '0 0 0');
 +      self.weaponentity.angles = '0 0 0';
 +      self.weaponentity.viewmodelforclient = self;
 +      self.weaponentity.flags = 0;
 +      self.weaponentity.think = CL_Weaponentity_Think;
 +      self.weaponentity.customizeentityforclient = CL_Weaponentity_CustomizeEntityForClient;
 +      self.weaponentity.nextthink = time;
 +
 +      self.exteriorweaponentity = spawn();
 +      self.exteriorweaponentity.classname = "exteriorweaponentity";
 +      self.exteriorweaponentity.solid = SOLID_NOT;
 +      self.exteriorweaponentity.exteriorweaponentity = self.exteriorweaponentity;
 +      self.exteriorweaponentity.owner = self;
 +      setorigin(self.exteriorweaponentity, '0 0 0');
 +      self.exteriorweaponentity.angles = '0 0 0';
 +      self.exteriorweaponentity.think = CL_ExteriorWeaponentity_Think;
 +      self.exteriorweaponentity.nextthink = time;
 +
 +      {
 +              entity oldself = self;
 +              self = self.exteriorweaponentity;
 +              CSQCMODEL_AUTOINIT();
 +              self = oldself;
 +      }
 +}
 +
 +// Weapon subs
 +void w_clear()
 +{
 +      if (self.weapon != -1)
 +      {
 +              self.weapon = 0;
 +              self.switchingweapon = 0;
 +      }
 +      if (self.weaponentity)
 +      {
 +              self.weaponentity.state = WS_CLEAR;
 +              self.weaponentity.effects = 0;
 +      }
 +}
 +
 +void w_ready()
 +{
 +      if (self.weaponentity)
 +              self.weaponentity.state = WS_READY;
 +      weapon_thinkf(WFRAME_IDLE, 1000000, w_ready);
 +}
 +
 +.float prevdryfire;
 +.float prevwarntime;
 +float weapon_prepareattack_checkammo(float secondary)
 +{
 +      if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
 +      if (!WEP_ACTION(self.weapon, WR_CHECKAMMO1 + secondary))
 +      {
 +              // always keep the Mine Layer if we placed mines, so that we can detonate them
 +              entity mine;
 +              if(self.weapon == WEP_MINE_LAYER)
 +              for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
 +                      return FALSE;
 +
 +              if(self.weapon == self.switchweapon && time - self.prevdryfire > 1) // only play once BEFORE starting to switch weapons
 +              {
-               self.items &~= IT_AMMO;
++                      sound (self, CH_WEAPON_A, "weapons/dryfire.wav", VOL_BASE, ATTEN_NORM);
 +                      self.prevdryfire = time;
 +              }
 +
 +              if(WEP_ACTION(self.weapon, WR_CHECKAMMO2 - secondary)) // check if the other firing mode has enough ammo
 +              {
 +                      if(time - self.prevwarntime > 1)
 +                      {
 +                              Send_Notification(
 +                                      NOTIF_ONE,
 +                                      self,
 +                                      MSG_MULTI,
 +                                      ITEM_WEAPON_PRIMORSEC,
 +                                      self.weapon,
 +                                      secondary,
 +                                      (1 - secondary)
 +                              );
 +                      }
 +                      self.prevwarntime = time;
 +              }
 +              else // this weapon is totally unable to fire, switch to another one
 +              {
 +                      W_SwitchToOtherWeapon(self);
 +              }
 +              
 +              return FALSE;
 +      }
 +      return TRUE;
 +}
 +.float race_penalty;
 +float weapon_prepareattack_check(float secondary, float attacktime)
 +{
 +      if(!weapon_prepareattack_checkammo(secondary))
 +              return FALSE;
 +
 +      //if sv_ready_restart_after_countdown is set, don't allow the player to shoot
 +      //if all players readied up and the countdown is running
 +      if(time < game_starttime || time < self.race_penalty) {
 +              return FALSE;
 +      }
 +
 +      if (timeout_status == TIMEOUT_ACTIVE) //don't allow the player to shoot while game is paused
 +              return FALSE;
 +
 +      // do not even think about shooting if switching
 +      if(self.switchweapon != self.weapon)
 +              return FALSE;
 +
 +      if(attacktime >= 0)
 +      {
 +              // don't fire if previous attack is not finished
 +              if (ATTACK_FINISHED(self) > time + self.weapon_frametime * 0.5)
 +                      return FALSE;
 +              // don't fire while changing weapon
 +              if (self.weaponentity.state != WS_READY)
 +                      return FALSE;
 +      }
 +
 +      return TRUE;
 +}
 +float weapon_prepareattack_do(float secondary, float attacktime)
 +{
 +      self.weaponentity.state = WS_INUSE;
 +
 +      self.spawnshieldtime = min(self.spawnshieldtime, time); // kill spawn shield when you fire
 +
 +      // if the weapon hasn't been firing continuously, reset the timer
 +      if(attacktime >= 0)
 +      {
 +              if (ATTACK_FINISHED(self) < time - self.weapon_frametime * 1.5)
 +              {
 +                      ATTACK_FINISHED(self) = time;
 +                      //dprint("resetting attack finished to ", ftos(time), "\n");
 +              }
 +              ATTACK_FINISHED(self) = ATTACK_FINISHED(self) + attacktime * W_WeaponRateFactor();
 +      }
 +      self.bulletcounter += 1;
 +      //dprint("attack finished ", ftos(ATTACK_FINISHED(self)), "\n");
 +      return TRUE;
 +}
 +float weapon_prepareattack(float secondary, float attacktime)
 +{
 +      if(weapon_prepareattack_check(secondary, attacktime))
 +      {
 +              weapon_prepareattack_do(secondary, attacktime);
 +              return TRUE;
 +      }
 +      else
 +              return FALSE;
 +}
 +
 +void weapon_thinkf(float fr, float t, void() func)
 +{
 +      vector a;
 +      vector of, or, ou;
 +      float restartanim;
 +
 +      if(fr == WFRAME_DONTCHANGE)
 +      {
 +              fr = self.weaponentity.wframe;
 +              restartanim = FALSE;
 +      }
 +      else if (fr == WFRAME_IDLE)
 +              restartanim = FALSE;
 +      else
 +              restartanim = TRUE;
 +
 +      of = v_forward;
 +      or = v_right;
 +      ou = v_up;
 +
 +      if (self.weaponentity)
 +      {
 +              self.weaponentity.wframe = fr;
 +              a = '0 0 0';
 +              if (fr == WFRAME_IDLE)
 +                      a = self.weaponentity.anim_idle;
 +              else if (fr == WFRAME_FIRE1)
 +                      a = self.weaponentity.anim_fire1;
 +              else if (fr == WFRAME_FIRE2)
 +                      a = self.weaponentity.anim_fire2;
 +              else // if (fr == WFRAME_RELOAD)
 +                      a = self.weaponentity.anim_reload;
 +              a_z *= g_weaponratefactor;
 +              setanim(self.weaponentity, a, restartanim == FALSE, restartanim, restartanim);
 +      }
 +
 +      v_forward = of;
 +      v_right = or;
 +      v_up = ou;
 +
 +      if(self.weapon_think == w_ready && func != w_ready && self.weaponentity.state == WS_RAISE)
 +      {
 +              backtrace("Tried to override initial weapon think function - should this really happen?");
 +      }
 +
 +      t *= W_WeaponRateFactor();
 +
 +      // VorteX: haste can be added here
 +      if (self.weapon_think == w_ready)
 +      {
 +              self.weapon_nextthink = time;
 +              //dprint("started firing at ", ftos(time), "\n");
 +      }
 +      if (self.weapon_nextthink < time - self.weapon_frametime * 1.5 || self.weapon_nextthink > time + self.weapon_frametime * 1.5)
 +      {
 +              self.weapon_nextthink = time;
 +              //dprint("reset weapon animation timer at ", ftos(time), "\n");
 +      }
 +      self.weapon_nextthink = self.weapon_nextthink + t;
 +      self.weapon_think = func;
 +      //dprint("next ", ftos(self.weapon_nextthink), "\n");
 +
 +      if((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t)
 +      {
 +              if(self.weapon == WEP_SHOTGUN && fr == WFRAME_FIRE2)
 +                      animdecide_setaction(self, ANIMACTION_MELEE, restartanim);
 +              else
 +                      animdecide_setaction(self, ANIMACTION_SHOOT, restartanim);
 +      }
 +      else
 +      {
 +              if(self.anim_upper_action == ANIMACTION_SHOOT || self.anim_upper_action == ANIMACTION_MELEE)
 +                      self.anim_upper_action = 0;
 +      }
 +}
 +
 +float forbidWeaponUse()
 +{
 +      if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown)
 +              return 1;
 +      if(round_handler_IsActive() && !round_handler_IsRoundStarted())
 +              return 1;
 +      if(self.player_blocked)
 +              return 1;
 +      if(self.freezetag_frozen)
 +              return 1;
 +      return 0;
 +}
 +
 +void W_WeaponFrame()
 +{
 +      vector fo, ri, up;
 +
 +      if (frametime)
 +              self.weapon_frametime = frametime;
 +
 +      if (!self.weaponentity || self.health < 1)
 +              return; // Dead player can't use weapons and injure impulse commands
 +
 +      if(forbidWeaponUse())
 +      if(self.weaponentity.state != WS_CLEAR)
 +      {
 +              w_ready();
 +              return;
 +      }
 +
 +      if(!self.switchweapon)
 +      {
 +              self.weapon = 0;
 +              self.switchingweapon = 0;
 +              self.weaponentity.state = WS_CLEAR;
 +              self.weaponname = "";
-                       self.items &~= IT_AMMO;
++              self.items &= ~IT_AMMO;
 +              return;
 +      }
 +
 +      makevectors(self.v_angle);
 +      fo = v_forward; // save them in case the weapon think functions change it
 +      ri = v_right;
 +      up = v_up;
 +
 +      // Change weapon
 +      if (self.weapon != self.switchweapon)
 +      {
 +              if (self.weaponentity.state == WS_CLEAR)
 +              {
 +                      // end switching!
 +                      self.switchingweapon = self.switchweapon;
 +                      entity newwep = get_weaponinfo(self.switchweapon);
 +
-               if(w && !WEPSET_CONTAINS_EW(self, w))
++                      self.items &= ~IT_AMMO;
 +                      self.items = self.items | (newwep.items & IT_AMMO);
 +
 +                      // the two weapon entities will notice this has changed and update their models
 +                      self.weapon = self.switchweapon;
 +                      self.weaponname = newwep.mdl;
 +                      self.bulletcounter = 0; // WEAPONTODO
 +                      WEP_ACTION(self.switchweapon, WR_SETUP);
 +                      self.weaponentity.state = WS_RAISE;
 +
 +                      // set our clip load to the load of the weapon we switched to, if it's reloadable
 +                      if(newwep.spawnflags & WEP_FLAG_RELOADABLE && newwep.reloading_ammo) // prevent accessing undefined cvars
 +                      {
 +                              self.clip_load = self.(weapon_load[self.switchweapon]);
 +                              self.clip_size = newwep.reloading_ammo;
 +                      }
 +                      else
 +                              self.clip_load = self.clip_size = 0;
 +
 +                      // VorteX: add player model weapon select frame here
 +                      // setcustomframe(PlayerWeaponRaise);
 +                      weapon_thinkf(WFRAME_IDLE, newwep.switchdelay_raise, w_ready);
 +                      //print(sprintf("W_WeaponFrame(): cvar: %s, value: %f\n", sprintf("g_balance_%s_switchdelay_raise", newwep.netname), cvar(sprintf("g_balance_%s_switchdelay_raise", newwep.netname))));
 +              }
 +              else if (self.weaponentity.state == WS_DROP)
 +              {
 +                      // in dropping phase we can switch at any time
 +                      self.switchingweapon = self.switchweapon;
 +              }
 +              else if (self.weaponentity.state == WS_READY)
 +              {
 +                      // start switching!
 +                      self.switchingweapon = self.switchweapon;
 +
 +                      entity oldwep = get_weaponinfo(self.weapon);
 +                      
 +#ifndef INDEPENDENT_ATTACK_FINISHED
 +                      if(ATTACK_FINISHED(self) <= time + self.weapon_frametime * 0.5)
 +                      {
 +#endif
 +                      sound (self, CH_WEAPON_SINGLE, "weapons/weapon_switch.wav", VOL_BASE, ATTN_NORM);
 +                      self.weaponentity.state = WS_DROP;
 +                      // set up weapon switch think in the future, and start drop anim
 +                      weapon_thinkf(WFRAME_DONTCHANGE, oldwep.switchdelay_drop, w_clear);
 +                      //print(sprintf("W_WeaponFrame(): cvar: %s, value: %f\n", sprintf("g_balance_%s_switchdelay_drop", oldwep.netname), cvar(sprintf("g_balance_%s_switchdelay_drop", oldwep.netname))));
 +#ifndef INDEPENDENT_ATTACK_FINISHED
 +                      }
 +#endif
 +              }
 +      }
 +
 +      // LordHavoc: network timing test code
 +      //if (self.button0)
 +      //      print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(self)), " >= ", ftos(self.weapon_nextthink), "\n");
 +
 +      float w;
 +      w = self.weapon;
 +
 +      // call the think code which may fire the weapon
 +      // and do so multiple times to resolve framerate dependency issues if the
 +      // server framerate is very low and the weapon fire rate very high
 +      float c;
 +      c = 0;
 +      while (c < W_TICSPERFRAME)
 +      {
 +              c = c + 1;
-       sound(self, CH_WEAPON_SINGLE, self.reload_sound, VOL_BASE, ATTN_NORM);
++              if(w && !(self.weapons & WepSet_FromWeapon(w)))
 +              {
 +                      if(self.weapon == self.switchweapon)
 +                              W_SwitchWeapon_Force(self, w_getbestweapon(self));
 +                      w = 0;
 +              }
 +
 +              v_forward = fo;
 +              v_right = ri;
 +              v_up = up;
 +
 +              if(w)
 +                      WEP_ACTION(self.weapon, WR_THINK);
 +              else
 +                      WEP_ACTION(self.weapon, WR_GONETHINK);
 +
 +              if (time + self.weapon_frametime * 0.5 >= self.weapon_nextthink)
 +              {
 +                      if(self.weapon_think)
 +                      {
 +                              v_forward = fo;
 +                              v_right = ri;
 +                              v_up = up;
 +                              self.weapon_think();
 +                      }
 +                      else
 +                              bprint("\{1}^1ERROR: undefined weapon think function for ", self.netname, "\n");
 +              }
 +      }
 +
 +#if 0
 +      if (self.items & IT_CELLS)
 +              self.currentammo = self.ammo_cells;
 +      else if (self.items & IT_ROCKETS)
 +              self.currentammo = self.ammo_rockets;
 +      else if (self.items & IT_NAILS)
 +              self.currentammo = self.ammo_nails;
 +      else if (self.items & IT_SHELLS)
 +              self.currentammo = self.ammo_shells;
 +      else
 +              self.currentammo = 1;
 +#endif
 +}
 +
 +void W_AttachToShotorg(entity flash, vector offset)
 +{
 +      entity xflash;
 +      flash.owner = self;
 +      flash.angles_z = random() * 360;
 +
 +      if(gettagindex(self.weaponentity, "shot"))
 +              setattachment(flash, self.weaponentity, "shot");
 +      else
 +              setattachment(flash, self.weaponentity, "tag_shot");
 +      setorigin(flash, offset);
 +
 +      xflash = spawn();
 +      copyentity(flash, xflash);
 +
 +      flash.viewmodelforclient = self;
 +
 +      if(self.weaponentity.oldorigin_x > 0)
 +      {
 +              setattachment(xflash, self.exteriorweaponentity, "");
 +              setorigin(xflash, self.weaponentity.oldorigin + offset);
 +      }
 +      else
 +      {
 +              if(gettagindex(self.exteriorweaponentity, "shot"))
 +                      setattachment(xflash, self.exteriorweaponentity, "shot");
 +              else
 +                      setattachment(xflash, self.exteriorweaponentity, "tag_shot");
 +              setorigin(xflash, offset);
 +      }
 +}
 +
 +void W_DecreaseAmmo(.float ammo_type, float ammo_use, float ammo_reload) // WEAPONTODO: why does this have ammo_type?
 +{
 +      if((self.items & IT_UNLIMITED_WEAPON_AMMO) && !ammo_reload)
 +              return;
 +
 +      // if this weapon is reloadable, decrease its load. Else decrease the player's ammo
 +      if(ammo_reload)
 +      {
 +              self.clip_load -= ammo_use;
 +              self.(weapon_load[self.weapon]) = self.clip_load;
 +      }
 +      else
 +              self.(self.current_ammo) -= ammo_use;
 +}
 +
 +// weapon reloading code
 +
 +.float reload_ammo_amount, reload_ammo_min, reload_time;
 +.float reload_complain;
 +.string reload_sound;
 +
 +void W_ReloadedAndReady()
 +{
 +      // finish the reloading process, and do the ammo transfer
 +
 +      self.clip_load = self.old_clip_load; // restore the ammo counter, in case we still had ammo in the weapon before reloading
 +
 +      // if the gun uses no ammo, max out weapon load, else decrease ammo as we increase weapon load
 +      if(!self.reload_ammo_min || self.items & IT_UNLIMITED_WEAPON_AMMO)
 +              self.clip_load = self.reload_ammo_amount;
 +      else
 +      {
 +              while(self.clip_load < self.reload_ammo_amount && self.(self.current_ammo)) // make sure we don't add more ammo than we have
 +              {
 +                      self.clip_load += 1;
 +                      self.(self.current_ammo) -= 1;
 +              }
 +      }
 +      self.(weapon_load[self.weapon]) = self.clip_load;
 +
 +      // do not set ATTACK_FINISHED in reload code any more. This causes annoying delays if eg: You start reloading a weapon,
 +      // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there,
 +      // so your weapon is disabled for a few seconds without reason
 +
 +      //ATTACK_FINISHED(self) -= self.reload_time - 1;
 +
 +      w_ready();
 +}
 +
 +void W_Reload(float sent_ammo_min, string sent_sound)
 +{
 +      // set global values to work with
 +      entity e;
 +      e = get_weaponinfo(self.weapon);
 +
 +      self.reload_ammo_min = sent_ammo_min;
 +      self.reload_ammo_amount = e.reloading_ammo;;
 +      self.reload_time = e.reloading_time;
 +      self.reload_sound = sent_sound;
 +
 +      // don't reload weapons that don't have the RELOADABLE flag
 +      if not(e.spawnflags & WEP_FLAG_RELOADABLE)
 +      {
 +              dprint("Warning: Attempted to reload a weapon that does not have the WEP_FLAG_RELOADABLE flag. Fix your code!\n");
 +              return;
 +      }
 +
 +      // return if reloading is disabled for this weapon
 +      if(!self.reload_ammo_amount)
 +              return;
 +
 +      // our weapon is fully loaded, no need to reload
 +      if (self.clip_load >= self.reload_ammo_amount)
 +              return;
 +
 +      // no ammo, so nothing to load
 +      if(!self.(self.current_ammo) && self.reload_ammo_min)
 +      if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
 +      {
 +              if(IS_REAL_CLIENT(self) && self.reload_complain < time)
 +              {
 +                      play2(self, "weapons/unavailable.wav");
 +                      sprint(self, strcat("You don't have enough ammo to reload the ^2", W_Name(self.weapon), "\n"));
 +                      self.reload_complain = time + 1;
 +              }
 +              // switch away if the amount of ammo is not enough to keep using this weapon
 +              if not(WEP_ACTION(self.weapon, WR_CHECKAMMO1) + WEP_ACTION(self.weapon, WR_CHECKAMMO2))
 +              {
 +                      self.clip_load = -1; // reload later
 +                      W_SwitchToOtherWeapon(self);
 +              }
 +              return;
 +      }
 +
 +      if (self.weaponentity)
 +      {
 +              if (self.weaponentity.wframe == WFRAME_RELOAD)
 +                      return;
 +
 +              // allow switching away while reloading, but this will cause a new reload!
 +              self.weaponentity.state = WS_READY;
 +      }
 +
 +      // now begin the reloading process
 +
++      sound(self, CH_WEAPON_SINGLE, self.reload_sound, VOL_BASE, ATTEN_NORM);
 +
 +      // do not set ATTACK_FINISHED in reload code any more. This causes annoying delays if eg: You start reloading a weapon,
 +      // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there,
 +      // so your weapon is disabled for a few seconds without reason
 +
 +      //ATTACK_FINISHED(self) = max(time, ATTACK_FINISHED(self)) + self.reload_time + 1;
 +
 +      weapon_thinkf(WFRAME_RELOAD, self.reload_time, W_ReloadedAndReady);
 +
 +      if(self.clip_load < 0)
 +              self.clip_load = 0;
 +      self.old_clip_load = self.clip_load;
 +      self.clip_load = self.(weapon_load[self.weapon]) = -1;
 +}