]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge remote-tracking branch 'origin/master' into samual/weapons
authorSamual Lenks <samual@xonotic.org>
Fri, 6 Dec 2013 21:13:30 +0000 (16:13 -0500)
committerSamual Lenks <samual@xonotic.org>
Fri, 6 Dec 2013 21:13:30 +0000 (16:13 -0500)
Conflicts:
balanceXonotic.cfg
qcsrc/common/weapons/w_seeker.qc
qcsrc/server/cl_weapons.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/mutators/mutator_minstagib.qc
qcsrc/server/tturrets/units/unit_machinegun.qc
qcsrc/server/tturrets/units/unit_walker.qc
qcsrc/server/w_hagar.qc
qcsrc/server/w_hlac.qc
qcsrc/server/w_hook.qc
qcsrc/server/w_laser.qc
qcsrc/server/w_minstanex.qc
qcsrc/server/w_nex.qc
qcsrc/server/w_rifle.qc
qcsrc/server/w_shotgun.qc
qcsrc/server/w_uzi.qc
qcsrc/server/weapons/tracing.qc
qcsrc/server/weapons/weaponsystem.qc

139 files changed:
balance25.cfg [deleted file]
balanceFruitieX.cfg [deleted file]
balanceXDF.cfg [deleted file]
balanceXPM.cfg [deleted file]
balanceXonotic.cfg
effectinfo.txt
models/weapons/g_lightning.md3 [new file with mode: 0644]
models/weapons/h_lightning.iqm [new file with mode: 0644]
models/weapons/h_lightning.iqm.framegroups [new file with mode: 0644]
models/weapons/v_lightning.md3 [new file with mode: 0644]
models/weapons/w_lightning.zym [new file with mode: 0644]
oldbalance25.cfg [new file with mode: 0644]
oldbalanceFruitieX.cfg [new file with mode: 0644]
oldbalanceXDF.cfg [new file with mode: 0644]
oldbalanceXPM.cfg [new file with mode: 0644]
qcsrc/client/Defs.qc
qcsrc/client/Main.qc
qcsrc/client/View.qc
qcsrc/client/damage.qc
qcsrc/client/effects.qc
qcsrc/client/hook.qc
qcsrc/client/hud.qc
qcsrc/client/miscfunctions.qc
qcsrc/client/particles.qc
qcsrc/client/progs.src
qcsrc/client/projectile.qc [deleted file]
qcsrc/client/projectile.qh [deleted file]
qcsrc/client/weapons/projectile.qc [new file with mode: 0644]
qcsrc/client/weapons/projectile.qh [new file with mode: 0644]
qcsrc/common/command/generic.qc
qcsrc/common/constants.qh
qcsrc/common/explosion_equation.qc [deleted file]
qcsrc/common/explosion_equation.qh [deleted file]
qcsrc/common/items.qc [deleted file]
qcsrc/common/items.qh [deleted file]
qcsrc/common/util.qc
qcsrc/common/util.qh
qcsrc/common/weapons/all.qh [new file with mode: 0644]
qcsrc/common/weapons/calculations.qc [new file with mode: 0644]
qcsrc/common/weapons/calculations.qh [new file with mode: 0644]
qcsrc/common/weapons/config.qc [new file with mode: 0644]
qcsrc/common/weapons/config.qh [new file with mode: 0644]
qcsrc/common/weapons/w_arc.qc [new file with mode: 0644]
qcsrc/common/weapons/w_blaster.qc [new file with mode: 0644]
qcsrc/common/weapons/w_crylink.qc [new file with mode: 0644]
qcsrc/common/weapons/w_devastator.qc [new file with mode: 0644]
qcsrc/common/weapons/w_electro.qc [new file with mode: 0644]
qcsrc/common/weapons/w_fireball.qc [new file with mode: 0644]
qcsrc/common/weapons/w_hagar.qc [new file with mode: 0644]
qcsrc/common/weapons/w_hlac.qc [new file with mode: 0644]
qcsrc/common/weapons/w_hook.qc [new file with mode: 0644]
qcsrc/common/weapons/w_machinegun.qc [new file with mode: 0644]
qcsrc/common/weapons/w_minelayer.qc [new file with mode: 0644]
qcsrc/common/weapons/w_minstanex.qc [new file with mode: 0644]
qcsrc/common/weapons/w_mortar.qc [new file with mode: 0644]
qcsrc/common/weapons/w_nex.qc [new file with mode: 0644]
qcsrc/common/weapons/w_porto.qc [new file with mode: 0644]
qcsrc/common/weapons/w_rifle.qc [new file with mode: 0644]
qcsrc/common/weapons/w_seeker.qc [new file with mode: 0644]
qcsrc/common/weapons/w_shockwave.qc [new file with mode: 0644]
qcsrc/common/weapons/w_tuba.qc [new file with mode: 0644]
qcsrc/common/weapons/weapons.qc [new file with mode: 0644]
qcsrc/common/weapons/weapons.qh [new file with mode: 0644]
qcsrc/menu/progs.src
qcsrc/server/accuracy.qc [deleted file]
qcsrc/server/accuracy.qh [deleted file]
qcsrc/server/autocvars.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/cl_weaponsystem.qc [deleted file]
qcsrc/server/csqceffects.qc
qcsrc/server/csqcprojectile.qc [deleted file]
qcsrc/server/csqcprojectile.qh [deleted file]
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/progs.src
qcsrc/server/t_items.qc
qcsrc/server/t_items.qh [new file with mode: 0644]
qcsrc/server/t_quake3.qc
qcsrc/server/tturrets/system/system_main.qc
qcsrc/server/tturrets/units/unit_flac.qc
qcsrc/server/tturrets/units/unit_machinegun.qc
qcsrc/server/tturrets/units/unit_walker.qc
qcsrc/server/vehicles/bumblebee.qc
qcsrc/server/vehicles/racer.qc
qcsrc/server/vehicles/raptor.qc
qcsrc/server/vehicles/spiderbot.qc
qcsrc/server/vehicles/vehicles.qc
qcsrc/server/w_all.qc [deleted file]
qcsrc/server/w_common.qc [deleted file]
qcsrc/server/w_electro.qc [deleted file]
qcsrc/server/w_electro.qh [deleted file]
qcsrc/server/w_fireball.qc [deleted file]
qcsrc/server/w_hagar.qc [deleted file]
qcsrc/server/w_hlac.qc [deleted file]
qcsrc/server/w_hook.qc [deleted file]
qcsrc/server/w_laser.qc [deleted file]
qcsrc/server/w_laser.qh [deleted file]
qcsrc/server/w_minstanex.qc [deleted file]
qcsrc/server/w_nex.qc [deleted file]
qcsrc/server/w_porto.qc [deleted file]
qcsrc/server/w_rifle.qc [deleted file]
qcsrc/server/w_seeker.qc [deleted file]
qcsrc/server/w_shotgun.qc [deleted file]
qcsrc/server/w_tuba.qc [deleted file]
qcsrc/server/w_uzi.qc [deleted file]
qcsrc/server/weapons/accuracy.qc [new file with mode: 0644]
qcsrc/server/weapons/accuracy.qh [new file with mode: 0644]
qcsrc/server/weapons/common.qc [new file with mode: 0644]
qcsrc/server/weapons/common.qh [new file with mode: 0644]
qcsrc/server/weapons/csqcprojectile.qc [new file with mode: 0644]
qcsrc/server/weapons/csqcprojectile.qh [new file with mode: 0644]
qcsrc/server/weapons/hitplot.qc [new file with mode: 0644]
qcsrc/server/weapons/hitplot.qh [new file with mode: 0644]
qcsrc/server/weapons/selection.qc [new file with mode: 0644]
qcsrc/server/weapons/selection.qh [new file with mode: 0644]
qcsrc/server/weapons/spawning.qc [new file with mode: 0644]
qcsrc/server/weapons/spawning.qh [new file with mode: 0644]
qcsrc/server/weapons/throwing.qc [new file with mode: 0644]
qcsrc/server/weapons/throwing.qh [new file with mode: 0644]
qcsrc/server/weapons/tracing.qc [new file with mode: 0644]
qcsrc/server/weapons/tracing.qh [new file with mode: 0644]
qcsrc/server/weapons/weaponsystem.qc [new file with mode: 0644]
qcsrc/server/weapons/weaponsystem.qh [new file with mode: 0644]
sound/weapons/gauntlet_fire.ogg [deleted file]
sound/weapons/gauntletbeam_fly.ogg [deleted file]

diff --git a/balance25.cfg b/balance25.cfg
deleted file mode 100644 (file)
index 9451d37..0000000
+++ /dev/null
@@ -1,707 +0,0 @@
-g_mod_balance Nexuiz25
-
-// {{{ starting gear
-set g_start_weapon_laser -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_shotgun -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_uzi -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_hagar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_balance_health_start 150
-set g_balance_armor_start 0
-set g_start_ammo_shells 40
-set g_start_ammo_nails 0
-set g_start_ammo_rockets 0
-set g_start_ammo_cells 0
-set g_start_ammo_fuel 0
-set g_warmup_start_health 250 "starting values when being in warmup-stage"
-set g_warmup_start_armor 100 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_shells 50 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_nails 150 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_rockets 50 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_cells 50 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage"
-set g_lms_start_health 250
-set g_lms_start_armor 100
-set g_lms_start_ammo_shells 50
-set g_lms_start_ammo_nails 150
-set g_lms_start_ammo_rockets 50
-set g_lms_start_ammo_cells 50
-set g_lms_start_ammo_fuel 0
-set g_balance_nix_roundtime 25
-set g_balance_nix_incrtime 1.6
-set g_balance_nix_ammo_shells 15
-set g_balance_nix_ammo_nails 45
-set g_balance_nix_ammo_rockets 15
-set g_balance_nix_ammo_cells 15
-set g_balance_nix_ammo_fuel 0
-set g_balance_nix_ammoincr_shells 2
-set g_balance_nix_ammoincr_nails 6
-set g_balance_nix_ammoincr_rockets 2
-set g_balance_nix_ammoincr_cells 2
-set g_balance_nix_ammoincr_fuel 2
-// }}}
-
-// {{{ pickup items
-set g_pickup_ammo_anyway 0
-set g_pickup_weapons_anyway 0
-set g_pickup_shells 15
-set g_pickup_shells_weapon 15
-set g_pickup_shells_max 999
-set g_pickup_nails 80
-set g_pickup_nails_weapon 80
-set g_pickup_nails_max 999
-set g_pickup_rockets 15
-set g_pickup_rockets_weapon 15
-set g_pickup_rockets_max 999
-set g_pickup_cells 25
-set g_pickup_cells_weapon 25
-set g_pickup_cells_max 999
-set g_pickup_fuel 25
-set g_pickup_fuel_weapon 25
-set g_pickup_fuel_jetpack 50
-set g_pickup_fuel_max 999
-set g_pickup_armorsmall 5
-set g_pickup_armorsmall_max 999
-set g_pickup_armorsmall_anyway 0
-set g_pickup_armormedium 25
-set g_pickup_armormedium_max 999
-set g_pickup_armormedium_anyway 0
-set g_pickup_armorbig 50
-set g_pickup_armorbig_max 999
-set g_pickup_armorbig_anyway 0
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 999
-set g_pickup_armorlarge_anyway 0
-set g_pickup_healthsmall 5
-set g_pickup_healthsmall_max 999
-set g_pickup_healthsmall_anyway 0
-set g_pickup_healthmedium 25
-set g_pickup_healthmedium_max 999
-set g_pickup_healthmedium_anyway 0
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 999
-set g_pickup_healthlarge_anyway 0
-set g_pickup_healthmega 100
-set g_pickup_healthmega_max 999
-set g_pickup_healthmega_anyway 0
-set g_pickup_respawntime_short 15
-set g_pickup_respawntime_medium 20
-set g_pickup_respawntime_long 30
-set g_pickup_respawntime_powerup 120
-set g_pickup_respawntime_weapon 15
-set g_pickup_respawntime_superweapon 120
-set g_pickup_respawntime_ammo 15
-set g_pickup_respawntimejitter_short 0
-set g_pickup_respawntimejitter_medium 0
-set g_pickup_respawntimejitter_long 0
-set g_pickup_respawntimejitter_powerup 10
-set g_pickup_respawntimejitter_weapon 0
-set g_pickup_respawntimejitter_superweapon 10
-set g_pickup_respawntimejitter_ammo 0
-// }}}
-
-// {{{ regen/rot
-set g_balance_health_regen 0.1
-set g_balance_health_regenlinear 0
-set g_balance_pause_health_regen 5
-set g_balance_pause_health_regen_spawn 0
-set g_balance_health_rot 0.1
-set g_balance_health_rotlinear 0
-set g_balance_pause_health_rot 5
-set g_balance_pause_health_rot_spawn 10
-set g_balance_health_regenstable 100
-set g_balance_health_rotstable 100
-set g_balance_health_limit 999
-set g_balance_armor_regen 0
-set g_balance_armor_regenlinear 0
-set g_balance_armor_rot 0.1
-set g_balance_armor_rotlinear 0
-set g_balance_pause_armor_rot 5
-set g_balance_pause_armor_rot_spawn 10
-set g_balance_armor_regenstable 100
-set g_balance_armor_rotstable 100
-set g_balance_armor_limit 999
-set g_balance_armor_blockpercent 0.6
-set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)"
-set g_balance_fuel_regenlinear 0
-set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter
-set g_balance_fuel_rot 0.05
-set g_balance_fuel_rotlinear 0
-set g_balance_pause_fuel_rot 5
-set g_balance_pause_fuel_rot_spawn 10
-set g_balance_fuel_regenstable 50
-set g_balance_fuel_rotstable 100
-set g_balance_fuel_limit 999
-// }}}
-
-// {{{ misc
-set g_balance_selfdamagepercent 0.6
-set g_weaponspeedfactor 1 "weapon projectile speed multiplier"
-set g_weaponratefactor 1 "weapon fire rate multiplier"
-set g_weapondamagefactor 1 "weapon damage multiplier"
-set g_weaponforcefactor 1 "weapon force multiplier"
-set g_weaponspreadfactor 1 "weapon spread multiplier"
-set g_balance_firetransfer_time 0.9
-set g_balance_firetransfer_damage 0.8
-set g_throughfloor_damage 1
-set g_throughfloor_force 1
-set g_projectiles_damage 2
-// possible values:
-// -2: absolutely no damage to projectiles (no exceptions)
-// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines)
-// 0: only damage from contents (lava/slime) or exceptions
-// 1: only self damage or damage from contents or exceptions
-// 2: allow all damage to projectiles normally
-set g_projectiles_keep_owner 0
-set g_projectiles_newton_style 2
-// possible values:
-// 0: absolute velocity projectiles (like Quake)
-// 1: relative velocity projectiles, "Newtonian" (like Tribes 2)
-// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard)
-set g_projectiles_newton_style_2_minfactor 0.7
-set g_projectiles_newton_style_2_maxfactor 5
-set g_projectiles_spread_style 0
-// possible values:
-// 0: forward + solid sphere (like Quake) - varies velocity
-// 1: forward + flattened solid sphere
-// 2: forward + solid circle
-// 3: forward + normal distribution 3D - varies velocity
-// 4: forward + normal distribution on a plane
-// 5: forward + circle with 1-r falloff
-// 6: forward + circle with 1-r^2 falloff
-// 7: forward + circle with (1-r)(2-r) falloff
-set g_balance_falldamage_deadminspeed 150
-set g_balance_falldamage_minspeed 1400
-set g_balance_falldamage_factor 0.15
-set g_balance_falldamage_maxdamage 25
-set g_balance_damagepush_speedfactor 0
-set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage
-set g_balance_contents_drowndelay 10 // time under water before a player begins drowning
-set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning
-set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava
-set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime
-set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime
-set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap"
-// }}}
-
-// {{{ powerups
-set g_balance_powerup_invincible_takedamage 0.2
-set g_balance_powerup_invincible_time 30
-set g_balance_powerup_strength_damage 3
-set g_balance_powerup_strength_force 4
-set g_balance_powerup_strength_time 30
-set g_balance_powerup_strength_selfdamage 1.5
-set g_balance_powerup_strength_selfforce 1.5
-set g_balance_superweapons_time 30
-// }}}
-
-// {{{ jetpack/hook
-set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack"
-set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction"
-set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)"
-set g_jetpack_maxspeed_side 1500 "max speed of the jetpack in xy direction"
-set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction"
-set g_jetpack_fuel 8 "fuel per second for jetpack"
-set g_jetpack_attenuation 2 "jetpack sound attenuation"
-
-set g_grappling_hook_tarzan 2 // 2: can also pull players
-set g_balance_grapplehook_speed_fly 1800
-set g_balance_grapplehook_speed_pull 2000
-set g_balance_grapplehook_force_rubber 2000
-set g_balance_grapplehook_force_rubber_overstretch 1000
-set g_balance_grapplehook_length_min 50
-set g_balance_grapplehook_stretch 50
-set g_balance_grapplehook_airfriction 0.2
-set g_balance_grapplehook_health 130
-set g_balance_grapplehook_damagedbycontents 0
-// }}}
-
-// {{{ weapon properties
-// {{{ laser
-set g_balance_laser_primary_damage 35
-set g_balance_laser_primary_edgedamage 10
-set g_balance_laser_primary_force 400
-set g_balance_laser_primary_radius 70
-set g_balance_laser_primary_speed 9000
-set g_balance_laser_primary_spread 0
-set g_balance_laser_primary_refire 0.7
-set g_balance_laser_primary_animtime 0.3
-set g_balance_laser_primary_lifetime 30
-set g_balance_laser_primary_shotangle 0
-set g_balance_laser_primary_delay 0.05
-set g_balance_laser_primary_gauntlet 0
-set g_balance_laser_primary_force_zscale 1
-set g_balance_laser_primary_force_velocitybias 0
-set g_balance_laser_primary_force_other_scale 1
-set g_balance_laser_secondary 0 // when 1, a secondary laser mode exists
-set g_balance_laser_secondary_damage 35
-set g_balance_laser_secondary_edgedamage 10
-set g_balance_laser_secondary_force 400
-set g_balance_laser_secondary_radius 70
-set g_balance_laser_secondary_speed 9000
-set g_balance_laser_secondary_spread 0
-set g_balance_laser_secondary_refire 0.7
-set g_balance_laser_secondary_animtime 0.3
-set g_balance_laser_secondary_lifetime 30
-set g_balance_laser_secondary_shotangle 0
-set g_balance_laser_secondary_delay 0
-set g_balance_laser_secondary_gauntlet 0
-set g_balance_laser_secondary_force_zscale 1
-set g_balance_laser_secondary_force_velocitybias 0
-set g_balance_laser_secondary_force_other_scale 1
-set g_balance_laser_switchdelay_drop 0.15
-set g_balance_laser_switchdelay_raise 0.15
-set g_balance_laser_reload_ammo 0 //default: 6
-set g_balance_laser_reload_time 2
-// }}}
-// {{{ shotgun
-set g_balance_shotgun_primary_bullets 6
-set g_balance_shotgun_primary_damage 9
-set g_balance_shotgun_primary_force 60
-set g_balance_shotgun_primary_spread 0.07
-set g_balance_shotgun_primary_refire 0.5
-set g_balance_shotgun_primary_animtime 0.2
-set g_balance_shotgun_primary_ammo 1
-set g_balance_shotgun_primary_speed 12000
-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 0
-set g_balance_shotgun_secondary_melee_multihit 1
-set g_balance_shotgun_secondary_damage 115
-set g_balance_shotgun_secondary_force 150
-set g_balance_shotgun_secondary_refire 1.1
-set g_balance_shotgun_secondary_animtime 1
-set g_balance_shotgun_switchdelay_drop 0.15
-set g_balance_shotgun_switchdelay_raise 0.15
-set g_balance_shotgun_reload_ammo 0 //default: 5
-set g_balance_shotgun_reload_time 2
-// }}}
-// {{{ uzi
-set g_balance_uzi_mode 0                               // Activates varible spread for sustained & burst mode secondary
-set g_balance_uzi_spread_min 0.02
-set g_balance_uzi_spread_max 0.6
-set g_balance_uzi_spread_add 0.012
-
-set g_balance_uzi_burst 0                              // # of bullets in a burst (if set to 2 or more)
-set g_balance_uzi_burst_animtime 0.75
-set g_balance_uzi_burst_refire 0.05            // refire between burst bullets
-set g_balance_uzi_burst_refire2 0.75   // refire after burst
-set g_balance_uzi_burst_spread 0.04
-set g_balance_uzi_burst_damage 18
-set g_balance_uzi_burst_force 50
-set g_balance_uzi_burst_ammo 3
-
-set g_balance_uzi_first 1
-set g_balance_uzi_first_damage 30
-set g_balance_uzi_first_force 50
-set g_balance_uzi_first_spread 0.015
-set g_balance_uzi_first_refire 0.2
-set g_balance_uzi_first_ammo 1
-
-set g_balance_uzi_sustained_damage 15
-set g_balance_uzi_sustained_force 27
-set g_balance_uzi_sustained_spread 0.05
-set g_balance_uzi_sustained_refire 0.1
-set g_balance_uzi_sustained_ammo 1
-
-set g_balance_uzi_speed 18000
-set g_balance_uzi_bulletconstant 115 // 13.1qu
-
-set g_balance_uzi_switchdelay_drop 0.15
-set g_balance_uzi_switchdelay_raise 0.15
-
-set g_balance_uzi_reload_ammo 0 //default: 30
-set g_balance_uzi_reload_time 2
-// }}}
-// {{{ mortar
-set g_balance_grenadelauncher_primary_type 0
-set g_balance_grenadelauncher_primary_damage 70
-set g_balance_grenadelauncher_primary_edgedamage 38
-set g_balance_grenadelauncher_primary_force 400
-set g_balance_grenadelauncher_primary_radius 140
-set g_balance_grenadelauncher_primary_speed 2000
-set g_balance_grenadelauncher_primary_speed_up 200
-set g_balance_grenadelauncher_primary_speed_z 0
-set g_balance_grenadelauncher_primary_spread 0
-set g_balance_grenadelauncher_primary_lifetime 30
-set g_balance_grenadelauncher_primary_lifetime2 1
-set g_balance_grenadelauncher_primary_refire 0.8
-set g_balance_grenadelauncher_primary_animtime 0.3
-set g_balance_grenadelauncher_primary_ammo 2
-set g_balance_grenadelauncher_primary_health 25
-set g_balance_grenadelauncher_primary_damageforcescale 4
-set g_balance_grenadelauncher_primary_remote_minbouncecnt 0
-
-set g_balance_grenadelauncher_secondary_type 1
-set g_balance_grenadelauncher_secondary_damage 70
-set g_balance_grenadelauncher_secondary_edgedamage 38
-set g_balance_grenadelauncher_secondary_force 400
-set g_balance_grenadelauncher_secondary_radius 140
-set g_balance_grenadelauncher_secondary_speed 1400
-set g_balance_grenadelauncher_secondary_speed_up 200
-set g_balance_grenadelauncher_secondary_speed_z 0
-set g_balance_grenadelauncher_secondary_spread 0
-set g_balance_grenadelauncher_secondary_lifetime 2.5
-set g_balance_grenadelauncher_secondary_lifetime_bounce 0
-set g_balance_grenadelauncher_secondary_lifetime_stick 0
-set g_balance_grenadelauncher_secondary_refire 0.7
-set g_balance_grenadelauncher_secondary_animtime 0.3
-set g_balance_grenadelauncher_secondary_ammo 2
-set g_balance_grenadelauncher_secondary_health 10
-set g_balance_grenadelauncher_secondary_damageforcescale 4
-set g_balance_grenadelauncher_secondary_remote_detonateprimary 0
-
-set g_balance_grenadelauncher_bouncefactor 0.5
-set g_balance_grenadelauncher_bouncestop 0.075
-
-set g_balance_grenadelauncher_switchdelay_drop 0.15
-set g_balance_grenadelauncher_switchdelay_raise 0.15
-
-set g_balance_grenadelauncher_reload_ammo 0 //default: 12
-set g_balance_grenadelauncher_reload_time 2
-// }}}
-// {{{ electro
-set g_balance_electro_lightning 0
-set g_balance_electro_primary_damage 65
-set g_balance_electro_primary_edgedamage 0
-set g_balance_electro_primary_force 200
-set g_balance_electro_primary_force_up 0
-set g_balance_electro_primary_radius 150
-set g_balance_electro_primary_comboradius 0
-set g_balance_electro_primary_speed 2000
-set g_balance_electro_primary_spread 0
-set g_balance_electro_primary_lifetime 30
-set g_balance_electro_primary_refire 0.6
-set g_balance_electro_primary_animtime 0.3
-set g_balance_electro_primary_ammo 2
-set g_balance_electro_primary_range 0
-set g_balance_electro_primary_falloff_mindist 255 // 0.3 * radius
-set g_balance_electro_primary_falloff_maxdist 850
-set g_balance_electro_primary_falloff_halflifedist 425
-set g_balance_electro_secondary_damage 50
-set g_balance_electro_secondary_edgedamage 0
-set g_balance_electro_secondary_force 200
-set g_balance_electro_secondary_radius 150
-set g_balance_electro_secondary_speed 900
-set g_balance_electro_secondary_speed_up 200
-set g_balance_electro_secondary_speed_z 0
-set g_balance_electro_secondary_spread 0
-set g_balance_electro_secondary_lifetime 5
-set g_balance_electro_secondary_refire 0.3
-set g_balance_electro_secondary_refire2 0
-set g_balance_electro_secondary_animtime 0.3
-set g_balance_electro_secondary_ammo 2
-set g_balance_electro_secondary_health 5
-set g_balance_electro_secondary_damageforcescale 4
-set g_balance_electro_secondary_damagedbycontents 0
-set g_balance_electro_secondary_count 1
-set g_balance_electro_secondary_bouncefactor 0.5
-set g_balance_electro_secondary_bouncestop 0.075
-set g_balance_electro_combo_damage 80
-set g_balance_electro_combo_edgedamage 0
-set g_balance_electro_combo_force 200
-set g_balance_electro_combo_radius 250
-set g_balance_electro_combo_comboradius 0
-set g_balance_electro_combo_speed 2000
-set g_balance_electro_combo_safeammocheck 0
-set g_balance_electro_switchdelay_drop 0.15
-set g_balance_electro_switchdelay_raise 0.15
-set g_balance_electro_reload_ammo 0 //default: 20
-set g_balance_electro_reload_time 2
-// }}}
-// {{{ crylink
-set g_balance_crylink_primary_damage 18
-set g_balance_crylink_primary_edgedamage 0
-set g_balance_crylink_primary_force -55
-set g_balance_crylink_primary_radius 80
-set g_balance_crylink_primary_speed 7000
-set g_balance_crylink_primary_spread 0.03
-set g_balance_crylink_primary_shots 4
-set g_balance_crylink_primary_bounces 1
-set g_balance_crylink_primary_refire 0.4
-set g_balance_crylink_primary_animtime 0.3
-set g_balance_crylink_primary_ammo 2
-set g_balance_crylink_primary_bouncedamagefactor 0.5
-set g_balance_crylink_primary_joindelay 0
-set g_balance_crylink_primary_joinspread 0
-set g_balance_crylink_primary_joinexplode 0
-set g_balance_crylink_primary_joinexplode_damage 0
-set g_balance_crylink_primary_joinexplode_edgedamage 0
-set g_balance_crylink_primary_joinexplode_radius 0
-set g_balance_crylink_primary_joinexplode_force 0
-set g_balance_crylink_primary_linkexplode 1
-
-set g_balance_crylink_primary_middle_lifetime 5 // range: 35000 full, fades to 70000
-set g_balance_crylink_primary_middle_fadetime 5
-set g_balance_crylink_primary_other_lifetime 0.1 // range: 700 full, fades to 2450
-set g_balance_crylink_primary_other_fadetime 0.25
-
-set g_balance_crylink_secondary 1
-set g_balance_crylink_secondary_damage 18
-set g_balance_crylink_secondary_edgedamage 0
-set g_balance_crylink_secondary_force -55
-set g_balance_crylink_secondary_radius 3
-set g_balance_crylink_secondary_speed 7000
-set g_balance_crylink_secondary_spread 0.08
-set g_balance_crylink_secondary_spreadtype 0
-set g_balance_crylink_secondary_shots 7
-set g_balance_crylink_secondary_bounces 0
-set g_balance_crylink_secondary_refire 0.5
-set g_balance_crylink_secondary_animtime 0.3
-set g_balance_crylink_secondary_ammo 2
-set g_balance_crylink_secondary_bouncedamagefactor 0.5
-set g_balance_crylink_secondary_joindelay 0
-set g_balance_crylink_secondary_joinspread 0
-set g_balance_crylink_secondary_joinexplode 0
-set g_balance_crylink_secondary_joinexplode_damage 0
-set g_balance_crylink_secondary_joinexplode_edgedamage 0
-set g_balance_crylink_secondary_joinexplode_radius 0
-set g_balance_crylink_secondary_joinexplode_force 0
-set g_balance_crylink_secondary_linkexplode 1
-
-set g_balance_crylink_secondary_middle_lifetime 5 // range: 35000 full, fades to 70000
-set g_balance_crylink_secondary_middle_fadetime 5
-set g_balance_crylink_secondary_line_lifetime 2 // range: 35000 full, fades to 70000
-set g_balance_crylink_secondary_line_fadetime 2
-
-set g_balance_crylink_switchdelay_drop 0.15
-set g_balance_crylink_switchdelay_raise 0.15
-
-set g_balance_crylink_reload_ammo 0 //default: 10
-set g_balance_crylink_reload_time 2
-// }}}
-// {{{ nex
-set g_balance_nex_primary_damage 100
-set g_balance_nex_primary_force 600
-set g_balance_nex_primary_refire 1.5
-set g_balance_nex_primary_animtime 0.3
-set g_balance_nex_primary_ammo 5
-set g_balance_nex_primary_damagefalloff_mindist 0
-set g_balance_nex_primary_damagefalloff_maxdist 0
-set g_balance_nex_primary_damagefalloff_halflife 0
-set g_balance_nex_primary_damagefalloff_forcehalflife 0
-
-set g_balance_nex_secondary 0
-set g_balance_nex_secondary_charge 0
-set g_balance_nex_secondary_charge_rate 0.1
-set g_balance_nex_secondary_chargepool 0
-set g_balance_nex_secondary_chargepool_regen 0.15
-set g_balance_nex_secondary_chargepool_pause_regen 1
-set g_balance_nex_secondary_chargepool_pause_health_regen 1
-set g_balance_nex_secondary_damage 100
-set g_balance_nex_secondary_force 600
-set g_balance_nex_secondary_refire 1.5
-set g_balance_nex_secondary_animtime 0.3
-set g_balance_nex_secondary_ammo 5
-set g_balance_nex_secondary_damagefalloff_mindist 0
-set g_balance_nex_secondary_damagefalloff_maxdist 0
-set g_balance_nex_secondary_damagefalloff_halflife 0
-set g_balance_nex_secondary_damagefalloff_forcehalflife 0
-
-set g_balance_nex_charge 0
-set g_balance_nex_charge_mindmg 40
-set g_balance_nex_charge_start 0
-set g_balance_nex_charge_rate 0.1
-set g_balance_nex_charge_animlimit 0.5
-set g_balance_nex_charge_limit 0.5
-set g_balance_nex_charge_rot_rate 0
-set g_balance_nex_charge_rot_pause 0 // Dont rot down until this long after release of charge button
-set g_balance_nex_charge_shot_multiplier 0.5
-set g_balance_nex_charge_velocity_rate 0.2
-set g_balance_nex_charge_minspeed 400
-set g_balance_nex_charge_maxspeed 1000
-
-set g_balance_nex_switchdelay_drop 0.15
-set g_balance_nex_switchdelay_raise 0.15
-
-set g_balance_nex_reload_ammo 0 //default: 25
-set g_balance_nex_reload_time 2
-// }}}
-// {{{ minstanex
-set g_balance_minstanex_refire 1
-set g_balance_minstanex_animtime 0.3
-set g_balance_minstanex_ammo 10
-set g_balance_minstanex_laser_ammo 0
-set g_balance_minstanex_laser_animtime 0.3
-set g_balance_minstanex_laser_refire 0.7
-set g_balance_minstanex_switchdelay_drop 0.15
-set g_balance_minstanex_switchdelay_raise 0.15
-set g_balance_minstanex_reload_ammo 0 //default: 50
-set g_balance_minstanex_reload_time 2
-// }}}
-// {{{ hagar
-set g_balance_hagar_primary_damage 37
-set g_balance_hagar_primary_edgedamage 15
-set g_balance_hagar_primary_force 100
-set g_balance_hagar_primary_health 0
-set g_balance_hagar_primary_damageforcescale 0
-set g_balance_hagar_primary_radius 65
-set g_balance_hagar_primary_spread 0.010
-set g_balance_hagar_primary_speed 3000
-set g_balance_hagar_primary_lifetime 30
-set g_balance_hagar_primary_refire 0.15
-set g_balance_hagar_primary_ammo 1
-set g_balance_hagar_secondary 1
-set g_balance_hagar_secondary_load 0
-set g_balance_hagar_secondary_load_speed 0.6
-set g_balance_hagar_secondary_load_spread 0.075
-set g_balance_hagar_secondary_load_spread_bias 0.5
-set g_balance_hagar_secondary_load_max 4
-set g_balance_hagar_secondary_load_hold 0
-set g_balance_hagar_secondary_load_releasedeath 1
-set g_balance_hagar_secondary_load_abort 1
-set g_balance_hagar_secondary_load_linkexplode 0
-set g_balance_hagar_secondary_load_animtime 0.2
-set g_balance_hagar_secondary_damage 37
-set g_balance_hagar_secondary_edgedamage 15
-set g_balance_hagar_secondary_force 100
-set g_balance_hagar_secondary_health 0
-set g_balance_hagar_secondary_damageforcescale 0
-set g_balance_hagar_secondary_radius 65
-set g_balance_hagar_secondary_spread 0.015
-set g_balance_hagar_secondary_speed 1400
-set g_balance_hagar_secondary_lifetime_min 30
-set g_balance_hagar_secondary_lifetime_rand 0
-set g_balance_hagar_secondary_refire 0.15
-set g_balance_hagar_secondary_ammo 1
-set g_balance_hagar_switchdelay_drop 0.15
-set g_balance_hagar_switchdelay_raise 0.15
-set g_balance_hagar_reload_ammo 0 //default: 25
-set g_balance_hagar_reload_time 2
-// }}}
-// {{{ rocketlauncher
-set g_balance_rocketlauncher_damage 105
-set g_balance_rocketlauncher_edgedamage 40
-set g_balance_rocketlauncher_force 600
-set g_balance_rocketlauncher_radius 150
-set g_balance_rocketlauncher_speed 850
-set g_balance_rocketlauncher_speedaccel 0
-set g_balance_rocketlauncher_speedstart 850
-set g_balance_rocketlauncher_lifetime 30
-set g_balance_rocketlauncher_refire 1
-set g_balance_rocketlauncher_animtime 0.3
-set g_balance_rocketlauncher_ammo 3
-set g_balance_rocketlauncher_health 40
-set g_balance_rocketlauncher_damageforcescale 4
-set g_balance_rocketlauncher_detonatedelay 0.2 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time
-set g_balance_rocketlauncher_guiderate 90 // max degrees per second
-set g_balance_rocketlauncher_guideratedelay 0.01 // immediate
-set g_balance_rocketlauncher_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic)
-set g_balance_rocketlauncher_guidedelay 0.15 // delay before guiding kicks in
-set g_balance_rocketlauncher_guidestop 0 // stop guiding when firing again
-set g_balance_rocketlauncher_remote_damage 105
-set g_balance_rocketlauncher_remote_edgedamage 40
-set g_balance_rocketlauncher_remote_radius 150
-set g_balance_rocketlauncher_remote_force 600
-set g_balance_rocketlauncher_switchdelay_drop 0.15
-set g_balance_rocketlauncher_switchdelay_raise 0.15
-set g_balance_rocketlauncher_reload_ammo 0 //default: 25
-set g_balance_rocketlauncher_reload_time 2
-// }}}
-// {{{ porto
-set g_balance_porto_primary_refire 1.5
-set g_balance_porto_primary_animtime 0.3
-set g_balance_porto_primary_speed 2000
-set g_balance_porto_primary_lifetime 30
-set g_balance_porto_secondary 0
-set g_balance_porto_secondary_refire 1.5
-set g_balance_porto_secondary_animtime 0.3
-set g_balance_porto_secondary_speed 2000
-set g_balance_porto_secondary_lifetime 30
-set g_balance_porto_switchdelay_drop 0.15
-set g_balance_porto_switchdelay_raise 0.15
-set g_balance_portal_health 200 // these get recharged whenever the portal is used
-set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used
-// }}}
-// {{{ hook
-set g_balance_hook_primary_fuel 5 // hook monkeys set 0
-set g_balance_hook_primary_refire 0 // hook monkeys set 0
-set g_balance_hook_primary_animtime 0.3 // good shoot anim
-set g_balance_hook_primary_hooked_time_max 0 // infinite
-set g_balance_hook_primary_hooked_time_free 2 // 2s being hooked are free
-set g_balance_hook_primary_hooked_fuel 5 // fuel per second hooked
-set g_balance_hook_secondary_damage 25 // not much
-set g_balance_hook_secondary_edgedamage 5 // not much
-set g_balance_hook_secondary_radius 500 // LOTS
-set g_balance_hook_secondary_force -2000 // LOTS
-set g_balance_hook_secondary_ammo 50 // a whole pack
-set g_balance_hook_secondary_lifetime 30 // infinite
-set g_balance_hook_secondary_speed 0 // not much throwing
-set g_balance_hook_secondary_gravity 5 // fast falling
-set g_balance_hook_secondary_refire 3 // don't drop too many bombs...
-set g_balance_hook_secondary_animtime 0.3 // good shoot anim
-set g_balance_hook_secondary_power 3 // effect behaves like a square function
-set g_balance_hook_secondary_duration 1.5 // effect runs for three seconds
-set g_balance_hook_secondary_health 0
-set g_balance_hook_secondary_damageforcescale 0
-set g_balance_hook_switchdelay_drop 0.15
-set g_balance_hook_switchdelay_raise 0.15
-// }}}
-// {{{ tuba
-set g_balance_tuba_refire 0.05
-set g_balance_tuba_animtime 0.05
-set g_balance_tuba_attenuation 0.5
-set g_balance_tuba_volume 1
-set g_balance_tuba_fadetime 0.25
-set g_balance_tuba_damage 5
-set g_balance_tuba_edgedamage 0
-set g_balance_tuba_radius 200
-set g_balance_tuba_force 40
-set g_balance_tuba_pitchstep 6
-set g_balance_tuba_switchdelay_drop 0.15
-set g_balance_tuba_switchdelay_raise 0.15
-// }}}
-// {{{ fireball
-set g_balance_fireball_primary_animtime 0.15
-set g_balance_fireball_primary_bfgdamage 100
-set g_balance_fireball_primary_bfgforce 0
-set g_balance_fireball_primary_bfgradius 1000
-set g_balance_fireball_primary_damage 200
-set g_balance_fireball_primary_damageforcescale 4
-set g_balance_fireball_primary_edgedamage 0
-set g_balance_fireball_primary_force 700
-set g_balance_fireball_primary_health 50
-set g_balance_fireball_primary_laserburntime 0.5
-set g_balance_fireball_primary_laserdamage 80
-set g_balance_fireball_primary_laseredgedamage 20
-set g_balance_fireball_primary_laserradius 256
-set g_balance_fireball_primary_lifetime 15
-set g_balance_fireball_primary_radius 200
-set g_balance_fireball_primary_refire 5
-set g_balance_fireball_primary_refire2 0
-set g_balance_fireball_primary_speed 650
-set g_balance_fireball_primary_spread 0
-set g_balance_fireball_secondary_animtime 0.3
-set g_balance_fireball_secondary_damage 40
-set g_balance_fireball_secondary_damageforcescale 4
-set g_balance_fireball_secondary_damagetime 5
-set g_balance_fireball_secondary_force 100
-set g_balance_fireball_secondary_laserburntime 0.5
-set g_balance_fireball_secondary_laserdamage 50
-set g_balance_fireball_secondary_laseredgedamage 20
-set g_balance_fireball_secondary_laserradius 110
-set g_balance_fireball_secondary_lifetime 7
-set g_balance_fireball_secondary_refire 2
-set g_balance_fireball_secondary_speed 900
-set g_balance_fireball_secondary_speed_up 100
-set g_balance_fireball_secondary_speed_z 0
-set g_balance_fireball_secondary_spread 0
-set g_balance_fireball_switchdelay_drop 0.15
-set g_balance_fireball_switchdelay_raise 0.15
-// }}}
diff --git a/balanceFruitieX.cfg b/balanceFruitieX.cfg
deleted file mode 100644 (file)
index f985659..0000000
+++ /dev/null
@@ -1,707 +0,0 @@
-g_mod_balance FruitieX
-
-// {{{ starting gear
-set g_start_weapon_laser -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_shotgun -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_uzi -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_hagar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_balance_health_start 125
-set g_balance_armor_start 0
-set g_start_ammo_shells 20
-set g_start_ammo_nails 0
-set g_start_ammo_rockets 0
-set g_start_ammo_cells 0
-set g_start_ammo_fuel 0
-set g_warmup_start_health 200 "starting values when being in warmup-stage"
-set g_warmup_start_armor 100 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_shells 50 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_nails 150 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_rockets 50 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_cells 50 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage"
-set g_lms_start_health 200
-set g_lms_start_armor 100
-set g_lms_start_ammo_shells 30
-set g_lms_start_ammo_nails 200
-set g_lms_start_ammo_rockets 150
-set g_lms_start_ammo_cells 150
-set g_lms_start_ammo_fuel 0
-set g_balance_nix_roundtime 25
-set g_balance_nix_incrtime 1.6
-set g_balance_nix_ammo_shells 15
-set g_balance_nix_ammo_nails 45
-set g_balance_nix_ammo_rockets 15
-set g_balance_nix_ammo_cells 15
-set g_balance_nix_ammo_fuel 0
-set g_balance_nix_ammoincr_shells 2
-set g_balance_nix_ammoincr_nails 6
-set g_balance_nix_ammoincr_rockets 2
-set g_balance_nix_ammoincr_cells 2
-set g_balance_nix_ammoincr_fuel 2
-// }}}
-
-// {{{ pickup items
-set g_pickup_ammo_anyway 1
-set g_pickup_weapons_anyway 1
-set g_pickup_shells 20
-set g_pickup_shells_weapon 10
-set g_pickup_shells_max 45
-set g_pickup_nails 120
-set g_pickup_nails_weapon 60
-set g_pickup_nails_max 300
-set g_pickup_rockets 25
-set g_pickup_rockets_weapon 15
-set g_pickup_rockets_max 150
-set g_pickup_cells 30
-set g_pickup_cells_weapon 20
-set g_pickup_cells_max 150
-set g_pickup_fuel 25
-set g_pickup_fuel_weapon 15
-set g_pickup_fuel_jetpack 50
-set g_pickup_fuel_max 100
-set g_pickup_armorsmall 5
-set g_pickup_armorsmall_max 150
-set g_pickup_armorsmall_anyway 1
-set g_pickup_armormedium 25
-set g_pickup_armormedium_max 50
-set g_pickup_armormedium_anyway 0
-set g_pickup_armorbig 50
-set g_pickup_armorbig_max 75; // LOG: to allow a little more armor from medium armor
-set g_pickup_armorbig_anyway 0
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 150
-set g_pickup_armorlarge_anyway 1
-set g_pickup_healthsmall 5
-set g_pickup_healthsmall_max 250
-set g_pickup_healthsmall_anyway 1
-set g_pickup_healthmedium 25
-set g_pickup_healthmedium_max 100
-set g_pickup_healthmedium_anyway 0
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 150
-set g_pickup_healthlarge_anyway 0
-set g_pickup_healthmega 100
-set g_pickup_healthmega_max 250
-set g_pickup_healthmega_anyway 1
-set g_pickup_respawntime_short 15
-set g_pickup_respawntime_medium 20
-set g_pickup_respawntime_long 30
-set g_pickup_respawntime_powerup 120
-set g_pickup_respawntime_weapon 10
-set g_pickup_respawntime_superweapon 120
-set g_pickup_respawntime_ammo 25
-set g_pickup_respawntimejitter_short 0
-set g_pickup_respawntimejitter_medium 0
-set g_pickup_respawntimejitter_long 0
-set g_pickup_respawntimejitter_powerup 10
-set g_pickup_respawntimejitter_weapon 0
-set g_pickup_respawntimejitter_superweapon 10
-set g_pickup_respawntimejitter_ammo 0
-// }}}
-
-// {{{ regen/rot
-set g_balance_health_regen 0.05
-set g_balance_health_regenlinear 0
-set g_balance_pause_health_regen 5
-set g_balance_pause_health_regen_spawn 0
-set g_balance_health_rot 0
-set g_balance_health_rotlinear 1
-set g_balance_pause_health_rot 1
-set g_balance_pause_health_rot_spawn 0
-set g_balance_health_regenstable 100
-set g_balance_health_rotstable 100
-set g_balance_health_limit 999
-set g_balance_armor_regen 0
-set g_balance_armor_regenlinear 0
-set g_balance_armor_rot 0
-set g_balance_armor_rotlinear 1
-set g_balance_pause_armor_rot 1
-set g_balance_pause_armor_rot_spawn 0
-set g_balance_armor_regenstable 100
-set g_balance_armor_rotstable 100
-set g_balance_armor_limit 999
-set g_balance_armor_blockpercent 0.7
-set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)"
-set g_balance_fuel_regenlinear 0
-set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter
-set g_balance_fuel_rot 0.05
-set g_balance_fuel_rotlinear 0
-set g_balance_pause_fuel_rot 5
-set g_balance_pause_fuel_rot_spawn 10
-set g_balance_fuel_regenstable 50
-set g_balance_fuel_rotstable 100
-set g_balance_fuel_limit 999
-// }}}
-
-// {{{ misc
-set g_balance_selfdamagepercent 0.65
-set g_weaponspeedfactor 1 "weapon projectile speed multiplier"
-set g_weaponratefactor 1 "weapon fire rate multiplier"
-set g_weapondamagefactor 1 "weapon damage multiplier"
-set g_weaponforcefactor 1 "weapon force multiplier"
-set g_weaponspreadfactor 1 "weapon spread multiplier"
-set g_balance_firetransfer_time 0.9
-set g_balance_firetransfer_damage 0.8
-set g_throughfloor_damage 0.7
-set g_throughfloor_force 0.8
-set g_projectiles_damage 2
-// possible values:
-// -2: absolutely no damage to projectiles (no exceptions)
-// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines)
-// 0: only damage from contents (lava/slime) or exceptions
-// 1: only self damage or damage from contents or exceptions
-// 2: allow all damage to projectiles normally
-set g_projectiles_keep_owner 0
-set g_projectiles_newton_style 2
-// possible values:
-// 0: absolute velocity projectiles (like Quake)
-// 1: relative velocity projectiles, "Newtonian" (like Tribes 2)
-// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard)
-set g_projectiles_newton_style_2_minfactor 0.7
-set g_projectiles_newton_style_2_maxfactor 5
-set g_projectiles_spread_style 7
-// possible values:
-// 0: forward + solid sphere (like Quake) - varies velocity
-// 1: forward + flattened solid sphere
-// 2: forward + solid circle
-// 3: forward + normal distribution 3D - varies velocity
-// 4: forward + normal distribution on a plane
-// 5: forward + circle with 1-r falloff
-// 6: forward + circle with 1-r^2 falloff
-// 7: forward + circle with (1-r)(2-r) falloff
-set g_balance_falldamage_deadminspeed 150
-set g_balance_falldamage_minspeed 800
-set g_balance_falldamage_factor 0.20
-set g_balance_falldamage_maxdamage 15
-set g_balance_damagepush_speedfactor 2.5
-set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage
-set g_balance_contents_drowndelay 10 // time under water before a player begins drowning
-set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning
-set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava
-set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime
-set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime
-set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap"
-// }}}
-
-// {{{ powerups
-set g_balance_powerup_invincible_takedamage 0.6
-set g_balance_powerup_invincible_time 30
-set g_balance_powerup_strength_damage 3
-set g_balance_powerup_strength_force 4
-set g_balance_powerup_strength_time 30
-set g_balance_powerup_strength_selfdamage 1.5
-set g_balance_powerup_strength_selfforce 1.5
-set g_balance_superweapons_time 30
-// }}}
-
-// {{{ jetpack/hook
-set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack"
-set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction"
-set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)"
-set g_jetpack_maxspeed_side 1500 "max speed of the jetpack in xy direction"
-set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction"
-set g_jetpack_fuel 8 "fuel per second for jetpack"
-set g_jetpack_attenuation 2 "jetpack sound attenuation"
-
-set g_grappling_hook_tarzan 2 // 2: can also pull players
-set g_balance_grapplehook_speed_fly 1800
-set g_balance_grapplehook_speed_pull 2000
-set g_balance_grapplehook_force_rubber 2000
-set g_balance_grapplehook_force_rubber_overstretch 1000
-set g_balance_grapplehook_length_min 50
-set g_balance_grapplehook_stretch 50
-set g_balance_grapplehook_airfriction 0.2
-set g_balance_grapplehook_health 130
-set g_balance_grapplehook_damagedbycontents 0
-// }}}
-
-// {{{ weapon properties
-// {{{ laser
-set g_balance_laser_primary_damage 20 // dps 33, hope that's not too high
-set g_balance_laser_primary_edgedamage 20
-set g_balance_laser_primary_force 150 // this looks insanely low, but actually isn't with zscale and velocitybias
-set g_balance_laser_primary_radius 60
-set g_balance_laser_primary_speed 5000
-set g_balance_laser_primary_spread 0
-set g_balance_laser_primary_refire 0.6
-set g_balance_laser_primary_animtime 0.4
-set g_balance_laser_primary_lifetime 5
-set g_balance_laser_primary_shotangle 0
-set g_balance_laser_primary_delay 0
-set g_balance_laser_primary_gauntlet 0
-set g_balance_laser_primary_force_zscale 2 // 300 upforce
-set g_balance_laser_primary_force_velocitybias 0.3
-set g_balance_laser_primary_force_other_scale 2.5 // force 375 when pushing others around
-set g_balance_laser_secondary 0 // when 1, a secondary laser mode exists
-set g_balance_laser_secondary_damage 200 // dps
-set g_balance_laser_secondary_edgedamage 0
-set g_balance_laser_secondary_force 1300
-set g_balance_laser_secondary_radius 60
-set g_balance_laser_secondary_speed 0
-set g_balance_laser_secondary_spread 0
-set g_balance_laser_secondary_refire 0.066
-set g_balance_laser_secondary_animtime 0.066
-set g_balance_laser_secondary_lifetime 0
-set g_balance_laser_secondary_shotangle 0
-set g_balance_laser_secondary_delay 0
-set g_balance_laser_secondary_gauntlet 1
-set g_balance_laser_secondary_force_zscale 1.25
-set g_balance_laser_secondary_force_velocitybias 0
-set g_balance_laser_secondary_force_other_scale 0
-set g_balance_laser_switchdelay_drop 0.15
-set g_balance_laser_switchdelay_raise 0.15
-set g_balance_laser_reload_ammo 0 //default: 6
-set g_balance_laser_reload_time 2
-// }}}
-// {{{ shotgun
-set g_balance_shotgun_primary_bullets 18
-set g_balance_shotgun_primary_damage 3.5 // LOG: changed from 4 to 3.5, total damage 63
-set g_balance_shotgun_primary_force 20
-set g_balance_shotgun_primary_spread 0.16 // LOG: changed from 0.18 -> 0.16 to compensate a little for lower damage
-set g_balance_shotgun_primary_refire 1
-set g_balance_shotgun_primary_animtime 0.3
-set g_balance_shotgun_primary_ammo 1
-set g_balance_shotgun_primary_speed 12000
-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 0
-set g_balance_shotgun_secondary_melee_multihit 1
-set g_balance_shotgun_secondary_damage 110
-set g_balance_shotgun_secondary_force 150
-set g_balance_shotgun_secondary_refire 1.1
-set g_balance_shotgun_secondary_animtime 1
-set g_balance_shotgun_switchdelay_drop 0.15
-set g_balance_shotgun_switchdelay_raise 0.15
-set g_balance_shotgun_reload_ammo 0 //default: 5
-set g_balance_shotgun_reload_time 2
-// }}}
-// {{{ uzi
-set g_balance_uzi_mode 1                               // Activates varible spread for sustained & burst mode secondary
-set g_balance_uzi_spread_min 0.02
-set g_balance_uzi_spread_max 0.3 // LOG: 0.6 -> 0.3
-set g_balance_uzi_spread_add 0.008
-
-set g_balance_uzi_burst 3                              // # of bullets in a burst (if set to 2 or more)
-set g_balance_uzi_burst_animtime 0.45
-set g_balance_uzi_burst_refire 0.05            // refire between burst bullets
-set g_balance_uzi_burst_refire2 0.45   // refire after burst
-set g_balance_uzi_burst_spread 0.07
-set g_balance_uzi_burst_damage 25
-set g_balance_uzi_burst_force 50
-set g_balance_uzi_burst_ammo 3
-
-set g_balance_uzi_first 1
-set g_balance_uzi_first_damage 15 / f/ LOG: 22 -> 15
-set g_balance_uzi_first_force 50
-set g_balance_uzi_first_spread 0.03
-set g_balance_uzi_first_refire 0.2
-set g_balance_uzi_first_ammo 2
-
-set g_balance_uzi_sustained_damage 12   // 120 dps
-set g_balance_uzi_sustained_force 12
-set g_balance_uzi_sustained_spread 0.06
-set g_balance_uzi_sustained_refire 0.1
-set g_balance_uzi_sustained_ammo 1
-
-set g_balance_uzi_speed 18000
-set g_balance_uzi_bulletconstant 115 // 13.1qu
-
-set g_balance_uzi_switchdelay_drop 0.15
-set g_balance_uzi_switchdelay_raise 0.15
-
-set g_balance_uzi_reload_ammo 0 //default: 30
-set g_balance_uzi_reload_time 2
-// }}}
-// {{{ mortar
-set g_balance_grenadelauncher_primary_type 0
-set g_balance_grenadelauncher_primary_damage 44
-set g_balance_grenadelauncher_primary_edgedamage 32
-set g_balance_grenadelauncher_primary_force 300
-set g_balance_grenadelauncher_primary_radius 115
-set g_balance_grenadelauncher_primary_speed 1500
-set g_balance_grenadelauncher_primary_speed_up 225
-set g_balance_grenadelauncher_primary_speed_z 0
-set g_balance_grenadelauncher_primary_spread 0
-set g_balance_grenadelauncher_primary_lifetime 5
-set g_balance_grenadelauncher_primary_lifetime2 0.65
-set g_balance_grenadelauncher_primary_refire 0.8
-set g_balance_grenadelauncher_primary_animtime 0.3
-set g_balance_grenadelauncher_primary_ammo 2
-set g_balance_grenadelauncher_primary_health 80
-set g_balance_grenadelauncher_primary_damageforcescale 0
-set g_balance_grenadelauncher_primary_remote_minbouncecnt 0
-
-set g_balance_grenadelauncher_secondary_type 1
-set g_balance_grenadelauncher_secondary_damage 62
-set g_balance_grenadelauncher_secondary_edgedamage 32
-set g_balance_grenadelauncher_secondary_force 300
-set g_balance_grenadelauncher_secondary_radius 150
-set g_balance_grenadelauncher_secondary_speed 1000
-set g_balance_grenadelauncher_secondary_speed_up 250
-set g_balance_grenadelauncher_secondary_speed_z 0
-set g_balance_grenadelauncher_secondary_spread 0
-set g_balance_grenadelauncher_secondary_lifetime 3
-set g_balance_grenadelauncher_secondary_lifetime_bounce 0
-set g_balance_grenadelauncher_secondary_lifetime_stick 0.65
-set g_balance_grenadelauncher_secondary_refire 0.8
-set g_balance_grenadelauncher_secondary_animtime 0.3
-set g_balance_grenadelauncher_secondary_ammo 2
-set g_balance_grenadelauncher_secondary_health 40
-set g_balance_grenadelauncher_secondary_damageforcescale 0
-set g_balance_grenadelauncher_secondary_remote_detonateprimary 0
-
-set g_balance_grenadelauncher_bouncefactor 0.5
-set g_balance_grenadelauncher_bouncestop 0.12
-
-set g_balance_grenadelauncher_switchdelay_drop 0.15
-set g_balance_grenadelauncher_switchdelay_raise 0.15
-
-set g_balance_grenadelauncher_reload_ammo 0 //default: 12
-set g_balance_grenadelauncher_reload_time 2
-// }}}
-// {{{ electro
-set g_balance_electro_lightning 1
-set g_balance_electro_primary_damage 100
-set g_balance_electro_primary_edgedamage 0
-set g_balance_electro_primary_force 425
-set g_balance_electro_primary_force_up 125
-set g_balance_electro_primary_radius 850
-set g_balance_electro_primary_comboradius 150
-set g_balance_electro_primary_speed 0
-set g_balance_electro_primary_spread 0
-set g_balance_electro_primary_lifetime 0
-set g_balance_electro_primary_refire 0.4
-set g_balance_electro_primary_animtime 0.2
-set g_balance_electro_primary_ammo 5
-set g_balance_electro_primary_range 800
-set g_balance_electro_primary_falloff_mindist 0
-set g_balance_electro_primary_falloff_maxdist 0
-set g_balance_electro_primary_falloff_halflifedist 0
-set g_balance_electro_secondary_damage 25
-set g_balance_electro_secondary_edgedamage 0
-set g_balance_electro_secondary_force 100
-set g_balance_electro_secondary_radius 100
-set g_balance_electro_secondary_speed 700
-set g_balance_electro_secondary_speed_up 200
-set g_balance_electro_secondary_speed_z 0
-set g_balance_electro_secondary_spread 0.08
-set g_balance_electro_secondary_lifetime 3.5
-set g_balance_electro_secondary_refire 0.2
-set g_balance_electro_secondary_refire2 2
-set g_balance_electro_secondary_animtime 0.2
-set g_balance_electro_secondary_ammo 2
-set g_balance_electro_secondary_health 10
-set g_balance_electro_secondary_damageforcescale 4
-set g_balance_electro_secondary_damagedbycontents 0
-set g_balance_electro_secondary_count 3
-set g_balance_electro_secondary_bouncefactor 0.5
-set g_balance_electro_secondary_bouncestop 0.075
-set g_balance_electro_combo_damage 50
-set g_balance_electro_combo_edgedamage 0
-set g_balance_electro_combo_force 80
-set g_balance_electro_combo_radius 250
-set g_balance_electro_combo_comboradius 0
-set g_balance_electro_combo_speed 400
-set g_balance_electro_combo_safeammocheck 1
-set g_balance_electro_switchdelay_drop 0.15
-set g_balance_electro_switchdelay_raise 0.15
-set g_balance_electro_reload_ammo 0 //default: 20
-set g_balance_electro_reload_time 2
-// }}}
-// {{{ crylink
-set g_balance_crylink_primary_damage 7 // LOG: 10 -> 7
-set g_balance_crylink_primary_edgedamage 4 // LOG: 6 -> 4
-set g_balance_crylink_primary_force 35
-set g_balance_crylink_primary_radius 80
-set g_balance_crylink_primary_speed 1500
-set g_balance_crylink_primary_spread 0.05
-set g_balance_crylink_primary_shots 7
-set g_balance_crylink_primary_bounces 2
-set g_balance_crylink_primary_refire 0.8
-set g_balance_crylink_primary_animtime 0.3
-set g_balance_crylink_primary_ammo 2
-set g_balance_crylink_primary_bouncedamagefactor 0.2
-set g_balance_crylink_primary_joindelay 0
-set g_balance_crylink_primary_joinspread 0.2
-set g_balance_crylink_primary_joinexplode 0
-set g_balance_crylink_primary_joinexplode_damage 0
-set g_balance_crylink_primary_joinexplode_edgedamage 0
-set g_balance_crylink_primary_joinexplode_radius 0
-set g_balance_crylink_primary_joinexplode_force 0
-set g_balance_crylink_primary_linkexplode 1
-
-set g_balance_crylink_primary_middle_lifetime 5 // range: 10000 full, fades to 20000
-set g_balance_crylink_primary_middle_fadetime 5
-set g_balance_crylink_primary_other_lifetime 2 // range: 800 full, fades to 1300
-set g_balance_crylink_primary_other_fadetime 0.25
-
-set g_balance_crylink_secondary 1
-set g_balance_crylink_secondary_damage 5 // LOG: 8 -> 5
-set g_balance_crylink_secondary_edgedamage 3
-set g_balance_crylink_secondary_force 16 // LOG: 20 -> 16
-set g_balance_crylink_secondary_radius 15 // LOG: 20 -> 15
-set g_balance_crylink_secondary_speed 1250 // LOG: 1500 -> 1250
-set g_balance_crylink_secondary_spread 0.1
-set g_balance_crylink_secondary_spreadtype 0
-set g_balance_crylink_secondary_shots 6
-set g_balance_crylink_secondary_bounces 2
-set g_balance_crylink_secondary_refire 0.9 // LOG: 0.8 -> 0.9
-set g_balance_crylink_secondary_animtime 0.3
-set g_balance_crylink_secondary_ammo 3 // LOG: 2 -> 3
-set g_balance_crylink_secondary_bouncedamagefactor 0.4 // LOG: 0.2 -> 0.4
-set g_balance_crylink_secondary_joindelay 0
-set g_balance_crylink_secondary_joinspread 0.2
-set g_balance_crylink_secondary_joinexplode 0
-set g_balance_crylink_secondary_joinexplode_damage 0
-set g_balance_crylink_secondary_joinexplode_edgedamage 0
-set g_balance_crylink_secondary_joinexplode_radius 0
-set g_balance_crylink_secondary_joinexplode_force 0
-set g_balance_crylink_secondary_linkexplode 0
-
-set g_balance_crylink_secondary_middle_lifetime 5 // range: 10000 full, fades to 10000
-set g_balance_crylink_secondary_middle_fadetime 5
-set g_balance_crylink_secondary_line_lifetime 2 // range: 4000 full, fades to 8000
-set g_balance_crylink_secondary_line_fadetime 0.25
-
-set g_balance_crylink_switchdelay_drop 0.15
-set g_balance_crylink_switchdelay_raise 0.15
-
-set g_balance_crylink_reload_ammo 0 //default: 10
-set g_balance_crylink_reload_time 2
-// }}}
-// {{{ nex
-set g_balance_nex_primary_damage 90
-set g_balance_nex_primary_force 500
-set g_balance_nex_primary_refire 1
-set g_balance_nex_primary_animtime 0.3
-set g_balance_nex_primary_ammo 5
-set g_balance_nex_primary_damagefalloff_mindist 0
-set g_balance_nex_primary_damagefalloff_maxdist 0
-set g_balance_nex_primary_damagefalloff_halflife 0
-set g_balance_nex_primary_damagefalloff_forcehalflife 0
-
-set g_balance_nex_secondary 0 // LOG: disable secondary
-set g_balance_nex_secondary_charge 0 // LOG: disable secondary charge
-set g_balance_nex_secondary_charge_rate 0.4
-set g_balance_nex_secondary_chargepool 1
-set g_balance_nex_secondary_chargepool_regen 0.25
-set g_balance_nex_secondary_chargepool_pause_regen 2
-set g_balance_nex_secondary_chargepool_pause_health_regen 0.5
-set g_balance_nex_secondary_damage 0
-set g_balance_nex_secondary_force 0
-set g_balance_nex_secondary_refire 0
-set g_balance_nex_secondary_animtime 0
-set g_balance_nex_secondary_ammo 0.4 // full charge pool is 1, so it depletes in 2.5 secs
-set g_balance_nex_secondary_damagefalloff_mindist 0
-set g_balance_nex_secondary_damagefalloff_maxdist 0
-set g_balance_nex_secondary_damagefalloff_halflife 0
-set g_balance_nex_secondary_damagefalloff_forcehalflife 0
-
-set g_balance_nex_charge 1
-set g_balance_nex_charge_mindmg 20
-set g_balance_nex_charge_start 0.5
-set g_balance_nex_charge_rate 0.5
-set g_balance_nex_charge_animlimit 0.5
-set g_balance_nex_charge_limit 1 // LOG: 0.5 -> 1 - allow to fully charge automaticaly
-set g_balance_nex_charge_rot_rate 0 // LOG: 0.1 -> 0 - disable rot
-set g_balance_nex_charge_rot_pause 0.5 // Dont rot down until this long after release of charge button
-set g_balance_nex_charge_shot_multiplier 0
-set g_balance_nex_charge_velocity_rate 0
-set g_balance_nex_charge_minspeed 600
-set g_balance_nex_charge_maxspeed 1000
-
-set g_balance_nex_switchdelay_drop 0.15
-set g_balance_nex_switchdelay_raise 0.15
-
-set g_balance_nex_reload_ammo 0 //default: 25
-set g_balance_nex_reload_time 2
-// }}}
-// {{{ minstanex
-set g_balance_minstanex_refire 1
-set g_balance_minstanex_animtime 0.50
-set g_balance_minstanex_ammo 10
-set g_balance_minstanex_laser_ammo 0
-set g_balance_minstanex_laser_animtime 0.3
-set g_balance_minstanex_laser_refire 0.6
-set g_balance_minstanex_switchdelay_drop 0.15
-set g_balance_minstanex_switchdelay_raise 0.15
-set g_balance_minstanex_reload_ammo 0 //default: 50
-set g_balance_minstanex_reload_time 2
-// }}}
-// {{{ hagar
-set g_balance_hagar_primary_damage 14
-set g_balance_hagar_primary_edgedamage 6
-set g_balance_hagar_primary_force 70
-set g_balance_hagar_primary_health 0
-set g_balance_hagar_primary_damageforcescale 0
-set g_balance_hagar_primary_radius 110
-set g_balance_hagar_primary_spread 0.1
-set g_balance_hagar_primary_speed 1800
-set g_balance_hagar_primary_lifetime 5
-set g_balance_hagar_primary_refire 0.12
-set g_balance_hagar_primary_ammo 1
-set g_balance_hagar_secondary 1
-set g_balance_hagar_secondary_load 0
-set g_balance_hagar_secondary_load_speed 0.6
-set g_balance_hagar_secondary_load_spread 0.075
-set g_balance_hagar_secondary_load_spread_bias 0.5
-set g_balance_hagar_secondary_load_max 4
-set g_balance_hagar_secondary_load_hold 0
-set g_balance_hagar_secondary_load_releasedeath 1
-set g_balance_hagar_secondary_load_abort 1
-set g_balance_hagar_secondary_load_linkexplode 0
-set g_balance_hagar_secondary_load_animtime 0.2
-set g_balance_hagar_secondary_damage 14 // default for _load: 32
-set g_balance_hagar_secondary_edgedamage 6 // default for _load: 10
-set g_balance_hagar_secondary_force 70 // default for _load: 160
-set g_balance_hagar_secondary_health 0
-set g_balance_hagar_secondary_damageforcescale 0
-set g_balance_hagar_secondary_radius 125
-set g_balance_hagar_secondary_spread 0.15 // default for _load: 0.08
-set g_balance_hagar_secondary_speed 1800
-set g_balance_hagar_secondary_lifetime_min 5
-set g_balance_hagar_secondary_lifetime_rand 0
-set g_balance_hagar_secondary_refire 0.12 // default for _load: 0.8
-set g_balance_hagar_secondary_ammo 1
-set g_balance_hagar_switchdelay_drop 0.15
-set g_balance_hagar_switchdelay_raise 0.15
-set g_balance_hagar_reload_ammo 0 //default: 25
-set g_balance_hagar_reload_time 2
-// }}}
-// {{{ rocketlauncher
-set g_balance_rocketlauncher_damage 82
-set g_balance_rocketlauncher_edgedamage 32
-set g_balance_rocketlauncher_force 350
-set g_balance_rocketlauncher_radius 130
-set g_balance_rocketlauncher_speed 1400
-set g_balance_rocketlauncher_speedaccel 1400
-set g_balance_rocketlauncher_speedstart 800
-set g_balance_rocketlauncher_lifetime 5
-set g_balance_rocketlauncher_refire 1
-set g_balance_rocketlauncher_animtime 0.3
-set g_balance_rocketlauncher_ammo 3
-set g_balance_rocketlauncher_health 0
-set g_balance_rocketlauncher_damageforcescale 0
-set g_balance_rocketlauncher_detonatedelay 0.05 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time
-set g_balance_rocketlauncher_guiderate 42 // max degrees per second
-set g_balance_rocketlauncher_guideratedelay 0.01 // immediate
-set g_balance_rocketlauncher_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic)
-set g_balance_rocketlauncher_guidedelay 0.15 // delay before guiding kicks in
-set g_balance_rocketlauncher_guidestop 0 // stop guiding when firing again
-set g_balance_rocketlauncher_remote_damage 60
-set g_balance_rocketlauncher_remote_edgedamage 20
-set g_balance_rocketlauncher_remote_radius 120
-set g_balance_rocketlauncher_remote_force 350
-set g_balance_rocketlauncher_switchdelay_drop 0.15
-set g_balance_rocketlauncher_switchdelay_raise 0.15
-set g_balance_rocketlauncher_reload_ammo 0 //default: 25
-set g_balance_rocketlauncher_reload_time 2
-// }}}
-// {{{ porto
-set g_balance_porto_primary_refire 1.5
-set g_balance_porto_primary_animtime 0.2
-set g_balance_porto_primary_speed 2000
-set g_balance_porto_primary_lifetime 5
-set g_balance_porto_secondary 0
-set g_balance_porto_secondary_refire 1.5
-set g_balance_porto_secondary_animtime 0.2
-set g_balance_porto_secondary_speed 2000
-set g_balance_porto_secondary_lifetime 5
-set g_balance_porto_switchdelay_drop 0.15
-set g_balance_porto_switchdelay_raise 0.15
-set g_balance_portal_health 200 // these get recharged whenever the portal is used
-set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used
-// }}}
-// {{{ hook
-set g_balance_hook_primary_fuel 5 // hook monkeys set 0
-set g_balance_hook_primary_refire 0 // hook monkeys set 0
-set g_balance_hook_primary_animtime 0.2 // good shoot anim
-set g_balance_hook_primary_hooked_time_max 0 // infinite
-set g_balance_hook_primary_hooked_time_free 2 // 2s being hooked are free
-set g_balance_hook_primary_hooked_fuel 5 // fuel per second hooked
-set g_balance_hook_secondary_damage 25 // not much
-set g_balance_hook_secondary_edgedamage 5 // not much
-set g_balance_hook_secondary_radius 500 // LOTS
-set g_balance_hook_secondary_force -2000 // LOTS
-set g_balance_hook_secondary_ammo 50 // a whole pack
-set g_balance_hook_secondary_lifetime 5 // infinite
-set g_balance_hook_secondary_speed 0 // not much throwing
-set g_balance_hook_secondary_gravity 5 // fast falling
-set g_balance_hook_secondary_refire 3 // don't drop too many bombs...
-set g_balance_hook_secondary_animtime 0.2 // good shoot anim
-set g_balance_hook_secondary_power 3 // effect behaves like a square function
-set g_balance_hook_secondary_duration 1.5 // effect runs for three seconds
-set g_balance_hook_secondary_health 0
-set g_balance_hook_secondary_damageforcescale 0
-set g_balance_hook_switchdelay_drop 0.15
-set g_balance_hook_switchdelay_raise 0.15
-// }}}
-// {{{ tuba
-set g_balance_tuba_refire 0.05
-set g_balance_tuba_animtime 0.05
-set g_balance_tuba_attenuation 0.5
-set g_balance_tuba_volume 1
-set g_balance_tuba_fadetime 0.25
-set g_balance_tuba_damage 5
-set g_balance_tuba_edgedamage 0
-set g_balance_tuba_radius 200
-set g_balance_tuba_force 40
-set g_balance_tuba_pitchstep 6
-set g_balance_tuba_switchdelay_drop 0.15
-set g_balance_tuba_switchdelay_raise 0.15
-// }}}
-// {{{ fireball
-set g_balance_fireball_primary_animtime 0.2
-set g_balance_fireball_primary_bfgdamage 100
-set g_balance_fireball_primary_bfgforce 0
-set g_balance_fireball_primary_bfgradius 1000
-set g_balance_fireball_primary_damage 200
-set g_balance_fireball_primary_damageforcescale 4
-set g_balance_fireball_primary_edgedamage 0
-set g_balance_fireball_primary_force 700
-set g_balance_fireball_primary_health 50
-set g_balance_fireball_primary_laserburntime 0.5
-set g_balance_fireball_primary_laserdamage 80
-set g_balance_fireball_primary_laseredgedamage 20
-set g_balance_fireball_primary_laserradius 256
-set g_balance_fireball_primary_lifetime 15
-set g_balance_fireball_primary_radius 200
-set g_balance_fireball_primary_refire 5
-set g_balance_fireball_primary_refire2 0
-set g_balance_fireball_primary_speed 650
-set g_balance_fireball_primary_spread 0
-set g_balance_fireball_secondary_animtime 0.2
-set g_balance_fireball_secondary_damage 40
-set g_balance_fireball_secondary_damageforcescale 4
-set g_balance_fireball_secondary_damagetime 5
-set g_balance_fireball_secondary_force 100
-set g_balance_fireball_secondary_laserburntime 0.5
-set g_balance_fireball_secondary_laserdamage 50
-set g_balance_fireball_secondary_laseredgedamage 20
-set g_balance_fireball_secondary_laserradius 110
-set g_balance_fireball_secondary_lifetime 7
-set g_balance_fireball_secondary_refire 2
-set g_balance_fireball_secondary_speed 900
-set g_balance_fireball_secondary_speed_up 100
-set g_balance_fireball_secondary_speed_z 0
-set g_balance_fireball_secondary_spread 0
-set g_balance_fireball_switchdelay_drop 0.15
-set g_balance_fireball_switchdelay_raise 0.15
-// }}}
diff --git a/balanceXDF.cfg b/balanceXDF.cfg
deleted file mode 100644 (file)
index 4e06315..0000000
+++ /dev/null
@@ -1,707 +0,0 @@
-g_mod_balance XDF
-
-// {{{ starting gear
-set g_start_weapon_laser 0 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_shotgun 0 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_uzi 1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
-set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
-set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
-set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
-set g_start_weapon_hagar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" // UNTIL IT CAN BE REMOVED FROM CODE
-set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
-set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
-set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
-set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
-set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
-set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
-set g_balance_health_start 100
-set g_balance_armor_start 0
-set g_start_ammo_shells 15
-set g_start_ammo_nails 0
-set g_start_ammo_rockets 0
-set g_start_ammo_cells 0
-set g_start_ammo_fuel 0
-set g_warmup_start_health 100 "starting values when being in warmup-stage"
-set g_warmup_start_armor 100 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_nails 160 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_rockets 80 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_cells 90 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage"
-set g_lms_start_health 200
-set g_lms_start_armor 200
-set g_lms_start_ammo_shells 60
-set g_lms_start_ammo_nails 320
-set g_lms_start_ammo_rockets 160
-set g_lms_start_ammo_cells 180
-set g_lms_start_ammo_fuel 0
-set g_balance_nix_roundtime 25
-set g_balance_nix_incrtime 1.6
-set g_balance_nix_ammo_shells 60
-set g_balance_nix_ammo_nails 320
-set g_balance_nix_ammo_rockets 160
-set g_balance_nix_ammo_cells 180
-set g_balance_nix_ammo_fuel 0
-set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume
-set g_balance_nix_ammoincr_nails 6
-set g_balance_nix_ammoincr_rockets 2
-set g_balance_nix_ammoincr_cells 2
-set g_balance_nix_ammoincr_fuel 2
-// }}}
-
-// {{{ pickup items
-set g_pickup_ammo_anyway 1
-set g_pickup_weapons_anyway 1
-set g_pickup_shells 15
-set g_pickup_shells_weapon 15
-set g_pickup_shells_max 60
-set g_pickup_nails 80
-set g_pickup_nails_weapon 80
-set g_pickup_nails_max 320
-set g_pickup_rockets 40
-set g_pickup_rockets_weapon 40
-set g_pickup_rockets_max 160
-set g_pickup_cells 30
-set g_pickup_cells_weapon 30
-set g_pickup_cells_max 180
-set g_pickup_fuel 50
-set g_pickup_fuel_weapon 50
-set g_pickup_fuel_jetpack 100
-set g_pickup_fuel_max 100
-set g_pickup_armorsmall 5
-set g_pickup_armorsmall_max 200
-set g_pickup_armorsmall_anyway 1
-set g_pickup_armormedium 25
-set g_pickup_armormedium_max 200
-set g_pickup_armormedium_anyway 1
-set g_pickup_armorbig 50
-set g_pickup_armorbig_max 200
-set g_pickup_armorbig_anyway 1
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 200
-set g_pickup_armorlarge_anyway 1
-set g_pickup_healthsmall 5
-set g_pickup_healthsmall_max 200
-set g_pickup_healthsmall_anyway 1
-set g_pickup_healthmedium 25
-set g_pickup_healthmedium_max 200
-set g_pickup_healthmedium_anyway 1
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 200
-set g_pickup_healthlarge_anyway 1
-set g_pickup_healthmega 100
-set g_pickup_healthmega_max 200
-set g_pickup_healthmega_anyway 1
-set g_pickup_respawntime_short 0.1
-set g_pickup_respawntime_medium 0.1
-set g_pickup_respawntime_long 0.1
-set g_pickup_respawntime_powerup 0.1
-set g_pickup_respawntime_weapon 0.1
-set g_pickup_respawntime_superweapon 0.1
-set g_pickup_respawntime_ammo 0.1
-set g_pickup_respawntimejitter_short 0
-set g_pickup_respawntimejitter_medium 0
-set g_pickup_respawntimejitter_long 0
-set g_pickup_respawntimejitter_powerup 30
-set g_pickup_respawntimejitter_weapon 0
-set g_pickup_respawntimejitter_superweapon 10
-set g_pickup_respawntimejitter_ammo 0
-// }}}
-
-// {{{ regen/rot
-set g_balance_health_regen 0.08
-set g_balance_health_regenlinear 0.5
-set g_balance_pause_health_regen 5
-set g_balance_pause_health_regen_spawn 0
-set g_balance_health_rot 0.04
-set g_balance_health_rotlinear 0.75
-set g_balance_pause_health_rot 1
-set g_balance_pause_health_rot_spawn 5
-set g_balance_health_regenstable 100
-set g_balance_health_rotstable 100
-set g_balance_health_limit 999
-set g_balance_armor_regen 0
-set g_balance_armor_regenlinear 0
-set g_balance_armor_rot 0.04
-set g_balance_armor_rotlinear 0.75
-set g_balance_pause_armor_rot 1
-set g_balance_pause_armor_rot_spawn 5
-set g_balance_armor_regenstable 100
-set g_balance_armor_rotstable 100
-set g_balance_armor_limit 999
-set g_balance_armor_blockpercent 0.6
-set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)"
-set g_balance_fuel_regenlinear 0
-set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter
-set g_balance_fuel_rot 0.05
-set g_balance_fuel_rotlinear 0
-set g_balance_pause_fuel_rot 5
-set g_balance_pause_fuel_rot_spawn 10
-set g_balance_fuel_regenstable 50
-set g_balance_fuel_rotstable 100
-set g_balance_fuel_limit 999
-// }}}
-
-// {{{ misc
-set g_balance_selfdamagepercent 0
-set g_weaponspeedfactor 1 "weapon projectile speed multiplier"
-set g_weaponratefactor 1 "weapon fire rate multiplier"
-set g_weapondamagefactor 1 "weapon damage multiplier"
-set g_weaponforcefactor 1 "weapon force multiplier"
-set g_weaponspreadfactor 1 "weapon spread multiplier"
-set g_balance_firetransfer_time 0.9
-set g_balance_firetransfer_damage 0.8
-set g_throughfloor_damage 0.4
-set g_throughfloor_force 0.7
-set g_projectiles_damage 2
-// possible values:
-// -2: absolutely no damage to projectiles (no exceptions)
-// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines)
-// 0: only damage from contents (lava/slime) or exceptions
-// 1: only self damage or damage from contents or exceptions
-// 2: allow all damage to projectiles normally
-set g_projectiles_keep_owner 0
-set g_projectiles_newton_style 2
-// possible values:
-// 0: absolute velocity projectiles (like Quake)
-// 1: relative velocity projectiles, "Newtonian" (like Tribes 2)
-// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard)
-set g_projectiles_newton_style_2_minfactor 0.8
-set g_projectiles_newton_style_2_maxfactor 1.5
-set g_projectiles_spread_style 7
-// possible values:
-// 0: forward + solid sphere (like Quake) - varies velocity
-// 1: forward + flattened solid sphere
-// 2: forward + solid circle
-// 3: forward + normal distribution 3D - varies velocity
-// 4: forward + normal distribution on a plane
-// 5: forward + circle with 1-r falloff
-// 6: forward + circle with 1-r^2 falloff
-// 7: forward + circle with (1-r)(2-r) falloff
-set g_balance_falldamage_deadminspeed 250
-set g_balance_falldamage_minspeed 900
-set g_balance_falldamage_factor 0.20
-set g_balance_falldamage_maxdamage 40
-set g_balance_damagepush_speedfactor 2.5
-set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage
-set g_balance_contents_drowndelay 10 // time under water before a player begins drowning
-set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning
-set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava
-set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime
-set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime
-set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap"
-// }}}
-
-// {{{ powerups
-set g_balance_powerup_invincible_takedamage 0.25 // only 1/4th damage is taken
-set g_balance_powerup_invincible_time 999
-set g_balance_powerup_strength_damage 3
-set g_balance_powerup_strength_force 3
-set g_balance_powerup_strength_time 999
-set g_balance_powerup_strength_selfdamage 1.5
-set g_balance_powerup_strength_selfforce 1.5
-set g_balance_superweapons_time 30
-// }}}
-
-// {{{ jetpack/hook
-set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack"
-set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction"
-set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)"
-set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction"
-set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction"
-set g_jetpack_fuel 8 "fuel per second for jetpack"
-set g_jetpack_attenuation 2 "jetpack sound attenuation"
-
-set g_grappling_hook_tarzan 2 // 2: can also pull players
-set g_balance_grapplehook_speed_fly 1800
-set g_balance_grapplehook_speed_pull 2000
-set g_balance_grapplehook_force_rubber 2000
-set g_balance_grapplehook_force_rubber_overstretch 1000
-set g_balance_grapplehook_length_min 50
-set g_balance_grapplehook_stretch 50
-set g_balance_grapplehook_airfriction 0.2
-set g_balance_grapplehook_health 130
-set g_balance_grapplehook_damagedbycontents 0
-// }}}
-
-// {{{ weapon properties
-// {{{ laser
-set g_balance_laser_primary_damage 25
-set g_balance_laser_primary_edgedamage 12.5
-set g_balance_laser_primary_force 250
-set g_balance_laser_primary_radius 70
-set g_balance_laser_primary_speed 6000
-set g_balance_laser_primary_spread 0
-set g_balance_laser_primary_refire 0.7
-set g_balance_laser_primary_animtime 0.3
-set g_balance_laser_primary_lifetime 5
-set g_balance_laser_primary_shotangle 0
-set g_balance_laser_primary_delay 0
-set g_balance_laser_primary_gauntlet 0
-set g_balance_laser_primary_force_zscale 1.5
-set g_balance_laser_primary_force_velocitybias 0
-set g_balance_laser_primary_force_other_scale 1
-set g_balance_laser_secondary 0 // when 1, a secondary laser mode exists
-set g_balance_laser_secondary_damage 25
-set g_balance_laser_secondary_edgedamage 12.5
-set g_balance_laser_secondary_force 400
-set g_balance_laser_secondary_radius 70
-set g_balance_laser_secondary_speed 12000
-set g_balance_laser_secondary_spread 0
-set g_balance_laser_secondary_refire 0.7
-set g_balance_laser_secondary_animtime 0.3
-set g_balance_laser_secondary_lifetime 5
-set g_balance_laser_secondary_shotangle -90
-set g_balance_laser_secondary_delay 0
-set g_balance_laser_secondary_gauntlet 0
-set g_balance_laser_secondary_force_zscale 1.25
-set g_balance_laser_secondary_force_velocitybias 0
-set g_balance_laser_secondary_force_other_scale 1
-set g_balance_laser_switchdelay_drop 0
-set g_balance_laser_switchdelay_raise 0
-set g_balance_laser_reload_ammo 0 //default: 6
-set g_balance_laser_reload_time 2
-// }}}
-// {{{ shotgun
-set g_balance_shotgun_primary_bullets 14
-set g_balance_shotgun_primary_damage 4
-set g_balance_shotgun_primary_force 15
-set g_balance_shotgun_primary_spread 0.12
-set g_balance_shotgun_primary_refire 0.75
-set g_balance_shotgun_primary_animtime 0.2
-set g_balance_shotgun_primary_ammo 1
-set g_balance_shotgun_primary_speed 8000
-set g_balance_shotgun_primary_bulletconstant 75 // 3.8qu
-set g_balance_shotgun_secondary 1
-set g_balance_shotgun_secondary_melee_delay 0.25 // 0.35 was too slow
-set g_balance_shotgun_secondary_melee_range 120
-set g_balance_shotgun_secondary_melee_swing_side 120
-set g_balance_shotgun_secondary_melee_swing_up 30
-set g_balance_shotgun_secondary_melee_time 0.15
-set g_balance_shotgun_secondary_melee_traces 10
-set g_balance_shotgun_secondary_melee_no_doubleslap 1
-set g_balance_shotgun_secondary_melee_nonplayerdamage 40
-set g_balance_shotgun_secondary_melee_multihit 1
-set g_balance_shotgun_secondary_damage 80
-set g_balance_shotgun_secondary_force 200
-set g_balance_shotgun_secondary_refire 1.25
-set g_balance_shotgun_secondary_animtime 1
-set g_balance_shotgun_switchdelay_drop 0
-set g_balance_shotgun_switchdelay_raise 0
-set g_balance_shotgun_reload_ammo 0 //default: 5
-set g_balance_shotgun_reload_time 2
-// }}}
-// {{{ uzi
-set g_balance_uzi_mode 1                               // Activates varible spread for sustained & burst mode secondary
-set g_balance_uzi_spread_min 0
-set g_balance_uzi_spread_max 0
-set g_balance_uzi_spread_add 0
-
-set g_balance_uzi_burst 3                              // # of bullets in a burst (if set to 2 or more)
-set g_balance_uzi_burst_animtime 0.3
-set g_balance_uzi_burst_refire 0.06            // refire between burst bullets
-set g_balance_uzi_burst_refire2 0.45   // refire after burst
-set g_balance_uzi_burst_spread 0.03
-set g_balance_uzi_burst_damage 25
-set g_balance_uzi_burst_force 20
-set g_balance_uzi_burst_ammo 3
-
-set g_balance_uzi_first 1
-set g_balance_uzi_first_damage 14
-set g_balance_uzi_first_force 5
-set g_balance_uzi_first_spread 0.03
-set g_balance_uzi_first_refire 0.4
-set g_balance_uzi_first_ammo 1
-
-set g_balance_uzi_sustained_damage 12
-set g_balance_uzi_sustained_force 5
-set g_balance_uzi_sustained_spread 0
-set g_balance_uzi_sustained_refire 0.1
-set g_balance_uzi_sustained_ammo 1
-
-set g_balance_uzi_speed 18000
-set g_balance_uzi_bulletconstant 115 // 13.1qu
-
-set g_balance_uzi_switchdelay_drop 0
-set g_balance_uzi_switchdelay_raise 0
-
-set g_balance_uzi_reload_ammo 0 //default: 30
-set g_balance_uzi_reload_time 2
-// }}}
-// {{{ mortar
-set g_balance_grenadelauncher_primary_type 0
-set g_balance_grenadelauncher_primary_damage 50
-set g_balance_grenadelauncher_primary_edgedamage 25
-set g_balance_grenadelauncher_primary_force 250
-set g_balance_grenadelauncher_primary_radius 100
-set g_balance_grenadelauncher_primary_speed 2000
-set g_balance_grenadelauncher_primary_speed_up 200
-set g_balance_grenadelauncher_primary_speed_z 0
-set g_balance_grenadelauncher_primary_spread 0
-set g_balance_grenadelauncher_primary_lifetime 5
-set g_balance_grenadelauncher_primary_lifetime2 1
-set g_balance_grenadelauncher_primary_refire 0.7
-set g_balance_grenadelauncher_primary_animtime 0.3
-set g_balance_grenadelauncher_primary_ammo 2
-set g_balance_grenadelauncher_primary_health 0
-set g_balance_grenadelauncher_primary_damageforcescale 0
-set g_balance_grenadelauncher_primary_remote_minbouncecnt 0
-
-set g_balance_grenadelauncher_secondary_type 1
-set g_balance_grenadelauncher_secondary_damage 60
-set g_balance_grenadelauncher_secondary_edgedamage 30
-set g_balance_grenadelauncher_secondary_force 300
-set g_balance_grenadelauncher_secondary_radius 200
-set g_balance_grenadelauncher_secondary_speed 800
-set g_balance_grenadelauncher_secondary_speed_up 0
-set g_balance_grenadelauncher_secondary_speed_z 200
-set g_balance_grenadelauncher_secondary_spread 0
-set g_balance_grenadelauncher_secondary_lifetime 8
-set g_balance_grenadelauncher_secondary_lifetime_bounce 0.5
-set g_balance_grenadelauncher_secondary_lifetime_stick 0
-set g_balance_grenadelauncher_secondary_refire 0.7
-set g_balance_grenadelauncher_secondary_animtime 0.5
-set g_balance_grenadelauncher_secondary_ammo 2
-set g_balance_grenadelauncher_secondary_health 0
-set g_balance_grenadelauncher_secondary_damageforcescale 0
-set g_balance_grenadelauncher_secondary_remote_detonateprimary 0
-
-set g_balance_grenadelauncher_bouncefactor 0.5
-set g_balance_grenadelauncher_bouncestop 0.075
-
-set g_balance_grenadelauncher_switchdelay_drop 0
-set g_balance_grenadelauncher_switchdelay_raise 0
-
-set g_balance_grenadelauncher_reload_ammo 0 //default: 12
-set g_balance_grenadelauncher_reload_time 2
-// }}}
-// {{{ electro
-set g_balance_electro_lightning 0
-set g_balance_electro_primary_damage 55
-set g_balance_electro_primary_edgedamage 27.5
-set g_balance_electro_primary_force 200
-set g_balance_electro_primary_force_up 0
-set g_balance_electro_primary_radius 100
-set g_balance_electro_primary_comboradius 150
-set g_balance_electro_primary_speed 2500
-set g_balance_electro_primary_spread 0
-set g_balance_electro_primary_lifetime 5
-set g_balance_electro_primary_refire 0.6
-set g_balance_electro_primary_animtime 0.1
-set g_balance_electro_primary_ammo 4
-set g_balance_electro_primary_range 0
-set g_balance_electro_primary_falloff_mindist 255 // 0.3 * radius
-set g_balance_electro_primary_falloff_maxdist 850
-set g_balance_electro_primary_falloff_halflifedist 425
-set g_balance_electro_secondary_damage 40
-set g_balance_electro_secondary_edgedamage 20
-set g_balance_electro_secondary_force 200
-set g_balance_electro_secondary_radius 150
-set g_balance_electro_secondary_speed 900
-set g_balance_electro_secondary_speed_up 200
-set g_balance_electro_secondary_speed_z 0
-set g_balance_electro_secondary_spread 0.05
-set g_balance_electro_secondary_lifetime 3
-set g_balance_electro_secondary_refire 0.2
-set g_balance_electro_secondary_refire2 1.5
-set g_balance_electro_secondary_animtime 0.2
-set g_balance_electro_secondary_ammo 2
-set g_balance_electro_secondary_health 5
-set g_balance_electro_secondary_damageforcescale 4
-set g_balance_electro_secondary_damagedbycontents 1
-set g_balance_electro_secondary_count 3
-set g_balance_electro_secondary_bouncefactor 0.4
-set g_balance_electro_secondary_bouncestop 0.05
-set g_balance_electro_combo_damage 40
-set g_balance_electro_combo_edgedamage 20
-set g_balance_electro_combo_force 120
-set g_balance_electro_combo_radius 175
-set g_balance_electro_combo_comboradius 275
-set g_balance_electro_combo_speed 2000
-set g_balance_electro_combo_safeammocheck 1
-set g_balance_electro_switchdelay_drop 0
-set g_balance_electro_switchdelay_raise 0
-set g_balance_electro_reload_ammo 0 //default: 20
-set g_balance_electro_reload_time 2
-// }}}
-// {{{ crylink
-set g_balance_crylink_primary_damage 12
-set g_balance_crylink_primary_edgedamage 6
-set g_balance_crylink_primary_force -50
-set g_balance_crylink_primary_radius 80
-set g_balance_crylink_primary_speed 2000
-set g_balance_crylink_primary_spread 0.08
-set g_balance_crylink_primary_shots 6
-set g_balance_crylink_primary_bounces 1
-set g_balance_crylink_primary_refire 0.7
-set g_balance_crylink_primary_animtime 0.3
-set g_balance_crylink_primary_ammo 3
-set g_balance_crylink_primary_bouncedamagefactor 0.5
-set g_balance_crylink_primary_joindelay 0.1
-set g_balance_crylink_primary_joinspread 0.2
-set g_balance_crylink_primary_joinexplode 1
-set g_balance_crylink_primary_joinexplode_damage 0
-set g_balance_crylink_primary_joinexplode_edgedamage 0
-set g_balance_crylink_primary_joinexplode_radius 0
-set g_balance_crylink_primary_joinexplode_force 0
-set g_balance_crylink_primary_linkexplode 1
-
-set g_balance_crylink_primary_middle_lifetime 5 // range: 35000 full, fades to 70000
-set g_balance_crylink_primary_middle_fadetime 5
-set g_balance_crylink_primary_other_lifetime 5
-set g_balance_crylink_primary_other_fadetime 5
-
-set g_balance_crylink_secondary 1
-set g_balance_crylink_secondary_damage 10
-set g_balance_crylink_secondary_edgedamage 5
-set g_balance_crylink_secondary_force -150
-set g_balance_crylink_secondary_radius 100
-set g_balance_crylink_secondary_speed 3000
-set g_balance_crylink_secondary_spread 0.01
-set g_balance_crylink_secondary_spreadtype 1
-set g_balance_crylink_secondary_shots 5
-set g_balance_crylink_secondary_bounces 0
-set g_balance_crylink_secondary_refire 0.7
-set g_balance_crylink_secondary_animtime 0.2
-set g_balance_crylink_secondary_ammo 2
-set g_balance_crylink_secondary_bouncedamagefactor 0.5
-set g_balance_crylink_secondary_joindelay 0
-set g_balance_crylink_secondary_joinspread 0
-set g_balance_crylink_secondary_joinexplode 0
-set g_balance_crylink_secondary_joinexplode_damage 0
-set g_balance_crylink_secondary_joinexplode_edgedamage 0
-set g_balance_crylink_secondary_joinexplode_radius 0
-set g_balance_crylink_secondary_joinexplode_force 0
-set g_balance_crylink_secondary_linkexplode 1
-
-set g_balance_crylink_secondary_middle_lifetime 5 // range: 35000 full, fades to 70000
-set g_balance_crylink_secondary_middle_fadetime 5
-set g_balance_crylink_secondary_line_lifetime 5
-set g_balance_crylink_secondary_line_fadetime 5
-
-set g_balance_crylink_switchdelay_drop 0
-set g_balance_crylink_switchdelay_raise 0
-
-set g_balance_crylink_reload_ammo 0 //default: 10
-set g_balance_crylink_reload_time 2
-// }}}
-// {{{ nex
-set g_balance_nex_primary_damage 90
-set g_balance_nex_primary_force 400
-set g_balance_nex_primary_refire 1.5
-set g_balance_nex_primary_animtime 0.4
-set g_balance_nex_primary_ammo 6
-set g_balance_nex_primary_damagefalloff_mindist 0 // 1000    For tZork ;3
-set g_balance_nex_primary_damagefalloff_maxdist 0 // 3000
-set g_balance_nex_primary_damagefalloff_halflife 0 // 1500
-set g_balance_nex_primary_damagefalloff_forcehalflife 0 // 1500
-
-set g_balance_nex_secondary 0
-set g_balance_nex_secondary_charge 0
-set g_balance_nex_secondary_charge_rate 0.1
-set g_balance_nex_secondary_chargepool 0
-set g_balance_nex_secondary_chargepool_regen 0.15
-set g_balance_nex_secondary_chargepool_pause_regen 1
-set g_balance_nex_secondary_chargepool_pause_health_regen 1
-set g_balance_nex_secondary_damage 0
-set g_balance_nex_secondary_force 0
-set g_balance_nex_secondary_refire 0
-set g_balance_nex_secondary_animtime 0
-set g_balance_nex_secondary_ammo 2
-set g_balance_nex_secondary_damagefalloff_mindist 0
-set g_balance_nex_secondary_damagefalloff_maxdist 0
-set g_balance_nex_secondary_damagefalloff_halflife 0
-set g_balance_nex_secondary_damagefalloff_forcehalflife 0
-
-set g_balance_nex_charge 1
-set g_balance_nex_charge_mindmg 40
-set g_balance_nex_charge_start 0.5
-set g_balance_nex_charge_rate 0.4
-set g_balance_nex_charge_animlimit 0.5
-set g_balance_nex_charge_limit 1
-set g_balance_nex_charge_rot_rate 0
-set g_balance_nex_charge_rot_pause 0 // Dont rot down untill this long after release of charge button
-set g_balance_nex_charge_shot_multiplier 0
-set g_balance_nex_charge_velocity_rate 0
-set g_balance_nex_charge_minspeed 400
-set g_balance_nex_charge_maxspeed 800
-
-set g_balance_nex_switchdelay_drop 0
-set g_balance_nex_switchdelay_raise 0
-
-set g_balance_nex_reload_ammo 0 //default: 25
-set g_balance_nex_reload_time 2
-// }}}
-// {{{ minstanex
-set g_balance_minstanex_refire 1
-set g_balance_minstanex_animtime 0.3
-set g_balance_minstanex_ammo 10
-set g_balance_minstanex_laser_ammo 0
-set g_balance_minstanex_laser_animtime 0.3
-set g_balance_minstanex_laser_refire 0.7
-set g_balance_minstanex_switchdelay_drop 0
-set g_balance_minstanex_switchdelay_raise 0
-set g_balance_minstanex_reload_ammo 0 //default: 50
-set g_balance_minstanex_reload_time 2
-// }}}
-// {{{ hagar
-set g_balance_hagar_primary_damage 25
-set g_balance_hagar_primary_edgedamage 12.5
-set g_balance_hagar_primary_force 92
-set g_balance_hagar_primary_health 15
-set g_balance_hagar_primary_damageforcescale 0
-set g_balance_hagar_primary_radius 25
-set g_balance_hagar_primary_spread 0.03
-set g_balance_hagar_primary_speed 2000
-set g_balance_hagar_primary_lifetime 5
-set g_balance_hagar_primary_refire 0.11
-set g_balance_hagar_primary_ammo 1
-set g_balance_hagar_secondary 0
-set g_balance_hagar_secondary_load 1
-set g_balance_hagar_secondary_load_speed 0.5
-set g_balance_hagar_secondary_load_spread 0.075
-set g_balance_hagar_secondary_load_spread_bias 0.5
-set g_balance_hagar_secondary_load_max 4
-set g_balance_hagar_secondary_load_hold 4
-set g_balance_hagar_secondary_load_releasedeath 0
-set g_balance_hagar_secondary_load_abort 0
-set g_balance_hagar_secondary_load_linkexplode 0
-set g_balance_hagar_secondary_load_animtime 0.2
-set g_balance_hagar_secondary_damage 40
-set g_balance_hagar_secondary_edgedamage 20
-set g_balance_hagar_secondary_force 75
-set g_balance_hagar_secondary_health 15
-set g_balance_hagar_secondary_damageforcescale 0
-set g_balance_hagar_secondary_radius 80
-set g_balance_hagar_secondary_spread 0.05
-set g_balance_hagar_secondary_speed 2000
-set g_balance_hagar_secondary_lifetime_min 10
-set g_balance_hagar_secondary_lifetime_rand 0
-set g_balance_hagar_secondary_refire 0.5
-set g_balance_hagar_secondary_ammo 1
-set g_balance_hagar_switchdelay_drop 0
-set g_balance_hagar_switchdelay_raise 0
-set g_balance_hagar_reload_ammo 0 //default: 25
-set g_balance_hagar_reload_time 2
-// }}}
-// {{{ rocketlauncher
-set g_balance_rocketlauncher_damage 80
-set g_balance_rocketlauncher_edgedamage 40
-set g_balance_rocketlauncher_force 350
-set g_balance_rocketlauncher_radius 110
-set g_balance_rocketlauncher_speed 1000
-set g_balance_rocketlauncher_speedaccel 0
-set g_balance_rocketlauncher_speedstart 1000
-set g_balance_rocketlauncher_lifetime 100
-set g_balance_rocketlauncher_refire 0.9
-set g_balance_rocketlauncher_animtime 0.7
-set g_balance_rocketlauncher_ammo 4
-set g_balance_rocketlauncher_health 0 // 30 // 5 hitpoints above maximum laser value -- this way lasers can't blow it up, but grenadelauncher still can most the time.
-set g_balance_rocketlauncher_damageforcescale 0 // low damage force scale so that it can still be affected by other hits, but not so much that it does a 90 degree turn
-set g_balance_rocketlauncher_detonatedelay 999 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time
-set g_balance_rocketlauncher_guiderate 0 // max degrees per second
-set g_balance_rocketlauncher_guideratedelay 0.01 // immediate
-set g_balance_rocketlauncher_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic)
-set g_balance_rocketlauncher_guidedelay 0.2 // delay before guiding kicks in
-set g_balance_rocketlauncher_guidestop 1 // stop guiding when firing again
-set g_balance_rocketlauncher_remote_damage 70
-set g_balance_rocketlauncher_remote_edgedamage 35
-set g_balance_rocketlauncher_remote_radius 110
-set g_balance_rocketlauncher_remote_force 350
-set g_balance_rocketlauncher_switchdelay_drop 0
-set g_balance_rocketlauncher_switchdelay_raise 0
-set g_balance_rocketlauncher_reload_ammo 0 //default: 25
-set g_balance_rocketlauncher_reload_time 2
-// }}}
-// {{{ porto
-set g_balance_porto_primary_refire 1.5
-set g_balance_porto_primary_animtime 0.3
-set g_balance_porto_primary_speed 5000
-set g_balance_porto_primary_lifetime 5
-set g_balance_porto_secondary 1
-set g_balance_porto_secondary_refire 1.5
-set g_balance_porto_secondary_animtime 0.3
-set g_balance_porto_secondary_speed 1000
-set g_balance_porto_secondary_lifetime 5
-set g_balance_porto_switchdelay_drop 0
-set g_balance_porto_switchdelay_raise 0
-set g_balance_portal_health 200 // these get recharged whenever the portal is used
-set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used
-// }}}
-// {{{ hook
-set g_balance_hook_primary_fuel 5 // hook monkeys set 0
-set g_balance_hook_primary_refire 0 // hook monkeys set 0
-set g_balance_hook_primary_animtime 0.3 // good shoot anim
-set g_balance_hook_primary_hooked_time_max 0 // infinite
-set g_balance_hook_primary_hooked_time_free 2 // 2s being hooked are free
-set g_balance_hook_primary_hooked_fuel 5 // fuel per second hooked
-set g_balance_hook_secondary_damage 25 // not much
-set g_balance_hook_secondary_edgedamage 5 // not much
-set g_balance_hook_secondary_radius 500 // LOTS
-set g_balance_hook_secondary_force -2000 // LOTS
-set g_balance_hook_secondary_ammo 50 // a whole pack
-set g_balance_hook_secondary_lifetime 5 // infinite
-set g_balance_hook_secondary_speed 0 // not much throwing
-set g_balance_hook_secondary_gravity 5 // fast falling
-set g_balance_hook_secondary_refire 3 // don't drop too many bombs...
-set g_balance_hook_secondary_animtime 0.3 // good shoot anim
-set g_balance_hook_secondary_power 3 // effect behaves like a square function
-set g_balance_hook_secondary_duration 1.5 // effect runs for three seconds
-set g_balance_hook_secondary_health 15
-set g_balance_hook_secondary_damageforcescale 0
-set g_balance_hook_switchdelay_drop 0
-set g_balance_hook_switchdelay_raise 0
-// }}}
-// {{{ tuba
-set g_balance_tuba_refire 0.05
-set g_balance_tuba_animtime 0.05
-set g_balance_tuba_attenuation 0.5
-set g_balance_tuba_volume 1
-set g_balance_tuba_fadetime 0.25
-set g_balance_tuba_damage 5
-set g_balance_tuba_edgedamage 0
-set g_balance_tuba_radius 200
-set g_balance_tuba_force 40
-set g_balance_tuba_pitchstep 6
-set g_balance_tuba_switchdelay_drop 0
-set g_balance_tuba_switchdelay_raise 0
-// }}}
-// {{{ fireball // this is a superweapon -- lets make it behave as one.
-set g_balance_fireball_primary_animtime 0.2
-set g_balance_fireball_primary_bfgdamage 100
-set g_balance_fireball_primary_bfgforce 0
-set g_balance_fireball_primary_bfgradius 1000
-set g_balance_fireball_primary_damage 200
-set g_balance_fireball_primary_damageforcescale 0
-set g_balance_fireball_primary_edgedamage 50
-set g_balance_fireball_primary_force 600
-set g_balance_fireball_primary_health 0
-set g_balance_fireball_primary_laserburntime 0.5
-set g_balance_fireball_primary_laserdamage 80
-set g_balance_fireball_primary_laseredgedamage 20
-set g_balance_fireball_primary_laserradius 256
-set g_balance_fireball_primary_lifetime 15
-set g_balance_fireball_primary_radius 200
-set g_balance_fireball_primary_refire 2
-set g_balance_fireball_primary_refire2 0
-set g_balance_fireball_primary_speed 1200
-set g_balance_fireball_primary_spread 0
-set g_balance_fireball_secondary_animtime 0.3
-set g_balance_fireball_secondary_damage 40
-set g_balance_fireball_secondary_damageforcescale 4
-set g_balance_fireball_secondary_damagetime 5
-set g_balance_fireball_secondary_force 100
-set g_balance_fireball_secondary_laserburntime 0.5
-set g_balance_fireball_secondary_laserdamage 50
-set g_balance_fireball_secondary_laseredgedamage 20
-set g_balance_fireball_secondary_laserradius 110
-set g_balance_fireball_secondary_lifetime 7
-set g_balance_fireball_secondary_refire 1.5
-set g_balance_fireball_secondary_speed 900
-set g_balance_fireball_secondary_speed_up 100
-set g_balance_fireball_secondary_speed_z 0
-set g_balance_fireball_secondary_spread 0
-set g_balance_fireball_switchdelay_drop 0
-set g_balance_fireball_switchdelay_raise 0
-// }}}
diff --git a/balanceXPM.cfg b/balanceXPM.cfg
deleted file mode 100644 (file)
index 8c17147..0000000
+++ /dev/null
@@ -1,707 +0,0 @@
-g_mod_balance XPM
-
-// {{{ starting gear
-set g_start_weapon_laser -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_shotgun -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_uzi -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_hagar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms" // UNTIL IT CAN BE REMOVED FROM CODE
-set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
-set g_balance_health_start 100
-set g_balance_armor_start 0
-set g_start_ammo_shells 15
-set g_start_ammo_nails 0
-set g_start_ammo_rockets 0
-set g_start_ammo_cells 0
-set g_start_ammo_fuel 0
-set g_warmup_start_health 100 "starting values when being in warmup-stage"
-set g_warmup_start_armor 100 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_nails 160 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_rockets 80 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_cells 90 "starting values when being in warmup-stage"
-set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage"
-set g_lms_start_health 200
-set g_lms_start_armor 200
-set g_lms_start_ammo_shells 60
-set g_lms_start_ammo_nails 320
-set g_lms_start_ammo_rockets 160
-set g_lms_start_ammo_cells 180
-set g_lms_start_ammo_fuel 0
-set g_balance_nix_roundtime 25
-set g_balance_nix_incrtime 1.6
-set g_balance_nix_ammo_shells 60
-set g_balance_nix_ammo_nails 320
-set g_balance_nix_ammo_rockets 160
-set g_balance_nix_ammo_cells 180
-set g_balance_nix_ammo_fuel 0
-set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume
-set g_balance_nix_ammoincr_nails 6
-set g_balance_nix_ammoincr_rockets 2
-set g_balance_nix_ammoincr_cells 2
-set g_balance_nix_ammoincr_fuel 2
-// }}}
-
-// {{{ pickup items
-set g_pickup_ammo_anyway 1
-set g_pickup_weapons_anyway 1
-set g_pickup_shells 15
-set g_pickup_shells_weapon 15
-set g_pickup_shells_max 60
-set g_pickup_nails 80
-set g_pickup_nails_weapon 80
-set g_pickup_nails_max 320
-set g_pickup_rockets 40
-set g_pickup_rockets_weapon 40
-set g_pickup_rockets_max 160
-set g_pickup_cells 30
-set g_pickup_cells_weapon 30
-set g_pickup_cells_max 180
-set g_pickup_fuel 50
-set g_pickup_fuel_weapon 50
-set g_pickup_fuel_jetpack 100
-set g_pickup_fuel_max 100
-set g_pickup_armorsmall 5
-set g_pickup_armorsmall_max 200
-set g_pickup_armorsmall_anyway 0
-set g_pickup_armormedium 25
-set g_pickup_armormedium_max 100
-set g_pickup_armormedium_anyway 0
-set g_pickup_armorbig 50
-set g_pickup_armorbig_max 100
-set g_pickup_armorbig_anyway 0
-set g_pickup_armorlarge 100
-set g_pickup_armorlarge_max 200
-set g_pickup_armorlarge_anyway 0
-set g_pickup_healthsmall 5
-set g_pickup_healthsmall_max 200
-set g_pickup_healthsmall_anyway 0
-set g_pickup_healthmedium 25
-set g_pickup_healthmedium_max 100
-set g_pickup_healthmedium_anyway 0
-set g_pickup_healthlarge 50
-set g_pickup_healthlarge_max 100
-set g_pickup_healthlarge_anyway 0
-set g_pickup_healthmega 100
-set g_pickup_healthmega_max 200
-set g_pickup_healthmega_anyway 0
-set g_pickup_respawntime_short 15
-set g_pickup_respawntime_medium 20
-set g_pickup_respawntime_long 30
-set g_pickup_respawntime_powerup 120
-set g_pickup_respawntime_weapon 10
-set g_pickup_respawntime_superweapon 120
-set g_pickup_respawntime_ammo 15
-set g_pickup_respawntimejitter_short 0
-set g_pickup_respawntimejitter_medium 0
-set g_pickup_respawntimejitter_long 0
-set g_pickup_respawntimejitter_powerup 30
-set g_pickup_respawntimejitter_weapon 0
-set g_pickup_respawntimejitter_superweapon 10
-set g_pickup_respawntimejitter_ammo 0
-// }}}
-
-// {{{ regen/rot
-set g_balance_health_regen 0.08
-set g_balance_health_regenlinear 0.5
-set g_balance_pause_health_regen 5
-set g_balance_pause_health_regen_spawn 0
-set g_balance_health_rot 0.03
-set g_balance_health_rotlinear 0.75
-set g_balance_pause_health_rot 1
-set g_balance_pause_health_rot_spawn 5
-set g_balance_health_regenstable 100
-set g_balance_health_rotstable 100
-set g_balance_health_limit 999
-set g_balance_armor_regen 0
-set g_balance_armor_regenlinear 0
-set g_balance_armor_rot 0.03
-set g_balance_armor_rotlinear 0.75
-set g_balance_pause_armor_rot 1
-set g_balance_pause_armor_rot_spawn 5
-set g_balance_armor_regenstable 100
-set g_balance_armor_rotstable 100
-set g_balance_armor_limit 999
-set g_balance_armor_blockpercent 0.6
-set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)"
-set g_balance_fuel_regenlinear 0
-set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter
-set g_balance_fuel_rot 0.05
-set g_balance_fuel_rotlinear 0
-set g_balance_pause_fuel_rot 5
-set g_balance_pause_fuel_rot_spawn 10
-set g_balance_fuel_regenstable 50
-set g_balance_fuel_rotstable 100
-set g_balance_fuel_limit 999
-// }}}
-
-// {{{ misc
-set g_balance_selfdamagepercent 0.65
-set g_weaponspeedfactor 1 "weapon projectile speed multiplier"
-set g_weaponratefactor 1 "weapon fire rate multiplier"
-set g_weapondamagefactor 1 "weapon damage multiplier"
-set g_weaponforcefactor 1 "weapon force multiplier"
-set g_weaponspreadfactor 1 "weapon spread multiplier"
-set g_balance_firetransfer_time 0.9
-set g_balance_firetransfer_damage 0.8
-set g_throughfloor_damage 0.75
-set g_throughfloor_force 0.75
-set g_projectiles_damage 1
-// possible values:
-// -2: absolutely no damage to projectiles (no exceptions)
-// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines)
-// 0: only damage from contents (lava/slime) or exceptions
-// 1: only self damage or damage from contents or exceptions
-// 2: allow all damage to projectiles normally
-set g_projectiles_keep_owner 0
-set g_projectiles_newton_style 0
-// possible values:
-// 0: absolute velocity projectiles (like Quake)
-// 1: relative velocity projectiles, "Newtonian" (like Tribes 2)
-// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard)
-set g_projectiles_newton_style_2_minfactor 0.8
-set g_projectiles_newton_style_2_maxfactor 1.5
-set g_projectiles_spread_style 7
-// possible values:
-// 0: forward + solid sphere (like Quake) - varies velocity
-// 1: forward + flattened solid sphere
-// 2: forward + solid circle
-// 3: forward + normal distribution 3D - varies velocity
-// 4: forward + normal distribution on a plane
-// 5: forward + circle with 1-r falloff
-// 6: forward + circle with 1-r^2 falloff
-// 7: forward + circle with (1-r)(2-r) falloff
-set g_balance_falldamage_deadminspeed 250
-set g_balance_falldamage_minspeed 900
-set g_balance_falldamage_factor 0.20
-set g_balance_falldamage_maxdamage 40
-set g_balance_damagepush_speedfactor 2.5
-set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage
-set g_balance_contents_drowndelay 10 // time under water before a player begins drowning
-set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning
-set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava
-set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime
-set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime
-set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap"
-// }}}
-
-// {{{ powerups
-set g_balance_powerup_invincible_takedamage 0.25 // only 1/4th damage is taken
-set g_balance_powerup_invincible_time 30
-set g_balance_powerup_strength_damage 3
-set g_balance_powerup_strength_force 3
-set g_balance_powerup_strength_time 30
-set g_balance_powerup_strength_selfdamage 1.5
-set g_balance_powerup_strength_selfforce 1.5
-set g_balance_superweapons_time 30
-// }}}
-
-// {{{ jetpack/hook
-set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack"
-set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction"
-set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)"
-set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction"
-set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction"
-set g_jetpack_fuel 8 "fuel per second for jetpack"
-set g_jetpack_attenuation 2 "jetpack sound attenuation"
-
-set g_grappling_hook_tarzan 2 // 2: can also pull players
-set g_balance_grapplehook_speed_fly 1800
-set g_balance_grapplehook_speed_pull 2000
-set g_balance_grapplehook_force_rubber 2000
-set g_balance_grapplehook_force_rubber_overstretch 1000
-set g_balance_grapplehook_length_min 50
-set g_balance_grapplehook_stretch 50
-set g_balance_grapplehook_airfriction 0.2
-set g_balance_grapplehook_health 50
-set g_balance_grapplehook_damagedbycontents 1
-// }}}
-
-// {{{ weapon properties
-// {{{ laser
-set g_balance_laser_primary_damage 25
-set g_balance_laser_primary_edgedamage 12.5
-set g_balance_laser_primary_force 300
-set g_balance_laser_primary_radius 70
-set g_balance_laser_primary_speed 6000
-set g_balance_laser_primary_spread 0
-set g_balance_laser_primary_refire 0.7
-set g_balance_laser_primary_animtime 0.2
-set g_balance_laser_primary_lifetime 5
-set g_balance_laser_primary_shotangle 0
-set g_balance_laser_primary_delay 0
-set g_balance_laser_primary_gauntlet 0
-set g_balance_laser_primary_force_zscale 1.2
-set g_balance_laser_primary_force_velocitybias 0
-set g_balance_laser_primary_force_other_scale 1
-set g_balance_laser_secondary 0 // when 1, a secondary laser mode exists
-set g_balance_laser_secondary_damage 25
-set g_balance_laser_secondary_edgedamage 12.5
-set g_balance_laser_secondary_force 400
-set g_balance_laser_secondary_radius 70
-set g_balance_laser_secondary_speed 12000
-set g_balance_laser_secondary_spread 0
-set g_balance_laser_secondary_refire 0.7
-set g_balance_laser_secondary_animtime 0.2
-set g_balance_laser_secondary_lifetime 5
-set g_balance_laser_secondary_shotangle -90
-set g_balance_laser_secondary_delay 0
-set g_balance_laser_secondary_gauntlet 0
-set g_balance_laser_secondary_force_zscale 1.25
-set g_balance_laser_secondary_force_velocitybias 0
-set g_balance_laser_secondary_force_other_scale 1
-set g_balance_laser_switchdelay_drop 0.15
-set g_balance_laser_switchdelay_raise 0.15
-set g_balance_laser_reload_ammo 0 //default: 6
-set g_balance_laser_reload_time 2
-// }}}
-// {{{ shotgun
-set g_balance_shotgun_primary_bullets 14
-set g_balance_shotgun_primary_damage 4
-set g_balance_shotgun_primary_force 15
-set g_balance_shotgun_primary_spread 0.12
-set g_balance_shotgun_primary_refire 0.75
-set g_balance_shotgun_primary_animtime 0.2
-set g_balance_shotgun_primary_ammo 1
-set g_balance_shotgun_primary_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
-// }}}
-// {{{ crylink
-set g_balance_crylink_primary_damage 12
-set g_balance_crylink_primary_edgedamage 6
-set g_balance_crylink_primary_force -50
-set g_balance_crylink_primary_radius 80
-set g_balance_crylink_primary_speed 2000
-set g_balance_crylink_primary_spread 0.08
-set g_balance_crylink_primary_shots 6
-set g_balance_crylink_primary_bounces 1
-set g_balance_crylink_primary_refire 0.7
-set g_balance_crylink_primary_animtime 0.3
-set g_balance_crylink_primary_ammo 3
-set g_balance_crylink_primary_bouncedamagefactor 0.5
-set g_balance_crylink_primary_joindelay 0.1
-set g_balance_crylink_primary_joinspread 0.2
-set g_balance_crylink_primary_joinexplode 1
-set g_balance_crylink_primary_joinexplode_damage 0
-set g_balance_crylink_primary_joinexplode_edgedamage 0
-set g_balance_crylink_primary_joinexplode_radius 0
-set g_balance_crylink_primary_joinexplode_force 0
-set g_balance_crylink_primary_linkexplode 1
-
-set g_balance_crylink_primary_middle_lifetime 5 // range: 35000 full, fades to 70000
-set g_balance_crylink_primary_middle_fadetime 5
-set g_balance_crylink_primary_other_lifetime 5
-set g_balance_crylink_primary_other_fadetime 5
-
-set g_balance_crylink_secondary 1
-set g_balance_crylink_secondary_damage 10
-set g_balance_crylink_secondary_edgedamage 5
-set g_balance_crylink_secondary_force -250
-set g_balance_crylink_secondary_radius 100
-set g_balance_crylink_secondary_speed 3000
-set g_balance_crylink_secondary_spread 0.01
-set g_balance_crylink_secondary_spreadtype 1
-set g_balance_crylink_secondary_shots 5
-set g_balance_crylink_secondary_bounces 0
-set g_balance_crylink_secondary_refire 0.7
-set g_balance_crylink_secondary_animtime 0.2
-set g_balance_crylink_secondary_ammo 2
-set g_balance_crylink_secondary_bouncedamagefactor 0.5
-set g_balance_crylink_secondary_joindelay 0
-set g_balance_crylink_secondary_joinspread 0
-set g_balance_crylink_secondary_joinexplode 0
-set g_balance_crylink_secondary_joinexplode_damage 0
-set g_balance_crylink_secondary_joinexplode_edgedamage 0
-set g_balance_crylink_secondary_joinexplode_radius 0
-set g_balance_crylink_secondary_joinexplode_force 0
-set g_balance_crylink_secondary_linkexplode 1
-
-set g_balance_crylink_secondary_middle_lifetime 5 // range: 35000 full, fades to 70000
-set g_balance_crylink_secondary_middle_fadetime 5
-set g_balance_crylink_secondary_line_lifetime 5
-set g_balance_crylink_secondary_line_fadetime 5
-
-set g_balance_crylink_switchdelay_drop 0.2
-set g_balance_crylink_switchdelay_raise 0.2
-
-set g_balance_crylink_reload_ammo 0 //default: 10
-set g_balance_crylink_reload_time 2
-// }}}
-// {{{ nex
-set g_balance_nex_primary_damage 80
-set g_balance_nex_primary_force 400
-set g_balance_nex_primary_refire 1.5
-set g_balance_nex_primary_animtime 0.6
-set g_balance_nex_primary_ammo 6
-set g_balance_nex_primary_damagefalloff_mindist 0 // 1000    For tZork ;3
-set g_balance_nex_primary_damagefalloff_maxdist 0 // 3000
-set g_balance_nex_primary_damagefalloff_halflife 0 // 1500
-set g_balance_nex_primary_damagefalloff_forcehalflife 0 // 1500
-
-set g_balance_nex_secondary 0
-set g_balance_nex_secondary_charge 0
-set g_balance_nex_secondary_charge_rate 0.1
-set g_balance_nex_secondary_chargepool 0
-set g_balance_nex_secondary_chargepool_regen 0.15
-set g_balance_nex_secondary_chargepool_pause_regen 1
-set g_balance_nex_secondary_chargepool_pause_health_regen 1
-set g_balance_nex_secondary_damage 0
-set g_balance_nex_secondary_force 0
-set g_balance_nex_secondary_refire 0
-set g_balance_nex_secondary_animtime 0
-set g_balance_nex_secondary_ammo 2
-set g_balance_nex_secondary_damagefalloff_mindist 0
-set g_balance_nex_secondary_damagefalloff_maxdist 0
-set g_balance_nex_secondary_damagefalloff_halflife 0
-set g_balance_nex_secondary_damagefalloff_forcehalflife 0
-
-set g_balance_nex_charge 1
-set g_balance_nex_charge_mindmg 40
-set g_balance_nex_charge_start 0.5
-set g_balance_nex_charge_rate 0.4
-set g_balance_nex_charge_animlimit 0.5
-set g_balance_nex_charge_limit 1
-set g_balance_nex_charge_rot_rate 0
-set g_balance_nex_charge_rot_pause 0 // Dont rot down until this long after release of charge button
-set g_balance_nex_charge_shot_multiplier 0
-set g_balance_nex_charge_velocity_rate 0
-set g_balance_nex_charge_minspeed 400
-set g_balance_nex_charge_maxspeed 800
-
-set g_balance_nex_switchdelay_drop 0.3
-set g_balance_nex_switchdelay_raise 0.25
-
-set g_balance_nex_reload_ammo 0 //default: 25
-set g_balance_nex_reload_time 2
-// }}}
-// {{{ minstanex
-set g_balance_minstanex_refire 1
-set g_balance_minstanex_animtime 0.3
-set g_balance_minstanex_ammo 10
-set g_balance_minstanex_laser_ammo 0
-set g_balance_minstanex_laser_animtime 0.3
-set g_balance_minstanex_laser_refire 0.7
-set g_balance_minstanex_switchdelay_drop 0.2
-set g_balance_minstanex_switchdelay_raise 0.2
-set g_balance_minstanex_reload_ammo 0 //default: 50
-set g_balance_minstanex_reload_time 2
-// }}}
-// {{{ hagar
-set g_balance_hagar_primary_damage 25
-set g_balance_hagar_primary_edgedamage 12.5
-set g_balance_hagar_primary_force 100
-set g_balance_hagar_primary_health 15
-set g_balance_hagar_primary_damageforcescale 0
-set g_balance_hagar_primary_radius 65
-set g_balance_hagar_primary_spread 0.03
-set g_balance_hagar_primary_speed 2500
-set g_balance_hagar_primary_lifetime 5
-set g_balance_hagar_primary_refire 0.16667 // 6 rockets per second
-set g_balance_hagar_primary_ammo 1
-set g_balance_hagar_secondary 1
-set g_balance_hagar_secondary_load 1
-set g_balance_hagar_secondary_load_speed 0.5
-set g_balance_hagar_secondary_load_spread 0.075
-set g_balance_hagar_secondary_load_spread_bias 0.5
-set g_balance_hagar_secondary_load_max 4
-set g_balance_hagar_secondary_load_hold 4
-set g_balance_hagar_secondary_load_releasedeath 0
-set g_balance_hagar_secondary_load_abort 0
-set g_balance_hagar_secondary_load_linkexplode 0
-set g_balance_hagar_secondary_load_animtime 0.2
-set g_balance_hagar_secondary_damage 40
-set g_balance_hagar_secondary_edgedamage 20
-set g_balance_hagar_secondary_force 75
-set g_balance_hagar_secondary_health 15
-set g_balance_hagar_secondary_damageforcescale 0
-set g_balance_hagar_secondary_radius 80
-set g_balance_hagar_secondary_spread 0.05
-set g_balance_hagar_secondary_speed 2000
-set g_balance_hagar_secondary_lifetime_min 10
-set g_balance_hagar_secondary_lifetime_rand 0
-set g_balance_hagar_secondary_refire 0.5
-set g_balance_hagar_secondary_ammo 1
-set g_balance_hagar_switchdelay_drop 0.2
-set g_balance_hagar_switchdelay_raise 0.2
-set g_balance_hagar_reload_ammo 0 //default: 25
-set g_balance_hagar_reload_time 2
-// }}}
-// {{{ rocketlauncher
-set g_balance_rocketlauncher_damage 70
-set g_balance_rocketlauncher_edgedamage 35
-set g_balance_rocketlauncher_force 450
-set g_balance_rocketlauncher_radius 110
-set g_balance_rocketlauncher_speed 1300
-set g_balance_rocketlauncher_speedaccel 1300
-set g_balance_rocketlauncher_speedstart 1000
-set g_balance_rocketlauncher_lifetime 10
-set g_balance_rocketlauncher_refire 1.2
-set g_balance_rocketlauncher_animtime 0.4
-set g_balance_rocketlauncher_ammo 4
-set g_balance_rocketlauncher_health 30 // 30 // 5 hitpoints above maximum laser value -- this way lasers can't blow it up, but grenadelauncher still can most the time.
-set g_balance_rocketlauncher_damageforcescale 1 // low damage force scale so that it can still be affected by other hits, but not so much that it does a 90 degree turn
-set g_balance_rocketlauncher_detonatedelay 0.02 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time
-set g_balance_rocketlauncher_guiderate 70 // max degrees per second
-set g_balance_rocketlauncher_guideratedelay 0.01 // immediate
-set g_balance_rocketlauncher_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic)
-set g_balance_rocketlauncher_guidedelay 0.2 // delay before guiding kicks in
-set g_balance_rocketlauncher_guidestop 0 // stop guiding when firing again
-set g_balance_rocketlauncher_remote_damage 70
-set g_balance_rocketlauncher_remote_edgedamage 35
-set g_balance_rocketlauncher_remote_radius 110
-set g_balance_rocketlauncher_remote_force 400
-set g_balance_rocketlauncher_switchdelay_drop 0.3
-set g_balance_rocketlauncher_switchdelay_raise 0.2
-set g_balance_rocketlauncher_reload_ammo 0 //default: 25
-set g_balance_rocketlauncher_reload_time 2
-// }}}
-// {{{ porto
-set g_balance_porto_primary_refire 1.5
-set g_balance_porto_primary_animtime 0.3
-set g_balance_porto_primary_speed 1000
-set g_balance_porto_primary_lifetime 5
-set g_balance_porto_secondary 1
-set g_balance_porto_secondary_refire 1.5
-set g_balance_porto_secondary_animtime 0.3
-set g_balance_porto_secondary_speed 1000
-set g_balance_porto_secondary_lifetime 5
-set g_balance_porto_switchdelay_drop 0.2
-set g_balance_porto_switchdelay_raise 0.2
-set g_balance_portal_health 200 // these get recharged whenever the portal is used
-set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used
-// }}}
-// {{{ hook
-set g_balance_hook_primary_fuel 5 // hook monkeys set 0
-set g_balance_hook_primary_refire 0 // 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
-// }}}
index cdf3f201354aaebeb4351ee1d83f4af9f6d44490..d4fb252a0df814411752a09b4b6124b1a241d918 100644 (file)
@@ -4,12 +4,12 @@ g_mod_balance Xonotic
 set g_start_weapon_laser -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
 set g_start_weapon_shotgun -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
 set g_start_weapon_uzi -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
-set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
+set g_start_weapon_mortar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
 set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
 set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
 set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
 set g_start_weapon_hagar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" // UNTIL IT CAN BE REMOVED FROM CODE
-set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
+set g_start_weapon_devastator -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
 set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
 set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
 set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
@@ -227,6 +227,21 @@ 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
@@ -238,11 +253,11 @@ set g_balance_laser_primary_animtime 0.2
 set g_balance_laser_primary_lifetime 5
 set g_balance_laser_primary_shotangle 0
 set g_balance_laser_primary_delay 0
-set g_balance_laser_primary_gauntlet 0
 set g_balance_laser_primary_force_zscale 1.2
 set g_balance_laser_primary_force_velocitybias 0
 set g_balance_laser_primary_force_other_scale 1
-set g_balance_laser_secondary 0 // when 1, a secondary laser mode exists
+
+set g_balance_laser_secondary 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
@@ -254,10 +269,39 @@ set g_balance_laser_secondary_animtime 0.2
 set g_balance_laser_secondary_lifetime 5
 set g_balance_laser_secondary_shotangle -90
 set g_balance_laser_secondary_delay 0
-set g_balance_laser_secondary_gauntlet 0
 set g_balance_laser_secondary_force_zscale 1.25
 set g_balance_laser_secondary_force_velocitybias 0
 set g_balance_laser_secondary_force_other_scale 1
+
+set g_balance_laser_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
@@ -330,51 +374,51 @@ 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_mortar_primary_type 0
+set g_balance_mortar_primary_damage 50
+set g_balance_mortar_primary_edgedamage 25
+set g_balance_mortar_primary_force 250
+set g_balance_mortar_primary_radius 120
+set g_balance_mortar_primary_speed 1900
+set g_balance_mortar_primary_speed_up 225
+set g_balance_mortar_primary_speed_z 0
+set g_balance_mortar_primary_spread 0
+set g_balance_mortar_primary_lifetime 5
+set g_balance_mortar_primary_lifetime2 1
+set g_balance_mortar_primary_refire 0.8
+set g_balance_mortar_primary_animtime 0.3
+set g_balance_mortar_primary_ammo 2
+set g_balance_mortar_primary_health 15
+set g_balance_mortar_primary_damageforcescale 0
+set g_balance_mortar_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_mortar_secondary_type 1
+set g_balance_mortar_secondary_damage 60
+set g_balance_mortar_secondary_edgedamage 30
+set g_balance_mortar_secondary_force 250
+set g_balance_mortar_secondary_radius 120
+set g_balance_mortar_secondary_speed 1400
+set g_balance_mortar_secondary_speed_up 150
+set g_balance_mortar_secondary_speed_z 0
+set g_balance_mortar_secondary_spread 0
+set g_balance_mortar_secondary_lifetime 5
+set g_balance_mortar_secondary_lifetime_bounce 0.5
+set g_balance_mortar_secondary_lifetime_stick 0
+set g_balance_mortar_secondary_refire 0.7
+set g_balance_mortar_secondary_animtime 0.3
+set g_balance_mortar_secondary_ammo 2
+set g_balance_mortar_secondary_health 30
+set g_balance_mortar_secondary_damageforcescale 4
+set g_balance_mortar_secondary_remote_detonateprimary 0
 
-set g_balance_grenadelauncher_bouncefactor 0.5
-set g_balance_grenadelauncher_bouncestop 0.075
+set g_balance_mortar_bouncefactor 0.5
+set g_balance_mortar_bouncestop 0.075
 
-set g_balance_grenadelauncher_switchdelay_drop 0.2
-set g_balance_grenadelauncher_switchdelay_raise 0.2
+set g_balance_mortar_switchdelay_drop 0.2
+set g_balance_mortar_switchdelay_raise 0.2
 
-set g_balance_grenadelauncher_reload_ammo 0 //default: 12
-set g_balance_grenadelauncher_reload_time 2
+set g_balance_mortar_reload_ammo 0 //default: 12
+set g_balance_mortar_reload_time 2
 // }}}
 // {{{ electro
 set g_balance_electro_lightning 0
@@ -425,7 +469,37 @@ set g_balance_electro_switchdelay_raise 0.2
 set g_balance_electro_reload_ammo 0 //default: 20
 set g_balance_electro_reload_time 2
 // }}}
-// {{{ crylink
+// {{{ 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
@@ -477,8 +551,8 @@ 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_secondary_other_lifetime 5 
+set g_balance_crylink_secondary_other_fadetime 5
 
 set g_balance_crylink_switchdelay_drop 0.2
 set g_balance_crylink_switchdelay_raise 0.2
@@ -498,8 +572,6 @@ 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
@@ -585,34 +657,34 @@ set g_balance_hagar_switchdelay_raise 0.2
 set g_balance_hagar_reload_ammo 0 //default: 25
 set g_balance_hagar_reload_time 2
 // }}}
-// {{{ rocketlauncher
-set g_balance_rocketlauncher_damage 70
-set g_balance_rocketlauncher_edgedamage 35
-set g_balance_rocketlauncher_force 450
-set g_balance_rocketlauncher_radius 110
-set g_balance_rocketlauncher_speed 1300
-set g_balance_rocketlauncher_speedaccel 1300
-set g_balance_rocketlauncher_speedstart 1000
-set g_balance_rocketlauncher_lifetime 10
-set g_balance_rocketlauncher_refire 1.2
-set g_balance_rocketlauncher_animtime 0.4
-set g_balance_rocketlauncher_ammo 4
-set g_balance_rocketlauncher_health 30 // 30 // 5 hitpoints above maximum laser value -- this way lasers can't blow it up, but grenadelauncher still can most the time.
-set g_balance_rocketlauncher_damageforcescale 1 // low damage force scale so that it can still be affected by other hits, but not so much that it does a 90 degree turn
-set g_balance_rocketlauncher_detonatedelay 0.02 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time
-set g_balance_rocketlauncher_guiderate 70 // max degrees per second
-set g_balance_rocketlauncher_guideratedelay 0.01 // immediate
-set g_balance_rocketlauncher_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic)
-set g_balance_rocketlauncher_guidedelay 0.2 // delay before guiding kicks in
-set g_balance_rocketlauncher_guidestop 0 // stop guiding when firing again
-set g_balance_rocketlauncher_remote_damage 70
-set g_balance_rocketlauncher_remote_edgedamage 35
-set g_balance_rocketlauncher_remote_radius 110
-set g_balance_rocketlauncher_remote_force 400
-set g_balance_rocketlauncher_switchdelay_drop 0.3
-set g_balance_rocketlauncher_switchdelay_raise 0.2
-set g_balance_rocketlauncher_reload_ammo 0 //default: 25
-set g_balance_rocketlauncher_reload_time 2
+// {{{ devastator
+set g_balance_devastator_damage 70
+set g_balance_devastator_edgedamage 35
+set g_balance_devastator_force 450
+set g_balance_devastator_radius 110
+set g_balance_devastator_speed 1300
+set g_balance_devastator_speedaccel 1300
+set g_balance_devastator_speedstart 1000
+set g_balance_devastator_lifetime 10
+set g_balance_devastator_refire 1.2
+set g_balance_devastator_animtime 0.4
+set g_balance_devastator_ammo 4
+set g_balance_devastator_health 30 // 30 // 5 hitpoints above maximum laser value -- this way lasers can't blow it up, but mortar still can most the time.
+set g_balance_devastator_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_devastator_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_devastator_guiderate 70 // max degrees per second
+set g_balance_devastator_guideratedelay 0.01 // immediate
+set g_balance_devastator_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic)
+set g_balance_devastator_guidedelay 0.2 // delay before guiding kicks in
+set g_balance_devastator_guidestop 0 // stop guiding when firing again
+set g_balance_devastator_remote_damage 70
+set g_balance_devastator_remote_edgedamage 35
+set g_balance_devastator_remote_radius 110
+set g_balance_devastator_remote_force 400
+set g_balance_devastator_switchdelay_drop 0.3
+set g_balance_devastator_switchdelay_raise 0.2
+set g_balance_devastator_reload_ammo 0 //default: 25
+set g_balance_devastator_reload_time 2
 // }}}
 // {{{ porto
 set g_balance_porto_primary_refire 1.5
@@ -630,12 +702,12 @@ set g_balance_portal_health 200 // these get recharged whenever the portal is us
 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_ammo 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_primary_hooked_ammo 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
index d069d5f0fc0d797a06ede44d4e0814e254d5dfb6..1cbe37d7272e96d09d654b4db7e792a3997e1f4b 100644 (file)
@@ -7411,8 +7411,8 @@ velocityjitter 64 64 64
 //lightcolor 1 0.9 0.7
 //lightshadow 1
 
-// heal ray muzzleflash
 
+// heal ray muzzleflash
 effect healray_muzzleflash
 countabsolute 1
 type smoke
@@ -8950,3 +8950,83 @@ alpha 644 956 2500
 originjitter 64 64 64
 velocityjitter 324 324 324
 rotate -180 180 -100 100
+
+// laser_shockwave_attack
+// used nowhere in code
+effect laser_shockwave_attack
+// glow and light
+//countabsolute 1
+//type smoke
+//color 0xcc0000 0xff0000
+//tex 65 65
+//size 10 15
+//alpha 256 512 6280
+//airfriction 10
+//sizeincrease 1.5
+//stretchfactor 2
+//lightradius 200
+//lightradiusfade 2000
+//lightcolor 3 0.1 0.1
+// electricity
+effect laser_shockwave_attack
+count 1
+type spark
+color 0xb44215 0xff0000
+tex 43 43
+size 5 7
+bounce 0
+alpha 4096 4096 20000
+airfriction 1
+originjitter 2 2 2
+velocityjitter 10 10 10
+velocitymultiplier 10
+sizeincrease 1.5
+stretchfactor 2.3
+rotate -180 180 4000 -4000
+// fire
+effect laser_shockwave_attack
+count 1
+type spark
+color 0xff4200 0xff0000
+tex 8 15
+size 7 9
+bounce 0
+alpha 4096 4096 20000
+airfriction 1
+originjitter 2 2 2
+velocityjitter 10 10 10
+velocitymultiplier 10
+sizeincrease 1.5
+stretchfactor 2
+
+// new_laser_impact
+// used nowhere in code
+// decal
+effect new_laser_impact
+countabsolute 1
+type decal
+tex 8 16
+size 72 72
+alpha 256 256 0
+originjitter 2 2 2
+// flare effect
+//effect new_laser_impact
+//countabsolute 1
+//type static
+//tex 39 39
+//color 0xFF2010 0xFF2010
+//alpha 256 256 1024
+//size 24 24
+// sparks that rapidly expand and rapidly slow down to form an interesting spherical effect
+effect new_laser_impact
+count 128
+type spark
+color 0x800000 0xFF8020
+alpha 256 256 1024
+size 4 4
+bounce 1.5
+gravity 0.5
+airfriction 1
+liquidfriction 1
+originjitter 20 20 20
+velocityjitter 256 256 256
diff --git a/models/weapons/g_lightning.md3 b/models/weapons/g_lightning.md3
new file mode 100644 (file)
index 0000000..76c2e89
Binary files /dev/null and b/models/weapons/g_lightning.md3 differ
diff --git a/models/weapons/h_lightning.iqm b/models/weapons/h_lightning.iqm
new file mode 100644 (file)
index 0000000..8ae8898
Binary files /dev/null and b/models/weapons/h_lightning.iqm differ
diff --git a/models/weapons/h_lightning.iqm.framegroups b/models/weapons/h_lightning.iqm.framegroups
new file mode 100644 (file)
index 0000000..0a59625
--- /dev/null
@@ -0,0 +1,4 @@
+1 8 20 0 // fire
+9 5 20 0 // fire2
+15 200 20 1 // idle
+215 40 20 0 // reload
diff --git a/models/weapons/v_lightning.md3 b/models/weapons/v_lightning.md3
new file mode 100644 (file)
index 0000000..67105bc
Binary files /dev/null and b/models/weapons/v_lightning.md3 differ
diff --git a/models/weapons/w_lightning.zym b/models/weapons/w_lightning.zym
new file mode 100644 (file)
index 0000000..70cbd17
Binary files /dev/null and b/models/weapons/w_lightning.zym differ
diff --git a/oldbalance25.cfg b/oldbalance25.cfg
new file mode 100644 (file)
index 0000000..633bd96
--- /dev/null
@@ -0,0 +1,781 @@
+g_mod_balance Nexuiz25
+
+// {{{ starting gear
+set g_start_weapon_laser -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_shotgun -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_uzi -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_hagar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_balance_health_start 150
+set g_balance_armor_start 0
+set g_start_ammo_shells 40
+set g_start_ammo_nails 0
+set g_start_ammo_rockets 0
+set g_start_ammo_cells 0
+set g_start_ammo_fuel 0
+set g_warmup_start_health 250 "starting values when being in warmup-stage"
+set g_warmup_start_armor 100 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_shells 50 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_nails 150 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_rockets 50 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_cells 50 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage"
+set g_lms_start_health 250
+set g_lms_start_armor 100
+set g_lms_start_ammo_shells 50
+set g_lms_start_ammo_nails 150
+set g_lms_start_ammo_rockets 50
+set g_lms_start_ammo_cells 50
+set g_lms_start_ammo_fuel 0
+set g_balance_nix_roundtime 25
+set g_balance_nix_incrtime 1.6
+set g_balance_nix_ammo_shells 15
+set g_balance_nix_ammo_nails 45
+set g_balance_nix_ammo_rockets 15
+set g_balance_nix_ammo_cells 15
+set g_balance_nix_ammo_fuel 0
+set g_balance_nix_ammoincr_shells 2
+set g_balance_nix_ammoincr_nails 6
+set g_balance_nix_ammoincr_rockets 2
+set g_balance_nix_ammoincr_cells 2
+set g_balance_nix_ammoincr_fuel 2
+// }}}
+
+// {{{ pickup items
+set g_pickup_ammo_anyway 0
+set g_pickup_weapons_anyway 0
+set g_pickup_shells 15
+set g_pickup_shells_weapon 15
+set g_pickup_shells_max 999
+set g_pickup_nails 80
+set g_pickup_nails_weapon 80
+set g_pickup_nails_max 999
+set g_pickup_rockets 15
+set g_pickup_rockets_weapon 15
+set g_pickup_rockets_max 999
+set g_pickup_cells 25
+set g_pickup_cells_weapon 25
+set g_pickup_cells_max 999
+set g_pickup_fuel 25
+set g_pickup_fuel_weapon 25
+set g_pickup_fuel_jetpack 50
+set g_pickup_fuel_max 999
+set g_pickup_armorsmall 5
+set g_pickup_armorsmall_max 999
+set g_pickup_armorsmall_anyway 0
+set g_pickup_armormedium 25
+set g_pickup_armormedium_max 999
+set g_pickup_armormedium_anyway 0
+set g_pickup_armorbig 50
+set g_pickup_armorbig_max 999
+set g_pickup_armorbig_anyway 0
+set g_pickup_armorlarge 100
+set g_pickup_armorlarge_max 999
+set g_pickup_armorlarge_anyway 0
+set g_pickup_healthsmall 5
+set g_pickup_healthsmall_max 999
+set g_pickup_healthsmall_anyway 0
+set g_pickup_healthmedium 25
+set g_pickup_healthmedium_max 999
+set g_pickup_healthmedium_anyway 0
+set g_pickup_healthlarge 50
+set g_pickup_healthlarge_max 999
+set g_pickup_healthlarge_anyway 0
+set g_pickup_healthmega 100
+set g_pickup_healthmega_max 999
+set g_pickup_healthmega_anyway 0
+set g_pickup_respawntime_short 15
+set g_pickup_respawntime_medium 20
+set g_pickup_respawntime_long 30
+set g_pickup_respawntime_powerup 120
+set g_pickup_respawntime_weapon 15
+set g_pickup_respawntime_superweapon 120
+set g_pickup_respawntime_ammo 15
+set g_pickup_respawntimejitter_short 0
+set g_pickup_respawntimejitter_medium 0
+set g_pickup_respawntimejitter_long 0
+set g_pickup_respawntimejitter_powerup 10
+set g_pickup_respawntimejitter_weapon 0
+set g_pickup_respawntimejitter_superweapon 10
+set g_pickup_respawntimejitter_ammo 0
+// }}}
+
+// {{{ regen/rot
+set g_balance_health_regen 0.1
+set g_balance_health_regenlinear 0
+set g_balance_pause_health_regen 5
+set g_balance_pause_health_regen_spawn 0
+set g_balance_health_rot 0.1
+set g_balance_health_rotlinear 0
+set g_balance_pause_health_rot 5
+set g_balance_pause_health_rot_spawn 10
+set g_balance_health_regenstable 100
+set g_balance_health_rotstable 100
+set g_balance_health_limit 999
+set g_balance_armor_regen 0
+set g_balance_armor_regenlinear 0
+set g_balance_armor_rot 0.1
+set g_balance_armor_rotlinear 0
+set g_balance_pause_armor_rot 5
+set g_balance_pause_armor_rot_spawn 10
+set g_balance_armor_regenstable 100
+set g_balance_armor_rotstable 100
+set g_balance_armor_limit 999
+set g_balance_armor_blockpercent 0.6
+set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)"
+set g_balance_fuel_regenlinear 0
+set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter
+set g_balance_fuel_rot 0.05
+set g_balance_fuel_rotlinear 0
+set g_balance_pause_fuel_rot 5
+set g_balance_pause_fuel_rot_spawn 10
+set g_balance_fuel_regenstable 50
+set g_balance_fuel_rotstable 100
+set g_balance_fuel_limit 999
+// }}}
+
+// {{{ misc
+set g_balance_selfdamagepercent 0.6
+set g_weaponspeedfactor 1 "weapon projectile speed multiplier"
+set g_weaponratefactor 1 "weapon fire rate multiplier"
+set g_weapondamagefactor 1 "weapon damage multiplier"
+set g_weaponforcefactor 1 "weapon force multiplier"
+set g_weaponspreadfactor 1 "weapon spread multiplier"
+set g_balance_firetransfer_time 0.9
+set g_balance_firetransfer_damage 0.8
+set g_throughfloor_damage 1
+set g_throughfloor_force 1
+set g_projectiles_damage 2
+// possible values:
+// -2: absolutely no damage to projectiles (no exceptions)
+// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines)
+// 0: only damage from contents (lava/slime) or exceptions
+// 1: only self damage or damage from contents or exceptions
+// 2: allow all damage to projectiles normally
+set g_projectiles_keep_owner 0
+set g_projectiles_newton_style 2
+// possible values:
+// 0: absolute velocity projectiles (like Quake)
+// 1: relative velocity projectiles, "Newtonian" (like Tribes 2)
+// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard)
+set g_projectiles_newton_style_2_minfactor 0.7
+set g_projectiles_newton_style_2_maxfactor 5
+set g_projectiles_spread_style 0
+// possible values:
+// 0: forward + solid sphere (like Quake) - varies velocity
+// 1: forward + flattened solid sphere
+// 2: forward + solid circle
+// 3: forward + normal distribution 3D - varies velocity
+// 4: forward + normal distribution on a plane
+// 5: forward + circle with 1-r falloff
+// 6: forward + circle with 1-r^2 falloff
+// 7: forward + circle with (1-r)(2-r) falloff
+set g_balance_falldamage_deadminspeed 150
+set g_balance_falldamage_minspeed 1400
+set g_balance_falldamage_factor 0.15
+set g_balance_falldamage_maxdamage 25
+set g_balance_damagepush_speedfactor 0
+set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage
+set g_balance_contents_drowndelay 10 // time under water before a player begins drowning
+set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning
+set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava
+set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime
+set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime
+set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap"
+// }}}
+
+// {{{ powerups
+set g_balance_powerup_invincible_takedamage 0.2
+set g_balance_powerup_invincible_time 30
+set g_balance_powerup_strength_damage 3
+set g_balance_powerup_strength_force 4
+set g_balance_powerup_strength_time 30
+set g_balance_powerup_strength_selfdamage 1.5
+set g_balance_powerup_strength_selfforce 1.5
+set g_balance_superweapons_time 30
+// }}}
+
+// {{{ jetpack/hook
+set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack"
+set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction"
+set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)"
+set g_jetpack_maxspeed_side 1500 "max speed of the jetpack in xy direction"
+set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction"
+set g_jetpack_fuel 8 "fuel per second for jetpack"
+set g_jetpack_attenuation 2 "jetpack sound attenuation"
+
+set g_grappling_hook_tarzan 2 // 2: can also pull players
+set g_balance_grapplehook_speed_fly 1800
+set g_balance_grapplehook_speed_pull 2000
+set g_balance_grapplehook_force_rubber 2000
+set g_balance_grapplehook_force_rubber_overstretch 1000
+set g_balance_grapplehook_length_min 50
+set g_balance_grapplehook_stretch 50
+set g_balance_grapplehook_airfriction 0.2
+set g_balance_grapplehook_health 130
+set g_balance_grapplehook_damagedbycontents 0
+// }}}
+
+// {{{ weapon properties
+// {{{ laser
+set g_balance_laser_melee_animtime 0.3
+set g_balance_laser_melee_damage 80
+set g_balance_laser_melee_delay 0.25
+set g_balance_laser_melee_force 200
+set g_balance_laser_melee_multihit 1
+set g_balance_laser_melee_no_doubleslap 1
+set g_balance_laser_melee_nonplayerdamage 40
+set g_balance_laser_melee_range 120
+set g_balance_laser_melee_refire 1.25
+set g_balance_laser_melee_swing_side 120
+set g_balance_laser_melee_swing_up 30
+set g_balance_laser_melee_time 0.15
+set g_balance_laser_melee_traces 10
+
+set g_balance_laser_primary 1 // 0 = shockwave attack, 1 = projectile primary
+set g_balance_laser_primary_damage 35
+set g_balance_laser_primary_edgedamage 10
+set g_balance_laser_primary_force 400
+set g_balance_laser_primary_radius 70
+set g_balance_laser_primary_speed 9000
+set g_balance_laser_primary_spread 0
+set g_balance_laser_primary_refire 0.7
+set g_balance_laser_primary_animtime 0.3
+set g_balance_laser_primary_lifetime 30
+set g_balance_laser_primary_shotangle 0
+set g_balance_laser_primary_delay 0.05
+set g_balance_laser_primary_force_zscale 1
+set g_balance_laser_primary_force_velocitybias 0
+set g_balance_laser_primary_force_other_scale 1
+
+set g_balance_laser_secondary 0 // 0 = switch away to last used weapon, 1 = projectile secondary, 2 = melee secondary
+set g_balance_laser_secondary_damage 35
+set g_balance_laser_secondary_edgedamage 10
+set g_balance_laser_secondary_force 400
+set g_balance_laser_secondary_radius 70
+set g_balance_laser_secondary_speed 9000
+set g_balance_laser_secondary_spread 0
+set g_balance_laser_secondary_refire 0.7
+set g_balance_laser_secondary_animtime 0.3
+set g_balance_laser_secondary_lifetime 30
+set g_balance_laser_secondary_shotangle 0
+set g_balance_laser_secondary_delay 0
+set g_balance_laser_secondary_force_zscale 1
+set g_balance_laser_secondary_force_velocitybias 0
+set g_balance_laser_secondary_force_other_scale 1
+
+set g_balance_laser_shockwave_damage 20
+set g_balance_laser_shockwave_distance 2000
+set g_balance_laser_shockwave_edgedamage 0
+set g_balance_laser_shockwave_force 200
+set g_balance_laser_shockwave_force_forwardbias 50
+set g_balance_laser_shockwave_force_zscale 1.5
+set g_balance_laser_shockwave_jump_damage 20
+set g_balance_laser_shockwave_jump_edgedamage 0
+set g_balance_laser_shockwave_jump_force 300
+set g_balance_laser_shockwave_jump_force_velocitybias 0
+set g_balance_laser_shockwave_jump_force_zscale 1.25
+set g_balance_laser_shockwave_jump_multiplier_accuracy 0.5
+set g_balance_laser_shockwave_jump_multiplier_distance 0.5
+set g_balance_laser_shockwave_jump_multiplier_min 0
+set g_balance_laser_shockwave_jump_radius 150
+set g_balance_laser_shockwave_multiplier_accuracy 0.5
+set g_balance_laser_shockwave_multiplier_distance 0.5
+set g_balance_laser_shockwave_multiplier_min 0
+set g_balance_laser_shockwave_splash_damage 15
+set g_balance_laser_shockwave_splash_edgedamage 0
+set g_balance_laser_shockwave_splash_force 100
+set g_balance_laser_shockwave_splash_force_forwardbias 50
+set g_balance_laser_shockwave_splash_multiplier_accuracy 0.5
+set g_balance_laser_shockwave_splash_multiplier_distance 0.5
+set g_balance_laser_shockwave_splash_multiplier_min 0
+set g_balance_laser_shockwave_splash_radius 70
+set g_balance_laser_shockwave_spread_max 100
+set g_balance_laser_shockwave_spread_min 20
+
+set g_balance_laser_switchdelay_drop 0.15
+set g_balance_laser_switchdelay_raise 0.15
+set g_balance_laser_reload_ammo 0 //default: 6
+set g_balance_laser_reload_time 2
+// }}}
+// {{{ shotgun
+set g_balance_shotgun_primary_bullets 6
+set g_balance_shotgun_primary_damage 9
+set g_balance_shotgun_primary_force 60
+set g_balance_shotgun_primary_spread 0.07
+set g_balance_shotgun_primary_refire 0.5
+set g_balance_shotgun_primary_animtime 0.2
+set g_balance_shotgun_primary_ammo 1
+set g_balance_shotgun_primary_speed 12000
+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 0
+set g_balance_shotgun_secondary_melee_multihit 1
+set g_balance_shotgun_secondary_damage 115
+set g_balance_shotgun_secondary_force 150
+set g_balance_shotgun_secondary_refire 1.1
+set g_balance_shotgun_secondary_animtime 1
+set g_balance_shotgun_switchdelay_drop 0.15
+set g_balance_shotgun_switchdelay_raise 0.15
+set g_balance_shotgun_reload_ammo 0 //default: 5
+set g_balance_shotgun_reload_time 2
+// }}}
+// {{{ uzi
+set g_balance_uzi_mode 0                               // Activates varible spread for sustained & burst mode secondary
+set g_balance_uzi_spread_min 0.02
+set g_balance_uzi_spread_max 0.6
+set g_balance_uzi_spread_add 0.012
+
+set g_balance_uzi_burst 0                              // # of bullets in a burst (if set to 2 or more)
+set g_balance_uzi_burst_animtime 0.75
+set g_balance_uzi_burst_refire 0.05            // refire between burst bullets
+set g_balance_uzi_burst_refire2 0.75   // refire after burst
+set g_balance_uzi_burst_spread 0.04
+set g_balance_uzi_burst_damage 18
+set g_balance_uzi_burst_force 50
+set g_balance_uzi_burst_ammo 3
+
+set g_balance_uzi_first 1
+set g_balance_uzi_first_damage 30
+set g_balance_uzi_first_force 50
+set g_balance_uzi_first_spread 0.015
+set g_balance_uzi_first_refire 0.2
+set g_balance_uzi_first_ammo 1
+
+set g_balance_uzi_sustained_damage 15
+set g_balance_uzi_sustained_force 27
+set g_balance_uzi_sustained_spread 0.05
+set g_balance_uzi_sustained_refire 0.1
+set g_balance_uzi_sustained_ammo 1
+
+set g_balance_uzi_speed 18000
+set g_balance_uzi_bulletconstant 115 // 13.1qu
+
+set g_balance_uzi_switchdelay_drop 0.15
+set g_balance_uzi_switchdelay_raise 0.15
+
+set g_balance_uzi_reload_ammo 0 //default: 30
+set g_balance_uzi_reload_time 2
+// }}}
+// {{{ mortar
+set g_balance_grenadelauncher_primary_type 0
+set g_balance_grenadelauncher_primary_damage 70
+set g_balance_grenadelauncher_primary_edgedamage 38
+set g_balance_grenadelauncher_primary_force 400
+set g_balance_grenadelauncher_primary_radius 140
+set g_balance_grenadelauncher_primary_speed 2000
+set g_balance_grenadelauncher_primary_speed_up 200
+set g_balance_grenadelauncher_primary_speed_z 0
+set g_balance_grenadelauncher_primary_spread 0
+set g_balance_grenadelauncher_primary_lifetime 30
+set g_balance_grenadelauncher_primary_lifetime2 1
+set g_balance_grenadelauncher_primary_refire 0.8
+set g_balance_grenadelauncher_primary_animtime 0.3
+set g_balance_grenadelauncher_primary_ammo 2
+set g_balance_grenadelauncher_primary_health 25
+set g_balance_grenadelauncher_primary_damageforcescale 4
+set g_balance_grenadelauncher_primary_remote_minbouncecnt 0
+
+set g_balance_grenadelauncher_secondary_type 1
+set g_balance_grenadelauncher_secondary_damage 70
+set g_balance_grenadelauncher_secondary_edgedamage 38
+set g_balance_grenadelauncher_secondary_force 400
+set g_balance_grenadelauncher_secondary_radius 140
+set g_balance_grenadelauncher_secondary_speed 1400
+set g_balance_grenadelauncher_secondary_speed_up 200
+set g_balance_grenadelauncher_secondary_speed_z 0
+set g_balance_grenadelauncher_secondary_spread 0
+set g_balance_grenadelauncher_secondary_lifetime 2.5
+set g_balance_grenadelauncher_secondary_lifetime_bounce 0
+set g_balance_grenadelauncher_secondary_lifetime_stick 0
+set g_balance_grenadelauncher_secondary_refire 0.7
+set g_balance_grenadelauncher_secondary_animtime 0.3
+set g_balance_grenadelauncher_secondary_ammo 2
+set g_balance_grenadelauncher_secondary_health 10
+set g_balance_grenadelauncher_secondary_damageforcescale 4
+set g_balance_grenadelauncher_secondary_remote_detonateprimary 0
+
+set g_balance_grenadelauncher_bouncefactor 0.5
+set g_balance_grenadelauncher_bouncestop 0.075
+
+set g_balance_grenadelauncher_switchdelay_drop 0.15
+set g_balance_grenadelauncher_switchdelay_raise 0.15
+
+set g_balance_grenadelauncher_reload_ammo 0 //default: 12
+set g_balance_grenadelauncher_reload_time 2
+// }}}
+// {{{ electro
+set g_balance_electro_lightning 0
+set g_balance_electro_primary_damage 65
+set g_balance_electro_primary_edgedamage 0
+set g_balance_electro_primary_force 200
+set g_balance_electro_primary_force_up 0
+set g_balance_electro_primary_radius 150
+set g_balance_electro_primary_comboradius 0
+set g_balance_electro_primary_speed 2000
+set g_balance_electro_primary_spread 0
+set g_balance_electro_primary_lifetime 30
+set g_balance_electro_primary_refire 0.6
+set g_balance_electro_primary_animtime 0.3
+set g_balance_electro_primary_ammo 2
+set g_balance_electro_primary_range 0
+set g_balance_electro_primary_falloff_mindist 255 // 0.3 * radius
+set g_balance_electro_primary_falloff_maxdist 850
+set g_balance_electro_primary_falloff_halflifedist 425
+set g_balance_electro_secondary_damage 50
+set g_balance_electro_secondary_edgedamage 0
+set g_balance_electro_secondary_force 200
+set g_balance_electro_secondary_radius 150
+set g_balance_electro_secondary_speed 900
+set g_balance_electro_secondary_speed_up 200
+set g_balance_electro_secondary_speed_z 0
+set g_balance_electro_secondary_spread 0
+set g_balance_electro_secondary_lifetime 5
+set g_balance_electro_secondary_refire 0.3
+set g_balance_electro_secondary_refire2 0
+set g_balance_electro_secondary_animtime 0.3
+set g_balance_electro_secondary_ammo 2
+set g_balance_electro_secondary_health 5
+set g_balance_electro_secondary_damageforcescale 4
+set g_balance_electro_secondary_damagedbycontents 0
+set g_balance_electro_secondary_count 1
+set g_balance_electro_secondary_bouncefactor 0.5
+set g_balance_electro_secondary_bouncestop 0.075
+set g_balance_electro_combo_damage 80
+set g_balance_electro_combo_edgedamage 0
+set g_balance_electro_combo_force 200
+set g_balance_electro_combo_radius 250
+set g_balance_electro_combo_comboradius 0
+set g_balance_electro_combo_speed 2000
+set g_balance_electro_combo_safeammocheck 0
+set g_balance_electro_switchdelay_drop 0.15
+set g_balance_electro_switchdelay_raise 0.15
+set g_balance_electro_reload_ammo 0 //default: 20
+set g_balance_electro_reload_time 2
+// }}}
+// {{{ 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 18
+set g_balance_crylink_primary_edgedamage 0
+set g_balance_crylink_primary_force -55
+set g_balance_crylink_primary_radius 80
+set g_balance_crylink_primary_speed 7000
+set g_balance_crylink_primary_spread 0.03
+set g_balance_crylink_primary_shots 4
+set g_balance_crylink_primary_bounces 1
+set g_balance_crylink_primary_refire 0.4
+set g_balance_crylink_primary_animtime 0.3
+set g_balance_crylink_primary_ammo 2
+set g_balance_crylink_primary_bouncedamagefactor 0.5
+set g_balance_crylink_primary_joindelay 0
+set g_balance_crylink_primary_joinspread 0
+set g_balance_crylink_primary_joinexplode 0
+set g_balance_crylink_primary_joinexplode_damage 0
+set g_balance_crylink_primary_joinexplode_edgedamage 0
+set g_balance_crylink_primary_joinexplode_radius 0
+set g_balance_crylink_primary_joinexplode_force 0
+set g_balance_crylink_primary_linkexplode 1
+
+set g_balance_crylink_primary_middle_lifetime 5 // range: 35000 full, fades to 70000
+set g_balance_crylink_primary_middle_fadetime 5
+set g_balance_crylink_primary_other_lifetime 0.1 // range: 700 full, fades to 2450
+set g_balance_crylink_primary_other_fadetime 0.25
+
+set g_balance_crylink_secondary 1
+set g_balance_crylink_secondary_damage 18
+set g_balance_crylink_secondary_edgedamage 0
+set g_balance_crylink_secondary_force -55
+set g_balance_crylink_secondary_radius 3
+set g_balance_crylink_secondary_speed 7000
+set g_balance_crylink_secondary_spread 0.08
+set g_balance_crylink_secondary_spreadtype 0
+set g_balance_crylink_secondary_shots 7
+set g_balance_crylink_secondary_bounces 0
+set g_balance_crylink_secondary_refire 0.5
+set g_balance_crylink_secondary_animtime 0.3
+set g_balance_crylink_secondary_ammo 2
+set g_balance_crylink_secondary_bouncedamagefactor 0.5
+set g_balance_crylink_secondary_joindelay 0
+set g_balance_crylink_secondary_joinspread 0
+set g_balance_crylink_secondary_joinexplode 0
+set g_balance_crylink_secondary_joinexplode_damage 0
+set g_balance_crylink_secondary_joinexplode_edgedamage 0
+set g_balance_crylink_secondary_joinexplode_radius 0
+set g_balance_crylink_secondary_joinexplode_force 0
+set g_balance_crylink_secondary_linkexplode 1
+
+set g_balance_crylink_secondary_middle_lifetime 5 // range: 35000 full, fades to 70000
+set g_balance_crylink_secondary_middle_fadetime 5
+set g_balance_crylink_secondary_line_lifetime 2 // range: 35000 full, fades to 70000
+set g_balance_crylink_secondary_line_fadetime 2
+
+set g_balance_crylink_switchdelay_drop 0.15
+set g_balance_crylink_switchdelay_raise 0.15
+
+set g_balance_crylink_reload_ammo 0 //default: 10
+set g_balance_crylink_reload_time 2
+// }}}
+// {{{ nex
+set g_balance_nex_primary_damage 100
+set g_balance_nex_primary_force 600
+set g_balance_nex_primary_refire 1.5
+set g_balance_nex_primary_animtime 0.3
+set g_balance_nex_primary_ammo 5
+set g_balance_nex_primary_damagefalloff_mindist 0
+set g_balance_nex_primary_damagefalloff_maxdist 0
+set g_balance_nex_primary_damagefalloff_halflife 0
+set g_balance_nex_primary_damagefalloff_forcehalflife 0
+
+set g_balance_nex_secondary 0
+set g_balance_nex_secondary_charge 0
+set g_balance_nex_secondary_charge_rate 0.1
+set g_balance_nex_secondary_chargepool 0
+set g_balance_nex_secondary_chargepool_regen 0.15
+set g_balance_nex_secondary_chargepool_pause_regen 1
+set g_balance_nex_secondary_chargepool_pause_health_regen 1
+set g_balance_nex_secondary_damage 100
+set g_balance_nex_secondary_force 600
+set g_balance_nex_secondary_refire 1.5
+set g_balance_nex_secondary_animtime 0.3
+set g_balance_nex_secondary_ammo 5
+set g_balance_nex_secondary_damagefalloff_mindist 0
+set g_balance_nex_secondary_damagefalloff_maxdist 0
+set g_balance_nex_secondary_damagefalloff_halflife 0
+set g_balance_nex_secondary_damagefalloff_forcehalflife 0
+
+set g_balance_nex_charge 0
+set g_balance_nex_charge_mindmg 40
+set g_balance_nex_charge_start 0
+set g_balance_nex_charge_rate 0.1
+set g_balance_nex_charge_animlimit 0.5
+set g_balance_nex_charge_limit 0.5
+set g_balance_nex_charge_rot_rate 0
+set g_balance_nex_charge_rot_pause 0 // Dont rot down until this long after release of charge button
+set g_balance_nex_charge_shot_multiplier 0.5
+set g_balance_nex_charge_velocity_rate 0.2
+set g_balance_nex_charge_minspeed 400
+set g_balance_nex_charge_maxspeed 1000
+
+set g_balance_nex_switchdelay_drop 0.15
+set g_balance_nex_switchdelay_raise 0.15
+
+set g_balance_nex_reload_ammo 0 //default: 25
+set g_balance_nex_reload_time 2
+// }}}
+// {{{ minstanex
+set g_balance_minstanex_refire 1
+set g_balance_minstanex_animtime 0.3
+set g_balance_minstanex_ammo 10
+set g_balance_minstanex_laser_ammo 0
+set g_balance_minstanex_laser_animtime 0.3
+set g_balance_minstanex_laser_refire 0.7
+set g_balance_minstanex_switchdelay_drop 0.15
+set g_balance_minstanex_switchdelay_raise 0.15
+set g_balance_minstanex_reload_ammo 0 //default: 50
+set g_balance_minstanex_reload_time 2
+// }}}
+// {{{ hagar
+set g_balance_hagar_primary_damage 37
+set g_balance_hagar_primary_edgedamage 15
+set g_balance_hagar_primary_force 100
+set g_balance_hagar_primary_health 0
+set g_balance_hagar_primary_damageforcescale 0
+set g_balance_hagar_primary_radius 65
+set g_balance_hagar_primary_spread 0.010
+set g_balance_hagar_primary_speed 3000
+set g_balance_hagar_primary_lifetime 30
+set g_balance_hagar_primary_refire 0.15
+set g_balance_hagar_primary_ammo 1
+set g_balance_hagar_secondary 1
+set g_balance_hagar_secondary_load 0
+set g_balance_hagar_secondary_load_speed 0.6
+set g_balance_hagar_secondary_load_spread 0.075
+set g_balance_hagar_secondary_load_spread_bias 0.5
+set g_balance_hagar_secondary_load_max 4
+set g_balance_hagar_secondary_load_hold 0
+set g_balance_hagar_secondary_load_releasedeath 1
+set g_balance_hagar_secondary_load_abort 1
+set g_balance_hagar_secondary_load_linkexplode 0
+set g_balance_hagar_secondary_load_animtime 0.2
+set g_balance_hagar_secondary_damage 37
+set g_balance_hagar_secondary_edgedamage 15
+set g_balance_hagar_secondary_force 100
+set g_balance_hagar_secondary_health 0
+set g_balance_hagar_secondary_damageforcescale 0
+set g_balance_hagar_secondary_radius 65
+set g_balance_hagar_secondary_spread 0.015
+set g_balance_hagar_secondary_speed 1400
+set g_balance_hagar_secondary_lifetime_min 30
+set g_balance_hagar_secondary_lifetime_rand 0
+set g_balance_hagar_secondary_refire 0.15
+set g_balance_hagar_secondary_ammo 1
+set g_balance_hagar_switchdelay_drop 0.15
+set g_balance_hagar_switchdelay_raise 0.15
+set g_balance_hagar_reload_ammo 0 //default: 25
+set g_balance_hagar_reload_time 2
+// }}}
+// {{{ rocketlauncher
+set g_balance_rocketlauncher_damage 105
+set g_balance_rocketlauncher_edgedamage 40
+set g_balance_rocketlauncher_force 600
+set g_balance_rocketlauncher_radius 150
+set g_balance_rocketlauncher_speed 850
+set g_balance_rocketlauncher_speedaccel 0
+set g_balance_rocketlauncher_speedstart 850
+set g_balance_rocketlauncher_lifetime 30
+set g_balance_rocketlauncher_refire 1
+set g_balance_rocketlauncher_animtime 0.3
+set g_balance_rocketlauncher_ammo 3
+set g_balance_rocketlauncher_health 40
+set g_balance_rocketlauncher_damageforcescale 4
+set g_balance_rocketlauncher_detonatedelay 0.2 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time
+set g_balance_rocketlauncher_guiderate 90 // max degrees per second
+set g_balance_rocketlauncher_guideratedelay 0.01 // immediate
+set g_balance_rocketlauncher_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic)
+set g_balance_rocketlauncher_guidedelay 0.15 // delay before guiding kicks in
+set g_balance_rocketlauncher_guidestop 0 // stop guiding when firing again
+set g_balance_rocketlauncher_remote_damage 105
+set g_balance_rocketlauncher_remote_edgedamage 40
+set g_balance_rocketlauncher_remote_radius 150
+set g_balance_rocketlauncher_remote_force 600
+set g_balance_rocketlauncher_switchdelay_drop 0.15
+set g_balance_rocketlauncher_switchdelay_raise 0.15
+set g_balance_rocketlauncher_reload_ammo 0 //default: 25
+set g_balance_rocketlauncher_reload_time 2
+// }}}
+// {{{ porto
+set g_balance_porto_primary_refire 1.5
+set g_balance_porto_primary_animtime 0.3
+set g_balance_porto_primary_speed 2000
+set g_balance_porto_primary_lifetime 30
+set g_balance_porto_secondary 0
+set g_balance_porto_secondary_refire 1.5
+set g_balance_porto_secondary_animtime 0.3
+set g_balance_porto_secondary_speed 2000
+set g_balance_porto_secondary_lifetime 30
+set g_balance_porto_switchdelay_drop 0.15
+set g_balance_porto_switchdelay_raise 0.15
+set g_balance_portal_health 200 // these get recharged whenever the portal is used
+set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used
+// }}}
+// {{{ hook
+set g_balance_hook_primary_fuel 5 // hook monkeys set 0
+set g_balance_hook_primary_refire 0 // hook monkeys set 0
+set g_balance_hook_primary_animtime 0.3 // good shoot anim
+set g_balance_hook_primary_hooked_time_max 0 // infinite
+set g_balance_hook_primary_hooked_time_free 2 // 2s being hooked are free
+set g_balance_hook_primary_hooked_fuel 5 // fuel per second hooked
+set g_balance_hook_secondary_damage 25 // not much
+set g_balance_hook_secondary_edgedamage 5 // not much
+set g_balance_hook_secondary_radius 500 // LOTS
+set g_balance_hook_secondary_force -2000 // LOTS
+set g_balance_hook_secondary_ammo 50 // a whole pack
+set g_balance_hook_secondary_lifetime 30 // infinite
+set g_balance_hook_secondary_speed 0 // not much throwing
+set g_balance_hook_secondary_gravity 5 // fast falling
+set g_balance_hook_secondary_refire 3 // don't drop too many bombs...
+set g_balance_hook_secondary_animtime 0.3 // good shoot anim
+set g_balance_hook_secondary_power 3 // effect behaves like a square function
+set g_balance_hook_secondary_duration 1.5 // effect runs for three seconds
+set g_balance_hook_secondary_health 0
+set g_balance_hook_secondary_damageforcescale 0
+set g_balance_hook_switchdelay_drop 0.15
+set g_balance_hook_switchdelay_raise 0.15
+// }}}
+// {{{ tuba
+set g_balance_tuba_refire 0.05
+set g_balance_tuba_animtime 0.05
+set g_balance_tuba_attenuation 0.5
+set g_balance_tuba_volume 1
+set g_balance_tuba_fadetime 0.25
+set g_balance_tuba_damage 5
+set g_balance_tuba_edgedamage 0
+set g_balance_tuba_radius 200
+set g_balance_tuba_force 40
+set g_balance_tuba_pitchstep 6
+set g_balance_tuba_switchdelay_drop 0.15
+set g_balance_tuba_switchdelay_raise 0.15
+// }}}
+// {{{ fireball
+set g_balance_fireball_primary_animtime 0.15
+set g_balance_fireball_primary_bfgdamage 100
+set g_balance_fireball_primary_bfgforce 0
+set g_balance_fireball_primary_bfgradius 1000
+set g_balance_fireball_primary_damage 200
+set g_balance_fireball_primary_damageforcescale 4
+set g_balance_fireball_primary_edgedamage 0
+set g_balance_fireball_primary_force 700
+set g_balance_fireball_primary_health 50
+set g_balance_fireball_primary_laserburntime 0.5
+set g_balance_fireball_primary_laserdamage 80
+set g_balance_fireball_primary_laseredgedamage 20
+set g_balance_fireball_primary_laserradius 256
+set g_balance_fireball_primary_lifetime 15
+set g_balance_fireball_primary_radius 200
+set g_balance_fireball_primary_refire 5
+set g_balance_fireball_primary_refire2 0
+set g_balance_fireball_primary_speed 650
+set g_balance_fireball_primary_spread 0
+set g_balance_fireball_secondary_animtime 0.3
+set g_balance_fireball_secondary_damage 40
+set g_balance_fireball_secondary_damageforcescale 4
+set g_balance_fireball_secondary_damagetime 5
+set g_balance_fireball_secondary_force 100
+set g_balance_fireball_secondary_laserburntime 0.5
+set g_balance_fireball_secondary_laserdamage 50
+set g_balance_fireball_secondary_laseredgedamage 20
+set g_balance_fireball_secondary_laserradius 110
+set g_balance_fireball_secondary_lifetime 7
+set g_balance_fireball_secondary_refire 2
+set g_balance_fireball_secondary_speed 900
+set g_balance_fireball_secondary_speed_up 100
+set g_balance_fireball_secondary_speed_z 0
+set g_balance_fireball_secondary_spread 0
+set g_balance_fireball_switchdelay_drop 0.15
+set g_balance_fireball_switchdelay_raise 0.15
+// }}}
diff --git a/oldbalanceFruitieX.cfg b/oldbalanceFruitieX.cfg
new file mode 100644 (file)
index 0000000..a10d8b5
--- /dev/null
@@ -0,0 +1,781 @@
+g_mod_balance FruitieX
+
+// {{{ starting gear
+set g_start_weapon_laser -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_shotgun -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_uzi -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_hagar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_balance_health_start 125
+set g_balance_armor_start 0
+set g_start_ammo_shells 20
+set g_start_ammo_nails 0
+set g_start_ammo_rockets 0
+set g_start_ammo_cells 0
+set g_start_ammo_fuel 0
+set g_warmup_start_health 200 "starting values when being in warmup-stage"
+set g_warmup_start_armor 100 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_shells 50 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_nails 150 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_rockets 50 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_cells 50 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage"
+set g_lms_start_health 200
+set g_lms_start_armor 100
+set g_lms_start_ammo_shells 30
+set g_lms_start_ammo_nails 200
+set g_lms_start_ammo_rockets 150
+set g_lms_start_ammo_cells 150
+set g_lms_start_ammo_fuel 0
+set g_balance_nix_roundtime 25
+set g_balance_nix_incrtime 1.6
+set g_balance_nix_ammo_shells 15
+set g_balance_nix_ammo_nails 45
+set g_balance_nix_ammo_rockets 15
+set g_balance_nix_ammo_cells 15
+set g_balance_nix_ammo_fuel 0
+set g_balance_nix_ammoincr_shells 2
+set g_balance_nix_ammoincr_nails 6
+set g_balance_nix_ammoincr_rockets 2
+set g_balance_nix_ammoincr_cells 2
+set g_balance_nix_ammoincr_fuel 2
+// }}}
+
+// {{{ pickup items
+set g_pickup_ammo_anyway 1
+set g_pickup_weapons_anyway 1
+set g_pickup_shells 20
+set g_pickup_shells_weapon 10
+set g_pickup_shells_max 45
+set g_pickup_nails 120
+set g_pickup_nails_weapon 60
+set g_pickup_nails_max 300
+set g_pickup_rockets 25
+set g_pickup_rockets_weapon 15
+set g_pickup_rockets_max 150
+set g_pickup_cells 30
+set g_pickup_cells_weapon 20
+set g_pickup_cells_max 150
+set g_pickup_fuel 25
+set g_pickup_fuel_weapon 15
+set g_pickup_fuel_jetpack 50
+set g_pickup_fuel_max 100
+set g_pickup_armorsmall 5
+set g_pickup_armorsmall_max 150
+set g_pickup_armorsmall_anyway 1
+set g_pickup_armormedium 25
+set g_pickup_armormedium_max 50
+set g_pickup_armormedium_anyway 0
+set g_pickup_armorbig 50
+set g_pickup_armorbig_max 75; // LOG: to allow a little more armor from medium armor
+set g_pickup_armorbig_anyway 0
+set g_pickup_armorlarge 100
+set g_pickup_armorlarge_max 150
+set g_pickup_armorlarge_anyway 1
+set g_pickup_healthsmall 5
+set g_pickup_healthsmall_max 250
+set g_pickup_healthsmall_anyway 1
+set g_pickup_healthmedium 25
+set g_pickup_healthmedium_max 100
+set g_pickup_healthmedium_anyway 0
+set g_pickup_healthlarge 50
+set g_pickup_healthlarge_max 150
+set g_pickup_healthlarge_anyway 0
+set g_pickup_healthmega 100
+set g_pickup_healthmega_max 250
+set g_pickup_healthmega_anyway 1
+set g_pickup_respawntime_short 15
+set g_pickup_respawntime_medium 20
+set g_pickup_respawntime_long 30
+set g_pickup_respawntime_powerup 120
+set g_pickup_respawntime_weapon 10
+set g_pickup_respawntime_superweapon 120
+set g_pickup_respawntime_ammo 25
+set g_pickup_respawntimejitter_short 0
+set g_pickup_respawntimejitter_medium 0
+set g_pickup_respawntimejitter_long 0
+set g_pickup_respawntimejitter_powerup 10
+set g_pickup_respawntimejitter_weapon 0
+set g_pickup_respawntimejitter_superweapon 10
+set g_pickup_respawntimejitter_ammo 0
+// }}}
+
+// {{{ regen/rot
+set g_balance_health_regen 0.05
+set g_balance_health_regenlinear 0
+set g_balance_pause_health_regen 5
+set g_balance_pause_health_regen_spawn 0
+set g_balance_health_rot 0
+set g_balance_health_rotlinear 1
+set g_balance_pause_health_rot 1
+set g_balance_pause_health_rot_spawn 0
+set g_balance_health_regenstable 100
+set g_balance_health_rotstable 100
+set g_balance_health_limit 999
+set g_balance_armor_regen 0
+set g_balance_armor_regenlinear 0
+set g_balance_armor_rot 0
+set g_balance_armor_rotlinear 1
+set g_balance_pause_armor_rot 1
+set g_balance_pause_armor_rot_spawn 0
+set g_balance_armor_regenstable 100
+set g_balance_armor_rotstable 100
+set g_balance_armor_limit 999
+set g_balance_armor_blockpercent 0.7
+set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)"
+set g_balance_fuel_regenlinear 0
+set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter
+set g_balance_fuel_rot 0.05
+set g_balance_fuel_rotlinear 0
+set g_balance_pause_fuel_rot 5
+set g_balance_pause_fuel_rot_spawn 10
+set g_balance_fuel_regenstable 50
+set g_balance_fuel_rotstable 100
+set g_balance_fuel_limit 999
+// }}}
+
+// {{{ misc
+set g_balance_selfdamagepercent 0.65
+set g_weaponspeedfactor 1 "weapon projectile speed multiplier"
+set g_weaponratefactor 1 "weapon fire rate multiplier"
+set g_weapondamagefactor 1 "weapon damage multiplier"
+set g_weaponforcefactor 1 "weapon force multiplier"
+set g_weaponspreadfactor 1 "weapon spread multiplier"
+set g_balance_firetransfer_time 0.9
+set g_balance_firetransfer_damage 0.8
+set g_throughfloor_damage 0.7
+set g_throughfloor_force 0.8
+set g_projectiles_damage 2
+// possible values:
+// -2: absolutely no damage to projectiles (no exceptions)
+// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines)
+// 0: only damage from contents (lava/slime) or exceptions
+// 1: only self damage or damage from contents or exceptions
+// 2: allow all damage to projectiles normally
+set g_projectiles_keep_owner 0
+set g_projectiles_newton_style 2
+// possible values:
+// 0: absolute velocity projectiles (like Quake)
+// 1: relative velocity projectiles, "Newtonian" (like Tribes 2)
+// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard)
+set g_projectiles_newton_style_2_minfactor 0.7
+set g_projectiles_newton_style_2_maxfactor 5
+set g_projectiles_spread_style 7
+// possible values:
+// 0: forward + solid sphere (like Quake) - varies velocity
+// 1: forward + flattened solid sphere
+// 2: forward + solid circle
+// 3: forward + normal distribution 3D - varies velocity
+// 4: forward + normal distribution on a plane
+// 5: forward + circle with 1-r falloff
+// 6: forward + circle with 1-r^2 falloff
+// 7: forward + circle with (1-r)(2-r) falloff
+set g_balance_falldamage_deadminspeed 150
+set g_balance_falldamage_minspeed 800
+set g_balance_falldamage_factor 0.20
+set g_balance_falldamage_maxdamage 15
+set g_balance_damagepush_speedfactor 2.5
+set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage
+set g_balance_contents_drowndelay 10 // time under water before a player begins drowning
+set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning
+set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava
+set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime
+set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime
+set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap"
+// }}}
+
+// {{{ powerups
+set g_balance_powerup_invincible_takedamage 0.6
+set g_balance_powerup_invincible_time 30
+set g_balance_powerup_strength_damage 3
+set g_balance_powerup_strength_force 4
+set g_balance_powerup_strength_time 30
+set g_balance_powerup_strength_selfdamage 1.5
+set g_balance_powerup_strength_selfforce 1.5
+set g_balance_superweapons_time 30
+// }}}
+
+// {{{ jetpack/hook
+set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack"
+set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction"
+set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)"
+set g_jetpack_maxspeed_side 1500 "max speed of the jetpack in xy direction"
+set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction"
+set g_jetpack_fuel 8 "fuel per second for jetpack"
+set g_jetpack_attenuation 2 "jetpack sound attenuation"
+
+set g_grappling_hook_tarzan 2 // 2: can also pull players
+set g_balance_grapplehook_speed_fly 1800
+set g_balance_grapplehook_speed_pull 2000
+set g_balance_grapplehook_force_rubber 2000
+set g_balance_grapplehook_force_rubber_overstretch 1000
+set g_balance_grapplehook_length_min 50
+set g_balance_grapplehook_stretch 50
+set g_balance_grapplehook_airfriction 0.2
+set g_balance_grapplehook_health 130
+set g_balance_grapplehook_damagedbycontents 0
+// }}}
+
+// {{{ weapon properties
+// {{{ laser
+set g_balance_laser_melee_animtime 0.3
+set g_balance_laser_melee_damage 80
+set g_balance_laser_melee_delay 0.25
+set g_balance_laser_melee_force 200
+set g_balance_laser_melee_multihit 1
+set g_balance_laser_melee_no_doubleslap 1
+set g_balance_laser_melee_nonplayerdamage 40
+set g_balance_laser_melee_range 120
+set g_balance_laser_melee_refire 1.25
+set g_balance_laser_melee_swing_side 120
+set g_balance_laser_melee_swing_up 30
+set g_balance_laser_melee_time 0.15
+set g_balance_laser_melee_traces 10
+
+set g_balance_laser_primary 1 // 0 = shockwave attack, 1 = projectile primary
+set g_balance_laser_primary_damage 20 // dps 33, hope that's not too high
+set g_balance_laser_primary_edgedamage 20
+set g_balance_laser_primary_force 150 // this looks insanely low, but actually isn't with zscale and velocitybias
+set g_balance_laser_primary_radius 60
+set g_balance_laser_primary_speed 5000
+set g_balance_laser_primary_spread 0
+set g_balance_laser_primary_refire 0.6
+set g_balance_laser_primary_animtime 0.4
+set g_balance_laser_primary_lifetime 5
+set g_balance_laser_primary_shotangle 0
+set g_balance_laser_primary_delay 0
+set g_balance_laser_primary_force_zscale 2 // 300 upforce
+set g_balance_laser_primary_force_velocitybias 0.3
+set g_balance_laser_primary_force_other_scale 2.5 // force 375 when pushing others around
+
+set g_balance_laser_secondary 0 // 0 = switch away to last used weapon, 1 = projectile secondary, 2 = melee secondary
+set g_balance_laser_secondary_damage 200 // dps
+set g_balance_laser_secondary_edgedamage 0
+set g_balance_laser_secondary_force 1300
+set g_balance_laser_secondary_radius 60
+set g_balance_laser_secondary_speed 0
+set g_balance_laser_secondary_spread 0
+set g_balance_laser_secondary_refire 0.066
+set g_balance_laser_secondary_animtime 0.066
+set g_balance_laser_secondary_lifetime 0
+set g_balance_laser_secondary_shotangle 0
+set g_balance_laser_secondary_delay 0
+set g_balance_laser_secondary_force_zscale 1.25
+set g_balance_laser_secondary_force_velocitybias 0
+set g_balance_laser_secondary_force_other_scale 0
+
+set g_balance_laser_shockwave_damage 20
+set g_balance_laser_shockwave_distance 2000
+set g_balance_laser_shockwave_edgedamage 0
+set g_balance_laser_shockwave_force 200
+set g_balance_laser_shockwave_force_forwardbias 50
+set g_balance_laser_shockwave_force_zscale 1.5
+set g_balance_laser_shockwave_jump_damage 20
+set g_balance_laser_shockwave_jump_edgedamage 0
+set g_balance_laser_shockwave_jump_force 300
+set g_balance_laser_shockwave_jump_force_velocitybias 0
+set g_balance_laser_shockwave_jump_force_zscale 1.25
+set g_balance_laser_shockwave_jump_multiplier_accuracy 0.5
+set g_balance_laser_shockwave_jump_multiplier_distance 0.5
+set g_balance_laser_shockwave_jump_multiplier_min 0
+set g_balance_laser_shockwave_jump_radius 150
+set g_balance_laser_shockwave_multiplier_accuracy 0.5
+set g_balance_laser_shockwave_multiplier_distance 0.5
+set g_balance_laser_shockwave_multiplier_min 0
+set g_balance_laser_shockwave_splash_damage 15
+set g_balance_laser_shockwave_splash_edgedamage 0
+set g_balance_laser_shockwave_splash_force 100
+set g_balance_laser_shockwave_splash_force_forwardbias 50
+set g_balance_laser_shockwave_splash_multiplier_accuracy 0.5
+set g_balance_laser_shockwave_splash_multiplier_distance 0.5
+set g_balance_laser_shockwave_splash_multiplier_min 0
+set g_balance_laser_shockwave_splash_radius 70
+set g_balance_laser_shockwave_spread_max 100
+set g_balance_laser_shockwave_spread_min 20
+
+set g_balance_laser_switchdelay_drop 0.15
+set g_balance_laser_switchdelay_raise 0.15
+set g_balance_laser_reload_ammo 0 //default: 6
+set g_balance_laser_reload_time 2
+// }}}
+// {{{ shotgun
+set g_balance_shotgun_primary_bullets 18
+set g_balance_shotgun_primary_damage 3.5 // LOG: changed from 4 to 3.5, total damage 63
+set g_balance_shotgun_primary_force 20
+set g_balance_shotgun_primary_spread 0.16 // LOG: changed from 0.18 -> 0.16 to compensate a little for lower damage
+set g_balance_shotgun_primary_refire 1
+set g_balance_shotgun_primary_animtime 0.3
+set g_balance_shotgun_primary_ammo 1
+set g_balance_shotgun_primary_speed 12000
+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 0
+set g_balance_shotgun_secondary_melee_multihit 1
+set g_balance_shotgun_secondary_damage 110
+set g_balance_shotgun_secondary_force 150
+set g_balance_shotgun_secondary_refire 1.1
+set g_balance_shotgun_secondary_animtime 1
+set g_balance_shotgun_switchdelay_drop 0.15
+set g_balance_shotgun_switchdelay_raise 0.15
+set g_balance_shotgun_reload_ammo 0 //default: 5
+set g_balance_shotgun_reload_time 2
+// }}}
+// {{{ uzi
+set g_balance_uzi_mode 1                               // Activates varible spread for sustained & burst mode secondary
+set g_balance_uzi_spread_min 0.02
+set g_balance_uzi_spread_max 0.3 // LOG: 0.6 -> 0.3
+set g_balance_uzi_spread_add 0.008
+
+set g_balance_uzi_burst 3                              // # of bullets in a burst (if set to 2 or more)
+set g_balance_uzi_burst_animtime 0.45
+set g_balance_uzi_burst_refire 0.05            // refire between burst bullets
+set g_balance_uzi_burst_refire2 0.45   // refire after burst
+set g_balance_uzi_burst_spread 0.07
+set g_balance_uzi_burst_damage 25
+set g_balance_uzi_burst_force 50
+set g_balance_uzi_burst_ammo 3
+
+set g_balance_uzi_first 1
+set g_balance_uzi_first_damage 15 / f/ LOG: 22 -> 15
+set g_balance_uzi_first_force 50
+set g_balance_uzi_first_spread 0.03
+set g_balance_uzi_first_refire 0.2
+set g_balance_uzi_first_ammo 2
+
+set g_balance_uzi_sustained_damage 12   // 120 dps
+set g_balance_uzi_sustained_force 12
+set g_balance_uzi_sustained_spread 0.06
+set g_balance_uzi_sustained_refire 0.1
+set g_balance_uzi_sustained_ammo 1
+
+set g_balance_uzi_speed 18000
+set g_balance_uzi_bulletconstant 115 // 13.1qu
+
+set g_balance_uzi_switchdelay_drop 0.15
+set g_balance_uzi_switchdelay_raise 0.15
+
+set g_balance_uzi_reload_ammo 0 //default: 30
+set g_balance_uzi_reload_time 2
+// }}}
+// {{{ mortar
+set g_balance_grenadelauncher_primary_type 0
+set g_balance_grenadelauncher_primary_damage 44
+set g_balance_grenadelauncher_primary_edgedamage 32
+set g_balance_grenadelauncher_primary_force 300
+set g_balance_grenadelauncher_primary_radius 115
+set g_balance_grenadelauncher_primary_speed 1500
+set g_balance_grenadelauncher_primary_speed_up 225
+set g_balance_grenadelauncher_primary_speed_z 0
+set g_balance_grenadelauncher_primary_spread 0
+set g_balance_grenadelauncher_primary_lifetime 5
+set g_balance_grenadelauncher_primary_lifetime2 0.65
+set g_balance_grenadelauncher_primary_refire 0.8
+set g_balance_grenadelauncher_primary_animtime 0.3
+set g_balance_grenadelauncher_primary_ammo 2
+set g_balance_grenadelauncher_primary_health 80
+set g_balance_grenadelauncher_primary_damageforcescale 0
+set g_balance_grenadelauncher_primary_remote_minbouncecnt 0
+
+set g_balance_grenadelauncher_secondary_type 1
+set g_balance_grenadelauncher_secondary_damage 62
+set g_balance_grenadelauncher_secondary_edgedamage 32
+set g_balance_grenadelauncher_secondary_force 300
+set g_balance_grenadelauncher_secondary_radius 150
+set g_balance_grenadelauncher_secondary_speed 1000
+set g_balance_grenadelauncher_secondary_speed_up 250
+set g_balance_grenadelauncher_secondary_speed_z 0
+set g_balance_grenadelauncher_secondary_spread 0
+set g_balance_grenadelauncher_secondary_lifetime 3
+set g_balance_grenadelauncher_secondary_lifetime_bounce 0
+set g_balance_grenadelauncher_secondary_lifetime_stick 0.65
+set g_balance_grenadelauncher_secondary_refire 0.8
+set g_balance_grenadelauncher_secondary_animtime 0.3
+set g_balance_grenadelauncher_secondary_ammo 2
+set g_balance_grenadelauncher_secondary_health 40
+set g_balance_grenadelauncher_secondary_damageforcescale 0
+set g_balance_grenadelauncher_secondary_remote_detonateprimary 0
+
+set g_balance_grenadelauncher_bouncefactor 0.5
+set g_balance_grenadelauncher_bouncestop 0.12
+
+set g_balance_grenadelauncher_switchdelay_drop 0.15
+set g_balance_grenadelauncher_switchdelay_raise 0.15
+
+set g_balance_grenadelauncher_reload_ammo 0 //default: 12
+set g_balance_grenadelauncher_reload_time 2
+// }}}
+// {{{ electro
+set g_balance_electro_lightning 1
+set g_balance_electro_primary_damage 100
+set g_balance_electro_primary_edgedamage 0
+set g_balance_electro_primary_force 425
+set g_balance_electro_primary_force_up 125
+set g_balance_electro_primary_radius 850
+set g_balance_electro_primary_comboradius 150
+set g_balance_electro_primary_speed 0
+set g_balance_electro_primary_spread 0
+set g_balance_electro_primary_lifetime 0
+set g_balance_electro_primary_refire 0.4
+set g_balance_electro_primary_animtime 0.2
+set g_balance_electro_primary_ammo 5
+set g_balance_electro_primary_range 800
+set g_balance_electro_primary_falloff_mindist 0
+set g_balance_electro_primary_falloff_maxdist 0
+set g_balance_electro_primary_falloff_halflifedist 0
+set g_balance_electro_secondary_damage 25
+set g_balance_electro_secondary_edgedamage 0
+set g_balance_electro_secondary_force 100
+set g_balance_electro_secondary_radius 100
+set g_balance_electro_secondary_speed 700
+set g_balance_electro_secondary_speed_up 200
+set g_balance_electro_secondary_speed_z 0
+set g_balance_electro_secondary_spread 0.08
+set g_balance_electro_secondary_lifetime 3.5
+set g_balance_electro_secondary_refire 0.2
+set g_balance_electro_secondary_refire2 2
+set g_balance_electro_secondary_animtime 0.2
+set g_balance_electro_secondary_ammo 2
+set g_balance_electro_secondary_health 10
+set g_balance_electro_secondary_damageforcescale 4
+set g_balance_electro_secondary_damagedbycontents 0
+set g_balance_electro_secondary_count 3
+set g_balance_electro_secondary_bouncefactor 0.5
+set g_balance_electro_secondary_bouncestop 0.075
+set g_balance_electro_combo_damage 50
+set g_balance_electro_combo_edgedamage 0
+set g_balance_electro_combo_force 80
+set g_balance_electro_combo_radius 250
+set g_balance_electro_combo_comboradius 0
+set g_balance_electro_combo_speed 400
+set g_balance_electro_combo_safeammocheck 1
+set g_balance_electro_switchdelay_drop 0.15
+set g_balance_electro_switchdelay_raise 0.15
+set g_balance_electro_reload_ammo 0 //default: 20
+set g_balance_electro_reload_time 2
+// }}}
+// {{{ 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 7 // LOG: 10 -> 7
+set g_balance_crylink_primary_edgedamage 4 // LOG: 6 -> 4
+set g_balance_crylink_primary_force 35
+set g_balance_crylink_primary_radius 80
+set g_balance_crylink_primary_speed 1500
+set g_balance_crylink_primary_spread 0.05
+set g_balance_crylink_primary_shots 7
+set g_balance_crylink_primary_bounces 2
+set g_balance_crylink_primary_refire 0.8
+set g_balance_crylink_primary_animtime 0.3
+set g_balance_crylink_primary_ammo 2
+set g_balance_crylink_primary_bouncedamagefactor 0.2
+set g_balance_crylink_primary_joindelay 0
+set g_balance_crylink_primary_joinspread 0.2
+set g_balance_crylink_primary_joinexplode 0
+set g_balance_crylink_primary_joinexplode_damage 0
+set g_balance_crylink_primary_joinexplode_edgedamage 0
+set g_balance_crylink_primary_joinexplode_radius 0
+set g_balance_crylink_primary_joinexplode_force 0
+set g_balance_crylink_primary_linkexplode 1
+
+set g_balance_crylink_primary_middle_lifetime 5 // range: 10000 full, fades to 20000
+set g_balance_crylink_primary_middle_fadetime 5
+set g_balance_crylink_primary_other_lifetime 2 // range: 800 full, fades to 1300
+set g_balance_crylink_primary_other_fadetime 0.25
+
+set g_balance_crylink_secondary 1
+set g_balance_crylink_secondary_damage 5 // LOG: 8 -> 5
+set g_balance_crylink_secondary_edgedamage 3
+set g_balance_crylink_secondary_force 16 // LOG: 20 -> 16
+set g_balance_crylink_secondary_radius 15 // LOG: 20 -> 15
+set g_balance_crylink_secondary_speed 1250 // LOG: 1500 -> 1250
+set g_balance_crylink_secondary_spread 0.1
+set g_balance_crylink_secondary_spreadtype 0
+set g_balance_crylink_secondary_shots 6
+set g_balance_crylink_secondary_bounces 2
+set g_balance_crylink_secondary_refire 0.9 // LOG: 0.8 -> 0.9
+set g_balance_crylink_secondary_animtime 0.3
+set g_balance_crylink_secondary_ammo 3 // LOG: 2 -> 3
+set g_balance_crylink_secondary_bouncedamagefactor 0.4 // LOG: 0.2 -> 0.4
+set g_balance_crylink_secondary_joindelay 0
+set g_balance_crylink_secondary_joinspread 0.2
+set g_balance_crylink_secondary_joinexplode 0
+set g_balance_crylink_secondary_joinexplode_damage 0
+set g_balance_crylink_secondary_joinexplode_edgedamage 0
+set g_balance_crylink_secondary_joinexplode_radius 0
+set g_balance_crylink_secondary_joinexplode_force 0
+set g_balance_crylink_secondary_linkexplode 0
+
+set g_balance_crylink_secondary_middle_lifetime 5 // range: 10000 full, fades to 10000
+set g_balance_crylink_secondary_middle_fadetime 5
+set g_balance_crylink_secondary_line_lifetime 2 // range: 4000 full, fades to 8000
+set g_balance_crylink_secondary_line_fadetime 0.25
+
+set g_balance_crylink_switchdelay_drop 0.15
+set g_balance_crylink_switchdelay_raise 0.15
+
+set g_balance_crylink_reload_ammo 0 //default: 10
+set g_balance_crylink_reload_time 2
+// }}}
+// {{{ nex
+set g_balance_nex_primary_damage 90
+set g_balance_nex_primary_force 500
+set g_balance_nex_primary_refire 1
+set g_balance_nex_primary_animtime 0.3
+set g_balance_nex_primary_ammo 5
+set g_balance_nex_primary_damagefalloff_mindist 0
+set g_balance_nex_primary_damagefalloff_maxdist 0
+set g_balance_nex_primary_damagefalloff_halflife 0
+set g_balance_nex_primary_damagefalloff_forcehalflife 0
+
+set g_balance_nex_secondary 0 // LOG: disable secondary
+set g_balance_nex_secondary_charge 0 // LOG: disable secondary charge
+set g_balance_nex_secondary_charge_rate 0.4
+set g_balance_nex_secondary_chargepool 1
+set g_balance_nex_secondary_chargepool_regen 0.25
+set g_balance_nex_secondary_chargepool_pause_regen 2
+set g_balance_nex_secondary_chargepool_pause_health_regen 0.5
+set g_balance_nex_secondary_damage 0
+set g_balance_nex_secondary_force 0
+set g_balance_nex_secondary_refire 0
+set g_balance_nex_secondary_animtime 0
+set g_balance_nex_secondary_ammo 0.4 // full charge pool is 1, so it depletes in 2.5 secs
+set g_balance_nex_secondary_damagefalloff_mindist 0
+set g_balance_nex_secondary_damagefalloff_maxdist 0
+set g_balance_nex_secondary_damagefalloff_halflife 0
+set g_balance_nex_secondary_damagefalloff_forcehalflife 0
+
+set g_balance_nex_charge 1
+set g_balance_nex_charge_mindmg 20
+set g_balance_nex_charge_start 0.5
+set g_balance_nex_charge_rate 0.5
+set g_balance_nex_charge_animlimit 0.5
+set g_balance_nex_charge_limit 1 // LOG: 0.5 -> 1 - allow to fully charge automaticaly
+set g_balance_nex_charge_rot_rate 0 // LOG: 0.1 -> 0 - disable rot
+set g_balance_nex_charge_rot_pause 0.5 // Dont rot down until this long after release of charge button
+set g_balance_nex_charge_shot_multiplier 0
+set g_balance_nex_charge_velocity_rate 0
+set g_balance_nex_charge_minspeed 600
+set g_balance_nex_charge_maxspeed 1000
+
+set g_balance_nex_switchdelay_drop 0.15
+set g_balance_nex_switchdelay_raise 0.15
+
+set g_balance_nex_reload_ammo 0 //default: 25
+set g_balance_nex_reload_time 2
+// }}}
+// {{{ minstanex
+set g_balance_minstanex_refire 1
+set g_balance_minstanex_animtime 0.50
+set g_balance_minstanex_ammo 10
+set g_balance_minstanex_laser_ammo 0
+set g_balance_minstanex_laser_animtime 0.3
+set g_balance_minstanex_laser_refire 0.6
+set g_balance_minstanex_switchdelay_drop 0.15
+set g_balance_minstanex_switchdelay_raise 0.15
+set g_balance_minstanex_reload_ammo 0 //default: 50
+set g_balance_minstanex_reload_time 2
+// }}}
+// {{{ hagar
+set g_balance_hagar_primary_damage 14
+set g_balance_hagar_primary_edgedamage 6
+set g_balance_hagar_primary_force 70
+set g_balance_hagar_primary_health 0
+set g_balance_hagar_primary_damageforcescale 0
+set g_balance_hagar_primary_radius 110
+set g_balance_hagar_primary_spread 0.1
+set g_balance_hagar_primary_speed 1800
+set g_balance_hagar_primary_lifetime 5
+set g_balance_hagar_primary_refire 0.12
+set g_balance_hagar_primary_ammo 1
+set g_balance_hagar_secondary 1
+set g_balance_hagar_secondary_load 0
+set g_balance_hagar_secondary_load_speed 0.6
+set g_balance_hagar_secondary_load_spread 0.075
+set g_balance_hagar_secondary_load_spread_bias 0.5
+set g_balance_hagar_secondary_load_max 4
+set g_balance_hagar_secondary_load_hold 0
+set g_balance_hagar_secondary_load_releasedeath 1
+set g_balance_hagar_secondary_load_abort 1
+set g_balance_hagar_secondary_load_linkexplode 0
+set g_balance_hagar_secondary_load_animtime 0.2
+set g_balance_hagar_secondary_damage 14 // default for _load: 32
+set g_balance_hagar_secondary_edgedamage 6 // default for _load: 10
+set g_balance_hagar_secondary_force 70 // default for _load: 160
+set g_balance_hagar_secondary_health 0
+set g_balance_hagar_secondary_damageforcescale 0
+set g_balance_hagar_secondary_radius 125
+set g_balance_hagar_secondary_spread 0.15 // default for _load: 0.08
+set g_balance_hagar_secondary_speed 1800
+set g_balance_hagar_secondary_lifetime_min 5
+set g_balance_hagar_secondary_lifetime_rand 0
+set g_balance_hagar_secondary_refire 0.12 // default for _load: 0.8
+set g_balance_hagar_secondary_ammo 1
+set g_balance_hagar_switchdelay_drop 0.15
+set g_balance_hagar_switchdelay_raise 0.15
+set g_balance_hagar_reload_ammo 0 //default: 25
+set g_balance_hagar_reload_time 2
+// }}}
+// {{{ rocketlauncher
+set g_balance_rocketlauncher_damage 82
+set g_balance_rocketlauncher_edgedamage 32
+set g_balance_rocketlauncher_force 350
+set g_balance_rocketlauncher_radius 130
+set g_balance_rocketlauncher_speed 1400
+set g_balance_rocketlauncher_speedaccel 1400
+set g_balance_rocketlauncher_speedstart 800
+set g_balance_rocketlauncher_lifetime 5
+set g_balance_rocketlauncher_refire 1
+set g_balance_rocketlauncher_animtime 0.3
+set g_balance_rocketlauncher_ammo 3
+set g_balance_rocketlauncher_health 0
+set g_balance_rocketlauncher_damageforcescale 0
+set g_balance_rocketlauncher_detonatedelay 0.05 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time
+set g_balance_rocketlauncher_guiderate 42 // max degrees per second
+set g_balance_rocketlauncher_guideratedelay 0.01 // immediate
+set g_balance_rocketlauncher_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic)
+set g_balance_rocketlauncher_guidedelay 0.15 // delay before guiding kicks in
+set g_balance_rocketlauncher_guidestop 0 // stop guiding when firing again
+set g_balance_rocketlauncher_remote_damage 60
+set g_balance_rocketlauncher_remote_edgedamage 20
+set g_balance_rocketlauncher_remote_radius 120
+set g_balance_rocketlauncher_remote_force 350
+set g_balance_rocketlauncher_switchdelay_drop 0.15
+set g_balance_rocketlauncher_switchdelay_raise 0.15
+set g_balance_rocketlauncher_reload_ammo 0 //default: 25
+set g_balance_rocketlauncher_reload_time 2
+// }}}
+// {{{ porto
+set g_balance_porto_primary_refire 1.5
+set g_balance_porto_primary_animtime 0.2
+set g_balance_porto_primary_speed 2000
+set g_balance_porto_primary_lifetime 5
+set g_balance_porto_secondary 0
+set g_balance_porto_secondary_refire 1.5
+set g_balance_porto_secondary_animtime 0.2
+set g_balance_porto_secondary_speed 2000
+set g_balance_porto_secondary_lifetime 5
+set g_balance_porto_switchdelay_drop 0.15
+set g_balance_porto_switchdelay_raise 0.15
+set g_balance_portal_health 200 // these get recharged whenever the portal is used
+set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used
+// }}}
+// {{{ hook
+set g_balance_hook_primary_fuel 5 // hook monkeys set 0
+set g_balance_hook_primary_refire 0 // hook monkeys set 0
+set g_balance_hook_primary_animtime 0.2 // good shoot anim
+set g_balance_hook_primary_hooked_time_max 0 // infinite
+set g_balance_hook_primary_hooked_time_free 2 // 2s being hooked are free
+set g_balance_hook_primary_hooked_fuel 5 // fuel per second hooked
+set g_balance_hook_secondary_damage 25 // not much
+set g_balance_hook_secondary_edgedamage 5 // not much
+set g_balance_hook_secondary_radius 500 // LOTS
+set g_balance_hook_secondary_force -2000 // LOTS
+set g_balance_hook_secondary_ammo 50 // a whole pack
+set g_balance_hook_secondary_lifetime 5 // infinite
+set g_balance_hook_secondary_speed 0 // not much throwing
+set g_balance_hook_secondary_gravity 5 // fast falling
+set g_balance_hook_secondary_refire 3 // don't drop too many bombs...
+set g_balance_hook_secondary_animtime 0.2 // good shoot anim
+set g_balance_hook_secondary_power 3 // effect behaves like a square function
+set g_balance_hook_secondary_duration 1.5 // effect runs for three seconds
+set g_balance_hook_secondary_health 0
+set g_balance_hook_secondary_damageforcescale 0
+set g_balance_hook_switchdelay_drop 0.15
+set g_balance_hook_switchdelay_raise 0.15
+// }}}
+// {{{ tuba
+set g_balance_tuba_refire 0.05
+set g_balance_tuba_animtime 0.05
+set g_balance_tuba_attenuation 0.5
+set g_balance_tuba_volume 1
+set g_balance_tuba_fadetime 0.25
+set g_balance_tuba_damage 5
+set g_balance_tuba_edgedamage 0
+set g_balance_tuba_radius 200
+set g_balance_tuba_force 40
+set g_balance_tuba_pitchstep 6
+set g_balance_tuba_switchdelay_drop 0.15
+set g_balance_tuba_switchdelay_raise 0.15
+// }}}
+// {{{ fireball
+set g_balance_fireball_primary_animtime 0.2
+set g_balance_fireball_primary_bfgdamage 100
+set g_balance_fireball_primary_bfgforce 0
+set g_balance_fireball_primary_bfgradius 1000
+set g_balance_fireball_primary_damage 200
+set g_balance_fireball_primary_damageforcescale 4
+set g_balance_fireball_primary_edgedamage 0
+set g_balance_fireball_primary_force 700
+set g_balance_fireball_primary_health 50
+set g_balance_fireball_primary_laserburntime 0.5
+set g_balance_fireball_primary_laserdamage 80
+set g_balance_fireball_primary_laseredgedamage 20
+set g_balance_fireball_primary_laserradius 256
+set g_balance_fireball_primary_lifetime 15
+set g_balance_fireball_primary_radius 200
+set g_balance_fireball_primary_refire 5
+set g_balance_fireball_primary_refire2 0
+set g_balance_fireball_primary_speed 650
+set g_balance_fireball_primary_spread 0
+set g_balance_fireball_secondary_animtime 0.2
+set g_balance_fireball_secondary_damage 40
+set g_balance_fireball_secondary_damageforcescale 4
+set g_balance_fireball_secondary_damagetime 5
+set g_balance_fireball_secondary_force 100
+set g_balance_fireball_secondary_laserburntime 0.5
+set g_balance_fireball_secondary_laserdamage 50
+set g_balance_fireball_secondary_laseredgedamage 20
+set g_balance_fireball_secondary_laserradius 110
+set g_balance_fireball_secondary_lifetime 7
+set g_balance_fireball_secondary_refire 2
+set g_balance_fireball_secondary_speed 900
+set g_balance_fireball_secondary_speed_up 100
+set g_balance_fireball_secondary_speed_z 0
+set g_balance_fireball_secondary_spread 0
+set g_balance_fireball_switchdelay_drop 0.15
+set g_balance_fireball_switchdelay_raise 0.15
+// }}}
diff --git a/oldbalanceXDF.cfg b/oldbalanceXDF.cfg
new file mode 100644 (file)
index 0000000..f408bf8
--- /dev/null
@@ -0,0 +1,751 @@
+g_mod_balance XDF
+
+// {{{ starting gear
+set g_start_weapon_laser 0 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_shotgun 0 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_uzi 1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default, -2 = provide the weapon in ca and lms"
+set g_start_weapon_grenadelauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
+set g_start_weapon_electro -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
+set g_start_weapon_crylink -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
+set g_start_weapon_nex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
+set g_start_weapon_hagar -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default" // UNTIL IT CAN BE REMOVED FROM CODE
+set g_start_weapon_rocketlauncher -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
+set g_start_weapon_minstanex -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
+set g_start_weapon_porto -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
+set g_start_weapon_hook -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
+set g_start_weapon_tuba -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
+set g_start_weapon_fireball -1 "0 = never provide the weapon, 1 = always provide the weapon, -1 = game mode default"
+set g_balance_health_start 100
+set g_balance_armor_start 0
+set g_start_ammo_shells 15
+set g_start_ammo_nails 0
+set g_start_ammo_rockets 0
+set g_start_ammo_cells 0
+set g_start_ammo_fuel 0
+set g_warmup_start_health 100 "starting values when being in warmup-stage"
+set g_warmup_start_armor 100 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_nails 160 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_rockets 80 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_cells 90 "starting values when being in warmup-stage"
+set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage"
+set g_lms_start_health 200
+set g_lms_start_armor 200
+set g_lms_start_ammo_shells 60
+set g_lms_start_ammo_nails 320
+set g_lms_start_ammo_rockets 160
+set g_lms_start_ammo_cells 180
+set g_lms_start_ammo_fuel 0
+set g_balance_nix_roundtime 25
+set g_balance_nix_incrtime 1.6
+set g_balance_nix_ammo_shells 60
+set g_balance_nix_ammo_nails 320
+set g_balance_nix_ammo_rockets 160
+set g_balance_nix_ammo_cells 180
+set g_balance_nix_ammo_fuel 0
+set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume
+set g_balance_nix_ammoincr_nails 6
+set g_balance_nix_ammoincr_rockets 2
+set g_balance_nix_ammoincr_cells 2
+set g_balance_nix_ammoincr_fuel 2
+// }}}
+
+// {{{ pickup items
+set g_pickup_ammo_anyway 1
+set g_pickup_weapons_anyway 1
+set g_pickup_shells 15
+set g_pickup_shells_weapon 15
+set g_pickup_shells_max 60
+set g_pickup_nails 80
+set g_pickup_nails_weapon 80
+set g_pickup_nails_max 320
+set g_pickup_rockets 40
+set g_pickup_rockets_weapon 40
+set g_pickup_rockets_max 160
+set g_pickup_cells 30
+set g_pickup_cells_weapon 30
+set g_pickup_cells_max 180
+set g_pickup_fuel 50
+set g_pickup_fuel_weapon 50
+set g_pickup_fuel_jetpack 100
+set g_pickup_fuel_max 100
+set g_pickup_armorsmall 5
+set g_pickup_armorsmall_max 200
+set g_pickup_armorsmall_anyway 1
+set g_pickup_armormedium 25
+set g_pickup_armormedium_max 200
+set g_pickup_armormedium_anyway 1
+set g_pickup_armorbig 50
+set g_pickup_armorbig_max 200
+set g_pickup_armorbig_anyway 1
+set g_pickup_armorlarge 100
+set g_pickup_armorlarge_max 200
+set g_pickup_armorlarge_anyway 1
+set g_pickup_healthsmall 5
+set g_pickup_healthsmall_max 200
+set g_pickup_healthsmall_anyway 1
+set g_pickup_healthmedium 25
+set g_pickup_healthmedium_max 200
+set g_pickup_healthmedium_anyway 1
+set g_pickup_healthlarge 50
+set g_pickup_healthlarge_max 200
+set g_pickup_healthlarge_anyway 1
+set g_pickup_healthmega 100
+set g_pickup_healthmega_max 200
+set g_pickup_healthmega_anyway 1
+set g_pickup_respawntime_short 0.1
+set g_pickup_respawntime_medium 0.1
+set g_pickup_respawntime_long 0.1
+set g_pickup_respawntime_powerup 0.1
+set g_pickup_respawntime_weapon 0.1
+set g_pickup_respawntime_superweapon 0.1
+set g_pickup_respawntime_ammo 0.1
+set g_pickup_respawntimejitter_short 0
+set g_pickup_respawntimejitter_medium 0
+set g_pickup_respawntimejitter_long 0
+set g_pickup_respawntimejitter_powerup 30
+set g_pickup_respawntimejitter_weapon 0
+set g_pickup_respawntimejitter_superweapon 10
+set g_pickup_respawntimejitter_ammo 0
+// }}}
+
+// {{{ regen/rot
+set g_balance_health_regen 0.08
+set g_balance_health_regenlinear 0.5
+set g_balance_pause_health_regen 5
+set g_balance_pause_health_regen_spawn 0
+set g_balance_health_rot 0.04
+set g_balance_health_rotlinear 0.75
+set g_balance_pause_health_rot 1
+set g_balance_pause_health_rot_spawn 5
+set g_balance_health_regenstable 100
+set g_balance_health_rotstable 100
+set g_balance_health_limit 999
+set g_balance_armor_regen 0
+set g_balance_armor_regenlinear 0
+set g_balance_armor_rot 0.04
+set g_balance_armor_rotlinear 0.75
+set g_balance_pause_armor_rot 1
+set g_balance_pause_armor_rot_spawn 5
+set g_balance_armor_regenstable 100
+set g_balance_armor_rotstable 100
+set g_balance_armor_limit 999
+set g_balance_armor_blockpercent 0.6
+set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)"
+set g_balance_fuel_regenlinear 0
+set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter
+set g_balance_fuel_rot 0.05
+set g_balance_fuel_rotlinear 0
+set g_balance_pause_fuel_rot 5
+set g_balance_pause_fuel_rot_spawn 10
+set g_balance_fuel_regenstable 50
+set g_balance_fuel_rotstable 100
+set g_balance_fuel_limit 999
+// }}}
+
+// {{{ misc
+set g_balance_selfdamagepercent 0
+set g_weaponspeedfactor 1 "weapon projectile speed multiplier"
+set g_weaponratefactor 1 "weapon fire rate multiplier"
+set g_weapondamagefactor 1 "weapon damage multiplier"
+set g_weaponforcefactor 1 "weapon force multiplier"
+set g_weaponspreadfactor 1 "weapon spread multiplier"
+set g_balance_firetransfer_time 0.9
+set g_balance_firetransfer_damage 0.8
+set g_throughfloor_damage 0.4
+set g_throughfloor_force 0.7
+set g_projectiles_damage 2
+// possible values:
+// -2: absolutely no damage to projectiles (no exceptions)
+// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines)
+// 0: only damage from contents (lava/slime) or exceptions
+// 1: only self damage or damage from contents or exceptions
+// 2: allow all damage to projectiles normally
+set g_projectiles_keep_owner 0
+set g_projectiles_newton_style 2
+// possible values:
+// 0: absolute velocity projectiles (like Quake)
+// 1: relative velocity projectiles, "Newtonian" (like Tribes 2)
+// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard)
+set g_projectiles_newton_style_2_minfactor 0.8
+set g_projectiles_newton_style_2_maxfactor 1.5
+set g_projectiles_spread_style 7
+// possible values:
+// 0: forward + solid sphere (like Quake) - varies velocity
+// 1: forward + flattened solid sphere
+// 2: forward + solid circle
+// 3: forward + normal distribution 3D - varies velocity
+// 4: forward + normal distribution on a plane
+// 5: forward + circle with 1-r falloff
+// 6: forward + circle with 1-r^2 falloff
+// 7: forward + circle with (1-r)(2-r) falloff
+set g_balance_falldamage_deadminspeed 250
+set g_balance_falldamage_minspeed 900
+set g_balance_falldamage_factor 0.20
+set g_balance_falldamage_maxdamage 40
+set g_balance_damagepush_speedfactor 2.5
+set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage
+set g_balance_contents_drowndelay 10 // time under water before a player begins drowning
+set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning
+set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava
+set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime
+set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime
+set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap"
+// }}}
+
+// {{{ powerups
+set g_balance_powerup_invincible_takedamage 0.25 // only 1/4th damage is taken
+set g_balance_powerup_invincible_time 999
+set g_balance_powerup_strength_damage 3
+set g_balance_powerup_strength_force 3
+set g_balance_powerup_strength_time 999
+set g_balance_powerup_strength_selfdamage 1.5
+set g_balance_powerup_strength_selfforce 1.5
+set g_balance_superweapons_time 30
+// }}}
+
+// {{{ jetpack/hook
+set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack"
+set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction"
+set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)"
+set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction"
+set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction"
+set g_jetpack_fuel 8 "fuel per second for jetpack"
+set g_jetpack_attenuation 2 "jetpack sound attenuation"
+
+set g_grappling_hook_tarzan 2 // 2: can also pull players
+set g_balance_grapplehook_speed_fly 1800
+set g_balance_grapplehook_speed_pull 2000
+set g_balance_grapplehook_force_rubber 2000
+set g_balance_grapplehook_force_rubber_overstretch 1000
+set g_balance_grapplehook_length_min 50
+set g_balance_grapplehook_stretch 50
+set g_balance_grapplehook_airfriction 0.2
+set g_balance_grapplehook_health 130
+set g_balance_grapplehook_damagedbycontents 0
+// }}}
+
+// {{{ weapon properties
+// {{{ laser
+set g_balance_laser_melee_animtime 0.3
+set g_balance_laser_melee_damage 80
+set g_balance_laser_melee_delay 0.25
+set g_balance_laser_melee_force 200
+set g_balance_laser_melee_multihit 1
+set g_balance_laser_melee_no_doubleslap 1
+set g_balance_laser_melee_nonplayerdamage 40
+set g_balance_laser_melee_range 120
+set g_balance_laser_melee_refire 1.25
+set g_balance_laser_melee_swing_side 120
+set g_balance_laser_melee_swing_up 30
+set g_balance_laser_melee_time 0.15
+set g_balance_laser_melee_traces 10
+
+set g_balance_laser_primary 1 // 0 = shockwave attack, 1 = projectile primary
+set g_balance_laser_primary_damage 25
+set g_balance_laser_primary_edgedamage 12.5
+set g_balance_laser_primary_force 250
+set g_balance_laser_primary_radius 70
+set g_balance_laser_primary_speed 6000
+set g_balance_laser_primary_spread 0
+set g_balance_laser_primary_refire 0.7
+set g_balance_laser_primary_animtime 0.3
+set g_balance_laser_primary_lifetime 5
+set g_balance_laser_primary_shotangle 0
+set g_balance_laser_primary_delay 0
+set g_balance_laser_primary_force_zscale 1.5
+set g_balance_laser_primary_force_velocitybias 0
+set g_balance_laser_primary_force_other_scale 1
+
+set g_balance_laser_secondary 0 // 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
+set g_balance_laser_switchdelay_raise 0
+set g_balance_laser_reload_ammo 0 //default: 6
+set g_balance_laser_reload_time 2
+// }}}
+// {{{ shotgun
+set g_balance_shotgun_primary_bullets 14
+set g_balance_shotgun_primary_damage 4
+set g_balance_shotgun_primary_force 15
+set g_balance_shotgun_primary_spread 0.12
+set g_balance_shotgun_primary_refire 0.75
+set g_balance_shotgun_primary_animtime 0.2
+set g_balance_shotgun_primary_ammo 1
+set g_balance_shotgun_primary_speed 8000
+set g_balance_shotgun_primary_bulletconstant 75 // 3.8qu
+set g_balance_shotgun_secondary 1
+set g_balance_shotgun_secondary_melee_delay 0.25 // 0.35 was too slow
+set g_balance_shotgun_secondary_melee_range 120
+set g_balance_shotgun_secondary_melee_swing_side 120
+set g_balance_shotgun_secondary_melee_swing_up 30
+set g_balance_shotgun_secondary_melee_time 0.15
+set g_balance_shotgun_secondary_melee_traces 10
+set g_balance_shotgun_secondary_melee_no_doubleslap 1
+set g_balance_shotgun_secondary_melee_nonplayerdamage 40
+set g_balance_shotgun_secondary_melee_multihit 1
+set g_balance_shotgun_secondary_damage 80
+set g_balance_shotgun_secondary_force 200
+set g_balance_shotgun_secondary_refire 1.25
+set g_balance_shotgun_secondary_animtime 1
+set g_balance_shotgun_switchdelay_drop 0
+set g_balance_shotgun_switchdelay_raise 0
+set g_balance_shotgun_reload_ammo 0 //default: 5
+set g_balance_shotgun_reload_time 2
+// }}}
+// {{{ uzi
+set g_balance_uzi_mode 1                               // Activates varible spread for sustained & burst mode secondary
+set g_balance_uzi_spread_min 0
+set g_balance_uzi_spread_max 0
+set g_balance_uzi_spread_add 0
+
+set g_balance_uzi_burst 3                              // # of bullets in a burst (if set to 2 or more)
+set g_balance_uzi_burst_animtime 0.3
+set g_balance_uzi_burst_refire 0.06            // refire between burst bullets
+set g_balance_uzi_burst_refire2 0.45   // refire after burst
+set g_balance_uzi_burst_spread 0.03
+set g_balance_uzi_burst_damage 25
+set g_balance_uzi_burst_force 20
+set g_balance_uzi_burst_ammo 3
+
+set g_balance_uzi_first 1
+set g_balance_uzi_first_damage 14
+set g_balance_uzi_first_force 5
+set g_balance_uzi_first_spread 0.03
+set g_balance_uzi_first_refire 0.4
+set g_balance_uzi_first_ammo 1
+
+set g_balance_uzi_sustained_damage 12
+set g_balance_uzi_sustained_force 5
+set g_balance_uzi_sustained_spread 0
+set g_balance_uzi_sustained_refire 0.1
+set g_balance_uzi_sustained_ammo 1
+
+set g_balance_uzi_speed 18000
+set g_balance_uzi_bulletconstant 115 // 13.1qu
+
+set g_balance_uzi_switchdelay_drop 0
+set g_balance_uzi_switchdelay_raise 0
+
+set g_balance_uzi_reload_ammo 0 //default: 30
+set g_balance_uzi_reload_time 2
+// }}}
+// {{{ mortar
+set g_balance_grenadelauncher_primary_type 0
+set g_balance_grenadelauncher_primary_damage 50
+set g_balance_grenadelauncher_primary_edgedamage 25
+set g_balance_grenadelauncher_primary_force 250
+set g_balance_grenadelauncher_primary_radius 100
+set g_balance_grenadelauncher_primary_speed 2000
+set g_balance_grenadelauncher_primary_speed_up 200
+set g_balance_grenadelauncher_primary_speed_z 0
+set g_balance_grenadelauncher_primary_spread 0
+set g_balance_grenadelauncher_primary_lifetime 5
+set g_balance_grenadelauncher_primary_lifetime2 1
+set g_balance_grenadelauncher_primary_refire 0.7
+set g_balance_grenadelauncher_primary_animtime 0.3
+set g_balance_grenadelauncher_primary_ammo 2
+set g_balance_grenadelauncher_primary_health 0
+set g_balance_grenadelauncher_primary_damageforcescale 0
+set g_balance_grenadelauncher_primary_remote_minbouncecnt 0
+
+set g_balance_grenadelauncher_secondary_type 1
+set g_balance_grenadelauncher_secondary_damage 60
+set g_balance_grenadelauncher_secondary_edgedamage 30
+set g_balance_grenadelauncher_secondary_force 300
+set g_balance_grenadelauncher_secondary_radius 200
+set g_balance_grenadelauncher_secondary_speed 800
+set g_balance_grenadelauncher_secondary_speed_up 0
+set g_balance_grenadelauncher_secondary_speed_z 200
+set g_balance_grenadelauncher_secondary_spread 0
+set g_balance_grenadelauncher_secondary_lifetime 8
+set g_balance_grenadelauncher_secondary_lifetime_bounce 0.5
+set g_balance_grenadelauncher_secondary_lifetime_stick 0
+set g_balance_grenadelauncher_secondary_refire 0.7
+set g_balance_grenadelauncher_secondary_animtime 0.5
+set g_balance_grenadelauncher_secondary_ammo 2
+set g_balance_grenadelauncher_secondary_health 0
+set g_balance_grenadelauncher_secondary_damageforcescale 0
+set g_balance_grenadelauncher_secondary_remote_detonateprimary 0
+
+set g_balance_grenadelauncher_bouncefactor 0.5
+set g_balance_grenadelauncher_bouncestop 0.075
+
+set g_balance_grenadelauncher_switchdelay_drop 0
+set g_balance_grenadelauncher_switchdelay_raise 0
+
+set g_balance_grenadelauncher_reload_ammo 0 //default: 12
+set g_balance_grenadelauncher_reload_time 2
+// }}}
+// {{{ electro
+set g_balance_electro_lightning 0
+set g_balance_electro_primary_damage 55
+set g_balance_electro_primary_edgedamage 27.5
+set g_balance_electro_primary_force 200
+set g_balance_electro_primary_force_up 0
+set g_balance_electro_primary_radius 100
+set g_balance_electro_primary_comboradius 150
+set g_balance_electro_primary_speed 2500
+set g_balance_electro_primary_spread 0
+set g_balance_electro_primary_lifetime 5
+set g_balance_electro_primary_refire 0.6
+set g_balance_electro_primary_animtime 0.1
+set g_balance_electro_primary_ammo 4
+set g_balance_electro_primary_range 0
+set g_balance_electro_primary_falloff_mindist 255 // 0.3 * radius
+set g_balance_electro_primary_falloff_maxdist 850
+set g_balance_electro_primary_falloff_halflifedist 425
+set g_balance_electro_secondary_damage 40
+set g_balance_electro_secondary_edgedamage 20
+set g_balance_electro_secondary_force 200
+set g_balance_electro_secondary_radius 150
+set g_balance_electro_secondary_speed 900
+set g_balance_electro_secondary_speed_up 200
+set g_balance_electro_secondary_speed_z 0
+set g_balance_electro_secondary_spread 0.05
+set g_balance_electro_secondary_lifetime 3
+set g_balance_electro_secondary_refire 0.2
+set g_balance_electro_secondary_refire2 1.5
+set g_balance_electro_secondary_animtime 0.2
+set g_balance_electro_secondary_ammo 2
+set g_balance_electro_secondary_health 5
+set g_balance_electro_secondary_damageforcescale 4
+set g_balance_electro_secondary_damagedbycontents 1
+set g_balance_electro_secondary_count 3
+set g_balance_electro_secondary_bouncefactor 0.4
+set g_balance_electro_secondary_bouncestop 0.05
+set g_balance_electro_combo_damage 40
+set g_balance_electro_combo_edgedamage 20
+set g_balance_electro_combo_force 120
+set g_balance_electro_combo_radius 175
+set g_balance_electro_combo_comboradius 275
+set g_balance_electro_combo_speed 2000
+set g_balance_electro_combo_safeammocheck 1
+set g_balance_electro_switchdelay_drop 0
+set g_balance_electro_switchdelay_raise 0
+set g_balance_electro_reload_ammo 0 //default: 20
+set g_balance_electro_reload_time 2
+// }}}
+// {{{ crylink
+set g_balance_crylink_primary_damage 12
+set g_balance_crylink_primary_edgedamage 6
+set g_balance_crylink_primary_force -50
+set g_balance_crylink_primary_radius 80
+set g_balance_crylink_primary_speed 2000
+set g_balance_crylink_primary_spread 0.08
+set g_balance_crylink_primary_shots 6
+set g_balance_crylink_primary_bounces 1
+set g_balance_crylink_primary_refire 0.7
+set g_balance_crylink_primary_animtime 0.3
+set g_balance_crylink_primary_ammo 3
+set g_balance_crylink_primary_bouncedamagefactor 0.5
+set g_balance_crylink_primary_joindelay 0.1
+set g_balance_crylink_primary_joinspread 0.2
+set g_balance_crylink_primary_joinexplode 1
+set g_balance_crylink_primary_joinexplode_damage 0
+set g_balance_crylink_primary_joinexplode_edgedamage 0
+set g_balance_crylink_primary_joinexplode_radius 0
+set g_balance_crylink_primary_joinexplode_force 0
+set g_balance_crylink_primary_linkexplode 1
+
+set g_balance_crylink_primary_middle_lifetime 5 // range: 35000 full, fades to 70000
+set g_balance_crylink_primary_middle_fadetime 5
+set g_balance_crylink_primary_other_lifetime 5
+set g_balance_crylink_primary_other_fadetime 5
+
+set g_balance_crylink_secondary 1
+set g_balance_crylink_secondary_damage 10
+set g_balance_crylink_secondary_edgedamage 5
+set g_balance_crylink_secondary_force -150
+set g_balance_crylink_secondary_radius 100
+set g_balance_crylink_secondary_speed 3000
+set g_balance_crylink_secondary_spread 0.01
+set g_balance_crylink_secondary_spreadtype 1
+set g_balance_crylink_secondary_shots 5
+set g_balance_crylink_secondary_bounces 0
+set g_balance_crylink_secondary_refire 0.7
+set g_balance_crylink_secondary_animtime 0.2
+set g_balance_crylink_secondary_ammo 2
+set g_balance_crylink_secondary_bouncedamagefactor 0.5
+set g_balance_crylink_secondary_joindelay 0
+set g_balance_crylink_secondary_joinspread 0
+set g_balance_crylink_secondary_joinexplode 0
+set g_balance_crylink_secondary_joinexplode_damage 0
+set g_balance_crylink_secondary_joinexplode_edgedamage 0
+set g_balance_crylink_secondary_joinexplode_radius 0
+set g_balance_crylink_secondary_joinexplode_force 0
+set g_balance_crylink_secondary_linkexplode 1
+
+set g_balance_crylink_secondary_middle_lifetime 5 // range: 35000 full, fades to 70000
+set g_balance_crylink_secondary_middle_fadetime 5
+set g_balance_crylink_secondary_line_lifetime 5
+set g_balance_crylink_secondary_line_fadetime 5
+
+set g_balance_crylink_switchdelay_drop 0
+set g_balance_crylink_switchdelay_raise 0
+
+set g_balance_crylink_reload_ammo 0 //default: 10
+set g_balance_crylink_reload_time 2
+// }}}
+// {{{ nex
+set g_balance_nex_primary_damage 90
+set g_balance_nex_primary_force 400
+set g_balance_nex_primary_refire 1.5
+set g_balance_nex_primary_animtime 0.4
+set g_balance_nex_primary_ammo 6
+set g_balance_nex_primary_damagefalloff_mindist 0 // 1000    For tZork ;3
+set g_balance_nex_primary_damagefalloff_maxdist 0 // 3000
+set g_balance_nex_primary_damagefalloff_halflife 0 // 1500
+set g_balance_nex_primary_damagefalloff_forcehalflife 0 // 1500
+
+set g_balance_nex_secondary 0
+set g_balance_nex_secondary_charge 0
+set g_balance_nex_secondary_charge_rate 0.1
+set g_balance_nex_secondary_chargepool 0
+set g_balance_nex_secondary_chargepool_regen 0.15
+set g_balance_nex_secondary_chargepool_pause_regen 1
+set g_balance_nex_secondary_chargepool_pause_health_regen 1
+set g_balance_nex_secondary_damage 0
+set g_balance_nex_secondary_force 0
+set g_balance_nex_secondary_refire 0
+set g_balance_nex_secondary_animtime 0
+set g_balance_nex_secondary_ammo 2
+set g_balance_nex_secondary_damagefalloff_mindist 0
+set g_balance_nex_secondary_damagefalloff_maxdist 0
+set g_balance_nex_secondary_damagefalloff_halflife 0
+set g_balance_nex_secondary_damagefalloff_forcehalflife 0
+
+set g_balance_nex_charge 1
+set g_balance_nex_charge_mindmg 40
+set g_balance_nex_charge_start 0.5
+set g_balance_nex_charge_rate 0.4
+set g_balance_nex_charge_animlimit 0.5
+set g_balance_nex_charge_limit 1
+set g_balance_nex_charge_rot_rate 0
+set g_balance_nex_charge_rot_pause 0 // Dont rot down untill this long after release of charge button
+set g_balance_nex_charge_shot_multiplier 0
+set g_balance_nex_charge_velocity_rate 0
+set g_balance_nex_charge_minspeed 400
+set g_balance_nex_charge_maxspeed 800
+
+set g_balance_nex_switchdelay_drop 0
+set g_balance_nex_switchdelay_raise 0
+
+set g_balance_nex_reload_ammo 0 //default: 25
+set g_balance_nex_reload_time 2
+// }}}
+// {{{ minstanex
+set g_balance_minstanex_refire 1
+set g_balance_minstanex_animtime 0.3
+set g_balance_minstanex_ammo 10
+set g_balance_minstanex_laser_ammo 0
+set g_balance_minstanex_laser_animtime 0.3
+set g_balance_minstanex_laser_refire 0.7
+set g_balance_minstanex_switchdelay_drop 0
+set g_balance_minstanex_switchdelay_raise 0
+set g_balance_minstanex_reload_ammo 0 //default: 50
+set g_balance_minstanex_reload_time 2
+// }}}
+// {{{ hagar
+set g_balance_hagar_primary_damage 25
+set g_balance_hagar_primary_edgedamage 12.5
+set g_balance_hagar_primary_force 92
+set g_balance_hagar_primary_health 15
+set g_balance_hagar_primary_damageforcescale 0
+set g_balance_hagar_primary_radius 25
+set g_balance_hagar_primary_spread 0.03
+set g_balance_hagar_primary_speed 2000
+set g_balance_hagar_primary_lifetime 5
+set g_balance_hagar_primary_refire 0.11
+set g_balance_hagar_primary_ammo 1
+set g_balance_hagar_secondary 0
+set g_balance_hagar_secondary_load 1
+set g_balance_hagar_secondary_load_speed 0.5
+set g_balance_hagar_secondary_load_spread 0.075
+set g_balance_hagar_secondary_load_spread_bias 0.5
+set g_balance_hagar_secondary_load_max 4
+set g_balance_hagar_secondary_load_hold 4
+set g_balance_hagar_secondary_load_releasedeath 0
+set g_balance_hagar_secondary_load_abort 0
+set g_balance_hagar_secondary_load_linkexplode 0
+set g_balance_hagar_secondary_load_animtime 0.2
+set g_balance_hagar_secondary_damage 40
+set g_balance_hagar_secondary_edgedamage 20
+set g_balance_hagar_secondary_force 75
+set g_balance_hagar_secondary_health 15
+set g_balance_hagar_secondary_damageforcescale 0
+set g_balance_hagar_secondary_radius 80
+set g_balance_hagar_secondary_spread 0.05
+set g_balance_hagar_secondary_speed 2000
+set g_balance_hagar_secondary_lifetime_min 10
+set g_balance_hagar_secondary_lifetime_rand 0
+set g_balance_hagar_secondary_refire 0.5
+set g_balance_hagar_secondary_ammo 1
+set g_balance_hagar_switchdelay_drop 0
+set g_balance_hagar_switchdelay_raise 0
+set g_balance_hagar_reload_ammo 0 //default: 25
+set g_balance_hagar_reload_time 2
+// }}}
+// {{{ rocketlauncher
+set g_balance_rocketlauncher_damage 80
+set g_balance_rocketlauncher_edgedamage 40
+set g_balance_rocketlauncher_force 350
+set g_balance_rocketlauncher_radius 110
+set g_balance_rocketlauncher_speed 1000
+set g_balance_rocketlauncher_speedaccel 0
+set g_balance_rocketlauncher_speedstart 1000
+set g_balance_rocketlauncher_lifetime 100
+set g_balance_rocketlauncher_refire 0.9
+set g_balance_rocketlauncher_animtime 0.7
+set g_balance_rocketlauncher_ammo 4
+set g_balance_rocketlauncher_health 0 // 30 // 5 hitpoints above maximum laser value -- this way lasers can't blow it up, but grenadelauncher still can most the time.
+set g_balance_rocketlauncher_damageforcescale 0 // low damage force scale so that it can still be affected by other hits, but not so much that it does a 90 degree turn
+set g_balance_rocketlauncher_detonatedelay 999 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time
+set g_balance_rocketlauncher_guiderate 0 // max degrees per second
+set g_balance_rocketlauncher_guideratedelay 0.01 // immediate
+set g_balance_rocketlauncher_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic)
+set g_balance_rocketlauncher_guidedelay 0.2 // delay before guiding kicks in
+set g_balance_rocketlauncher_guidestop 1 // stop guiding when firing again
+set g_balance_rocketlauncher_remote_damage 70
+set g_balance_rocketlauncher_remote_edgedamage 35
+set g_balance_rocketlauncher_remote_radius 110
+set g_balance_rocketlauncher_remote_force 350
+set g_balance_rocketlauncher_switchdelay_drop 0
+set g_balance_rocketlauncher_switchdelay_raise 0
+set g_balance_rocketlauncher_reload_ammo 0 //default: 25
+set g_balance_rocketlauncher_reload_time 2
+// }}}
+// {{{ porto
+set g_balance_porto_primary_refire 1.5
+set g_balance_porto_primary_animtime 0.3
+set g_balance_porto_primary_speed 5000
+set g_balance_porto_primary_lifetime 5
+set g_balance_porto_secondary 1
+set g_balance_porto_secondary_refire 1.5
+set g_balance_porto_secondary_animtime 0.3
+set g_balance_porto_secondary_speed 1000
+set g_balance_porto_secondary_lifetime 5
+set g_balance_porto_switchdelay_drop 0
+set g_balance_porto_switchdelay_raise 0
+set g_balance_portal_health 200 // these get recharged whenever the portal is used
+set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used
+// }}}
+// {{{ hook
+set g_balance_hook_primary_fuel 5 // hook monkeys set 0
+set g_balance_hook_primary_refire 0 // hook monkeys set 0
+set g_balance_hook_primary_animtime 0.3 // good shoot anim
+set g_balance_hook_primary_hooked_time_max 0 // infinite
+set g_balance_hook_primary_hooked_time_free 2 // 2s being hooked are free
+set g_balance_hook_primary_hooked_fuel 5 // fuel per second hooked
+set g_balance_hook_secondary_damage 25 // not much
+set g_balance_hook_secondary_edgedamage 5 // not much
+set g_balance_hook_secondary_radius 500 // LOTS
+set g_balance_hook_secondary_force -2000 // LOTS
+set g_balance_hook_secondary_ammo 50 // a whole pack
+set g_balance_hook_secondary_lifetime 5 // infinite
+set g_balance_hook_secondary_speed 0 // not much throwing
+set g_balance_hook_secondary_gravity 5 // fast falling
+set g_balance_hook_secondary_refire 3 // don't drop too many bombs...
+set g_balance_hook_secondary_animtime 0.3 // good shoot anim
+set g_balance_hook_secondary_power 3 // effect behaves like a square function
+set g_balance_hook_secondary_duration 1.5 // effect runs for three seconds
+set g_balance_hook_secondary_health 15
+set g_balance_hook_secondary_damageforcescale 0
+set g_balance_hook_switchdelay_drop 0
+set g_balance_hook_switchdelay_raise 0
+// }}}
+// {{{ tuba
+set g_balance_tuba_refire 0.05
+set g_balance_tuba_animtime 0.05
+set g_balance_tuba_attenuation 0.5
+set g_balance_tuba_volume 1
+set g_balance_tuba_fadetime 0.25
+set g_balance_tuba_damage 5
+set g_balance_tuba_edgedamage 0
+set g_balance_tuba_radius 200
+set g_balance_tuba_force 40
+set g_balance_tuba_pitchstep 6
+set g_balance_tuba_switchdelay_drop 0
+set g_balance_tuba_switchdelay_raise 0
+// }}}
+// {{{ fireball // this is a superweapon -- lets make it behave as one.
+set g_balance_fireball_primary_animtime 0.2
+set g_balance_fireball_primary_bfgdamage 100
+set g_balance_fireball_primary_bfgforce 0
+set g_balance_fireball_primary_bfgradius 1000
+set g_balance_fireball_primary_damage 200
+set g_balance_fireball_primary_damageforcescale 0
+set g_balance_fireball_primary_edgedamage 50
+set g_balance_fireball_primary_force 600
+set g_balance_fireball_primary_health 0
+set g_balance_fireball_primary_laserburntime 0.5
+set g_balance_fireball_primary_laserdamage 80
+set g_balance_fireball_primary_laseredgedamage 20
+set g_balance_fireball_primary_laserradius 256
+set g_balance_fireball_primary_lifetime 15
+set g_balance_fireball_primary_radius 200
+set g_balance_fireball_primary_refire 2
+set g_balance_fireball_primary_refire2 0
+set g_balance_fireball_primary_speed 1200
+set g_balance_fireball_primary_spread 0
+set g_balance_fireball_secondary_animtime 0.3
+set g_balance_fireball_secondary_damage 40
+set g_balance_fireball_secondary_damageforcescale 4
+set g_balance_fireball_secondary_damagetime 5
+set g_balance_fireball_secondary_force 100
+set g_balance_fireball_secondary_laserburntime 0.5
+set g_balance_fireball_secondary_laserdamage 50
+set g_balance_fireball_secondary_laseredgedamage 20
+set g_balance_fireball_secondary_laserradius 110
+set g_balance_fireball_secondary_lifetime 7
+set g_balance_fireball_secondary_refire 1.5
+set g_balance_fireball_secondary_speed 900
+set g_balance_fireball_secondary_speed_up 100
+set g_balance_fireball_secondary_speed_z 0
+set g_balance_fireball_secondary_spread 0
+set g_balance_fireball_switchdelay_drop 0
+set g_balance_fireball_switchdelay_raise 0
+// }}}
diff --git a/oldbalanceXPM.cfg b/oldbalanceXPM.cfg
new file mode 100644 (file)
index 0000000..9c90e04
--- /dev/null
@@ -0,0 +1,781 @@
+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 70
+set g_balance_rocketlauncher_edgedamage 35
+set g_balance_rocketlauncher_force 450
+set g_balance_rocketlauncher_radius 110
+set g_balance_rocketlauncher_speed 1300
+set g_balance_rocketlauncher_speedaccel 1300
+set g_balance_rocketlauncher_speedstart 1000
+set g_balance_rocketlauncher_lifetime 10
+set g_balance_rocketlauncher_refire 1.2
+set g_balance_rocketlauncher_animtime 0.4
+set g_balance_rocketlauncher_ammo 4
+set g_balance_rocketlauncher_health 30 // 30 // 5 hitpoints above maximum laser value -- this way lasers can't blow it up, but grenadelauncher still can most the time.
+set g_balance_rocketlauncher_damageforcescale 1 // low damage force scale so that it can still be affected by other hits, but not so much that it does a 90 degree turn
+set g_balance_rocketlauncher_detonatedelay 0.02 // positive: timer till detonation is allowed, negative: "security device" that prevents ANY remote detonation if it could hurt its owner, zero: detonatable at any time
+set g_balance_rocketlauncher_guiderate 70 // max degrees per second
+set g_balance_rocketlauncher_guideratedelay 0.01 // immediate
+set g_balance_rocketlauncher_guidegoal 512 // goal distance for (non-laser) guiding (higher = less control, lower = erratic)
+set g_balance_rocketlauncher_guidedelay 0.2 // delay before guiding kicks in
+set g_balance_rocketlauncher_guidestop 0 // stop guiding when firing again
+set g_balance_rocketlauncher_remote_damage 70
+set g_balance_rocketlauncher_remote_edgedamage 35
+set g_balance_rocketlauncher_remote_radius 110
+set g_balance_rocketlauncher_remote_force 400
+set g_balance_rocketlauncher_switchdelay_drop 0.3
+set g_balance_rocketlauncher_switchdelay_raise 0.2
+set g_balance_rocketlauncher_reload_ammo 0 //default: 25
+set g_balance_rocketlauncher_reload_time 2
+// }}}
+// {{{ porto
+set g_balance_porto_primary_refire 1.5
+set g_balance_porto_primary_animtime 0.3
+set g_balance_porto_primary_speed 1000
+set g_balance_porto_primary_lifetime 5
+set g_balance_porto_secondary 1
+set g_balance_porto_secondary_refire 1.5
+set g_balance_porto_secondary_animtime 0.3
+set g_balance_porto_secondary_speed 1000
+set g_balance_porto_secondary_lifetime 5
+set g_balance_porto_switchdelay_drop 0.2
+set g_balance_porto_switchdelay_raise 0.2
+set g_balance_portal_health 200 // these get recharged whenever the portal is used
+set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used
+// }}}
+// {{{ hook
+set g_balance_hook_primary_fuel 5 // hook monkeys set 0
+set g_balance_hook_primary_refire 0 // 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
+// }}}
index b89a177f043a465c99c5fc7812a8b0d1eb83f3c3..4a206e55f4d6aee3496c4e55903e30a7b28e96a8 100644 (file)
@@ -224,7 +224,8 @@ float tempdb;
 float ClientProgsDB;
 vector hook_shotorigin[4];
 vector electro_shotorigin[4];
-vector gauntlet_shotorigin[4];
+vector lightning_shotorigin[4];
+
 
 #ifdef BLURTEST
 float blurtest_time0, blurtest_time1, blurtest_radius, blurtest_power;
index a06eb5e6584e3ac96f48c489b762ee748ce9a127..ca0c467768da675680410888c683a0c49117c724 100644 (file)
@@ -120,7 +120,6 @@ void CSQC_Init(void)
        Hook_Precache();
        GibSplash_Precache();
        Casings_Precache();
-       DamageInfo_Precache();
        Vehicles_Precache();
        turrets_precache();
        Tuba_Precache();
@@ -818,8 +817,8 @@ void CSQC_Ent_Update(float bIsNewEntity)
                case ENT_CLIENT_WARPZONE_TELEPORTED: WarpZone_Teleported_Read(bIsNewEntity); break;
                case ENT_CLIENT_TRIGGER_MUSIC: Ent_ReadTriggerMusic(); break;
                case ENT_CLIENT_HOOK: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_HOOK); break;
-               case ENT_CLIENT_LGBEAM: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_LGBEAM); break;
-               case ENT_CLIENT_GAUNTLET: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_GAUNTLET); break;
+               case ENT_CLIENT_ELECTRO_BEAM: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_ELECTRO_BEAM); break;
+               case ENT_CLIENT_ARC_BEAM: Ent_ReadHook(bIsNewEntity, ENT_CLIENT_ARC_BEAM); break;
                case ENT_CLIENT_ACCURACY: Ent_ReadAccuracy(); break;
                case ENT_CLIENT_AUXILIARYXHAIR: Net_AuXair2(bIsNewEntity); break;
                case ENT_CLIENT_TURRET: ent_turret(); break;
@@ -959,10 +958,10 @@ void Ent_Init()
        electro_shotorigin[1] = decompressShotOrigin(ReadInt24_t());
        electro_shotorigin[2] = decompressShotOrigin(ReadInt24_t());
        electro_shotorigin[3] = decompressShotOrigin(ReadInt24_t());
-       gauntlet_shotorigin[0] = decompressShotOrigin(ReadInt24_t());
-       gauntlet_shotorigin[1] = decompressShotOrigin(ReadInt24_t());
-       gauntlet_shotorigin[2] = decompressShotOrigin(ReadInt24_t());
-       gauntlet_shotorigin[3] = decompressShotOrigin(ReadInt24_t());
+       arc_shotorigin[0] = decompressShotOrigin(ReadInt24_t());
+       arc_shotorigin[1] = decompressShotOrigin(ReadInt24_t());
+       arc_shotorigin[2] = decompressShotOrigin(ReadInt24_t());
+       arc_shotorigin[3] = decompressShotOrigin(ReadInt24_t());
 
        if(forcefog)
                strunzone(forcefog);
@@ -1228,8 +1227,8 @@ float CSQC_Parse_TempEntity()
                        Net_TeamNagger();
                        bHandled = true;
                        break;
-               case TE_CSQC_LIGHTNINGARC:
-                       Net_ReadLightningarc();
+               case TE_CSQC_ARC:
+                       Net_ReadArc();
                        bHandled = true;
                        break;
                case TE_CSQC_PINGPLREPORT:
@@ -1248,6 +1247,10 @@ float CSQC_Parse_TempEntity()
                        cl_notice_read();
                        bHandled = true;
                        break;
+               case TE_CSQC_SHOCKWAVEPARTICLE:
+                       Net_ReadShockwaveParticle();
+                       bHandled = true;
+                       break;
                default:
                        // No special logic for this temporary entity; return 0 so the engine can handle it
                        bHandled = false;
index 8c9d59dd91f9a77e421d746a0b8193a8c449fb0f..d18eba00624e3360a28bc2bfdba6377a1abb31a2 100644 (file)
@@ -269,7 +269,7 @@ float TrueAimCheck()
        ta = trueaim;
        mv = MOVE_NOMONSTERS;
 
-       switch(activeweapon)
+       switch(activeweapon) // WEAPONTODO
        {
                case WEP_TUBA: // no aim
                case WEP_PORTO: // shoots from eye
@@ -289,7 +289,7 @@ float TrueAimCheck()
                                return EnemyHitCheck();
                        }
                        break;
-               case WEP_ROCKET_LAUNCHER: // projectile has a size!
+               case WEP_DEVASTATOR: // projectile has a size!
                        mi = '-3 -3 -3';
                        ma = '3 3 3';
                        break;
index cc3653db4d2deb21a79aaa188112f83b7e3ae6ec..66f1359fd396f1d367bd93af2a7bee82cbc77e8f 100644 (file)
@@ -350,13 +350,6 @@ void Ent_DamageInfo(float isNew)
                        w_backoff = -1 * normalize(force);
                setorigin(self, w_org + w_backoff * 2); // for sound() calls
 
-               (get_weaponinfo(hitwep)).weapon_func(WR_IMPACTEFFECT);
+               WEP_ACTION(hitwep, WR_IMPACTEFFECT);
        }
 }
-
-void DamageInfo_Precache()
-{
-       float i;
-       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
-               (get_weaponinfo(i)).weapon_func(WR_PRECACHE);
-}
index 8c237aade92621df131a49b4ef079a10760e1326..c35a3a94a7577dc7724a3ebe0330e08cf6c19bf3 100644 (file)
@@ -95,3 +95,4 @@ void Net_ReadLightningarc()
     }
 
 }
+void Net_ReadArc() { Net_ReadLightningarc(); }
index 196730a72dde016cbe3fd28308955ed2c2de6ee1..8beacac88befc88132a24d36e4f3fa3b0b0967c3 100644 (file)
@@ -75,11 +75,11 @@ void Draw_GrapplingHook()
                case ENT_CLIENT_HOOK:
                        vs = hook_shotorigin[s];
                        break;
-               case ENT_CLIENT_LGBEAM:
+               case ENT_CLIENT_ELECTRO_BEAM:
                        vs = electro_shotorigin[s];
                        break;
-               case ENT_CLIENT_GAUNTLET:
-                       vs = gauntlet_shotorigin[s];
+               case ENT_CLIENT_ARC_BEAM:
+                       vs = lightning_shotorigin[s];
                        break;
        }
 
@@ -92,8 +92,8 @@ void Draw_GrapplingHook()
                                a = view_origin + view_forward * vs_x + view_right * -vs_y + view_up * vs_z;
                                b = self.origin;
                                break;
-                       case ENT_CLIENT_LGBEAM:
-                       case ENT_CLIENT_GAUNTLET:
+                       case ENT_CLIENT_ELECTRO_BEAM:
+                       case ENT_CLIENT_ARC_BEAM:
                                if(self.HookRange)
                                        b = view_origin + view_forward * self.HookRange;
                                else
@@ -113,8 +113,8 @@ void Draw_GrapplingHook()
                                a = self.velocity;
                                b = self.origin;
                                break;
-                       case ENT_CLIENT_LGBEAM:
-                       case ENT_CLIENT_GAUNTLET:
+                       case ENT_CLIENT_ELECTRO_BEAM:
+                       case ENT_CLIENT_ARC_BEAM:
                                a = self.origin;
                                b = self.velocity;
                                break;
@@ -155,16 +155,16 @@ void Draw_GrapplingHook()
                                rgb = '.3 1 .3';
                        }
                        break;
-               case ENT_CLIENT_LGBEAM:
+               case ENT_CLIENT_ELECTRO_BEAM:
                        intensity = bound(0.2, 1 + Noise_Pink(self, frametime) * 1 + Noise_Burst(self, frametime, 0.03) * 0.3, 2);
                        offset = Noise_Brown(self, frametime) * 10;
                        tex = "particles/lgbeam";
                        rgb = '1 1 1';
                        break;
-               case ENT_CLIENT_GAUNTLET:
-                       intensity = 1;
-                       offset = Noise_White(self, frametime);
-                       tex = "particles/gauntletbeam";
+               case ENT_CLIENT_ARC_BEAM: // todo
+                       intensity = bound(0.2, 1 + Noise_Pink(self, frametime) * 1 + Noise_Burst(self, frametime, 0.03) * 0.3, 2);
+                       offset = Noise_Brown(self, frametime) * 10;
+                       tex = "particles/lgbeam";
                        rgb = '1 1 1';
                        break;
        }
@@ -193,8 +193,8 @@ void Draw_GrapplingHook()
                                self.drawmask = 0;
                        }
                        break;
-               case ENT_CLIENT_LGBEAM:
-               case ENT_CLIENT_GAUNTLET:
+               case ENT_CLIENT_ELECTRO_BEAM:
+               case ENT_CLIENT_ARC_BEAM:
                        setorigin(self, a); // beam origin!
                        break;
        }
@@ -204,11 +204,11 @@ void Draw_GrapplingHook()
                default:
                case ENT_CLIENT_HOOK:
                        break;
-               case ENT_CLIENT_LGBEAM:
-                       pointparticles(particleeffectnum("electro_lightning"), trace_endpos, normalize(atrans - trace_endpos), frametime * intensity);
+               case ENT_CLIENT_ELECTRO_BEAM:
+                       pointparticles(particleeffectnum("electro_lightning"), trace_endpos, normalize(atrans - trace_endpos), frametime * intensity); // todo: new effect
                        break;
-               case ENT_CLIENT_GAUNTLET:
-                       pointparticles(particleeffectnum("gauntlet_lightning"), b, normalize(a - b), frametime * intensity);
+               case ENT_CLIENT_ARC_BEAM:
+                       pointparticles(particleeffectnum("electro_lightning"), trace_endpos, normalize(atrans - trace_endpos), frametime * intensity); // todo: new effect
                        break;
        }
 }
@@ -237,10 +237,10 @@ void Ent_ReadHook(float bIsNew, float type)
                {
                        default:
                        case ENT_CLIENT_HOOK:
-                       case ENT_CLIENT_GAUNTLET:
                                self.HookRange = 0;
                                break;
-                       case ENT_CLIENT_LGBEAM:
+                       case ENT_CLIENT_ELECTRO_BEAM:
+                       case ENT_CLIENT_ARC_BEAM:
                                self.HookRange = ReadCoord();
                                break;
                }
@@ -274,11 +274,11 @@ void Ent_ReadHook(float bIsNew, float type)
                                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, 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, ATTEN_NORM);
                                break;
                }
        }
@@ -289,7 +289,6 @@ void Ent_ReadHook(float bIsNew, float type)
 void Hook_Precache()
 {
        precache_sound("weapons/lgbeam_fly.wav");
-       precache_sound("weapons/gauntletbeam_fly.wav");
        precache_model("models/hook.md3");
 }
 
index a78177b1991c5783e5e9b185cc5225d6a0d447d3..b9e3ca6f1f2a73f9ac579bb1958b4ffb04517463 100644 (file)
@@ -421,7 +421,7 @@ float GetAmmoStat(float i)
        }
 }
 
-float GetAmmoTypeForWep(float i)
+float GetAmmoTypeForWep(float i) // WEAPONTODO
 {
        switch(i)
        {
@@ -436,7 +436,7 @@ float GetAmmoTypeForWep(float i)
                case WEP_NEX: return 3;
                case WEP_RIFLE: return 1;
                case WEP_HAGAR: return 2;
-               case WEP_ROCKET_LAUNCHER: return 2;
+               case WEP_DEVASTATOR: return 2;
                case WEP_SEEKER: return 2;
                case WEP_FIREBALL: return 4;
                case WEP_HOOK: return 3;
index 2c2fc56df0983219934ebbdd062dda2ca767fc6e..569073694db352e60653334104b6ba971e3dee4a 100644 (file)
@@ -573,6 +573,20 @@ vector getplayerorigin(float pl)
        return GETPLAYERORIGIN_ERROR;
 }
 
+vector getcsqcplayercolor(float pl)
+{
+       entity e;
+       
+       e = CSQCModel_server2csqc(pl);
+       if(e)
+       {
+               if(e.colormap > 0)
+                       return colormapPaletteColor(((e.colormap >= 1024) ? e.colormap : stof(getplayerkeyvalue(e.colormap - 1, "colors"))) & 0x0F, TRUE);
+       }
+       
+       return '1 1 1';
+}
+
 float getplayerisdead(float pl)
 {
        entity e;
index ce8b6dd7f9ecbe612a0aeb52d87650633d6228e7..2f05e1d1c4f00c8106a34154b386099dcefcb7fe 100644 (file)
@@ -302,3 +302,121 @@ void Net_ReadNexgunBeamParticle()
        else
                WarpZone_TrailParticles_WithMultiplier(world, particleeffectnum("nex_beam"), shotorg, endpos, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE);
 }
+
+.vector sw_shotorg;
+.vector sw_endpos;
+.float sw_spread_max;
+.float sw_spread_min;
+.float sw_time;
+
+void Draw_Shockwave()
+{
+       float a = bound(0, (0.5 - ((time - self.sw_time) / 0.4)), 0.5);
+
+       if not(a) { remove(self); }
+       
+       vector deviation, angle;
+
+       vector sw_color = getcsqcplayercolor(self.sv_entnum); // GetTeamRGB(GetPlayerColor(self.sv_entnum));
+
+       vector first_min_end = '0 0 0', prev_min_end = '0 0 0', new_min_end = '0 0 0';
+       vector first_max_end = '0 0 0', prev_max_end = '0 0 0', new_max_end = '0 0 0';
+
+       float new_max_dist, new_min_dist;
+       
+       vector shotdir = normalize(self.sw_endpos - self.sw_shotorg);
+       vectorvectors(shotdir);
+       vector right = v_right;
+       vector up = v_up;
+       
+       float counter, dist_before_normal = 200, shots = 20;
+       
+       vector min_end = ((self.sw_shotorg + (shotdir * dist_before_normal)) + (up * self.sw_spread_min));
+       vector max_end = (self.sw_endpos + (up * self.sw_spread_max));
+       
+       float spread_to_min = vlen(normalize(min_end - self.sw_shotorg) - shotdir);
+       float spread_to_max = vlen(normalize(max_end - min_end) - shotdir);
+       
+       for(counter = 0; counter < shots; ++counter)
+       {
+               // perfect circle effect lines
+               angle = '0 0 0';
+               makevectors('0 360 0' * (0.75 + (counter - 0.5) / shots));
+               angle_y = v_forward_x;
+               angle_z = v_forward_y;
+
+               // first do the spread_to_min effect
+               deviation = angle * spread_to_min;
+               deviation = ((shotdir + (right * deviation_y) + (up * deviation_z)));
+               new_min_dist = dist_before_normal;
+               new_min_end = (self.sw_shotorg + (deviation * new_min_dist));
+               //te_lightning2(world, new_min_end, self.sw_shotorg);
+
+               // then calculate spread_to_max effect
+               deviation = angle * spread_to_max;
+               deviation = ((shotdir + (right * deviation_y) + (up * deviation_z)));
+               new_max_dist = vlen(new_min_end - self.sw_endpos);
+               new_max_end = (new_min_end + (deviation * new_max_dist));
+               //te_lightning2(world, new_end, prev_min_end);
+               
+
+               if(counter == 0)
+               {
+                       first_min_end = new_min_end;
+                       first_max_end = new_max_end;
+               }
+
+               if(counter >= 1)
+               {
+                       R_BeginPolygon("", DRAWFLAG_NORMAL);
+                       R_PolygonVertex(prev_min_end, '0 0 0', sw_color, a);
+                       R_PolygonVertex(new_min_end, '0 0 0', sw_color, a);
+                       R_PolygonVertex(self.sw_shotorg, '0 0 0', sw_color, a);
+                       R_EndPolygon();
+
+                       R_BeginPolygon("", DRAWFLAG_NORMAL);
+                       R_PolygonVertex(new_min_end, '0 0 0', sw_color, a);
+                       R_PolygonVertex(prev_min_end, '0 0 0', sw_color, a);
+                       R_PolygonVertex(prev_max_end, '0 0 0', sw_color, a);
+                       R_PolygonVertex(new_max_end, '0 0 0', sw_color, a);
+                       R_EndPolygon();
+               }
+
+               prev_min_end = new_min_end;
+               prev_max_end = new_max_end;
+
+               if((counter + 1) == shots)
+               {
+                       R_BeginPolygon("", DRAWFLAG_NORMAL);
+                       R_PolygonVertex(prev_min_end, '0 0 0', sw_color, a);
+                       R_PolygonVertex(first_min_end, '0 0 0', sw_color, a);
+                       R_PolygonVertex(self.sw_shotorg, '0 0 0', sw_color, a);
+                       R_EndPolygon();
+
+                       R_BeginPolygon("", DRAWFLAG_NORMAL);
+                       R_PolygonVertex(first_min_end, '0 0 0', sw_color, a);
+                       R_PolygonVertex(prev_min_end, '0 0 0', sw_color, a);
+                       R_PolygonVertex(prev_max_end, '0 0 0', sw_color, a);
+                       R_PolygonVertex(first_max_end, '0 0 0', sw_color, a);
+                       R_EndPolygon();
+               }
+       }
+}
+
+void Net_ReadShockwaveParticle()
+{
+       entity shockwave;
+       shockwave = spawn();
+       shockwave.draw = Draw_Shockwave;
+       
+       shockwave.sw_shotorg_x = ReadCoord(); shockwave.sw_shotorg_y = ReadCoord(); shockwave.sw_shotorg_z = ReadCoord();
+       shockwave.sw_endpos_x  = ReadCoord(); shockwave.sw_endpos_y  = ReadCoord(); shockwave.sw_endpos_z  = ReadCoord();
+       
+       shockwave.sw_spread_max = ReadByte();
+       shockwave.sw_spread_min = ReadByte();
+
+       shockwave.sv_entnum = ReadByte();
+
+       shockwave.sw_time = time;
+}
+
index fa61033f653b48f75947c6b95ea7a2e062ae8f0b..3449ae258c31b7b2bc2f77c6dd6cd3ea8c910be2 100644 (file)
@@ -18,8 +18,7 @@ Defs.qc
 ../common/util.qh
 ../common/test.qh
 ../common/counting.qh
-../common/items.qh
-../common/explosion_equation.qh
+../common/weapons/weapons.qh // TODO
 ../common/mapinfo.qh
 ../common/command/markup.qh
 ../common/command/rpn.qh
@@ -54,11 +53,12 @@ vehicles/vehicles.qh
 ../csqcmodellib/common.qh
 ../csqcmodellib/cl_model.qh
 ../csqcmodellib/cl_player.qh
-projectile.qh
+weapons/projectile.qh // TODO
 player_skeleton.qh
 
 sortlist.qc
 miscfunctions.qc
+../server/t_items.qh
 ../server/t_items.qc
 
 teamradar.qc
@@ -73,7 +73,7 @@ rubble.qc
 hook.qc
 particles.qc
 laser.qc
-projectile.qc
+weapons/projectile.qc // TODO
 gibs.qc
 damage.qc
 casings.qc
@@ -107,9 +107,7 @@ noise.qc
 ../common/command/rpn.qc
 ../common/command/generic.qc
 ../common/mapinfo.qc
-../common/items.qc
-../server/w_all.qc
-../common/explosion_equation.qc
+../common/weapons/weapons.qc // TODO
 ../common/urllib.qc
 command/cl_cmd.qc
 
diff --git a/qcsrc/client/projectile.qc b/qcsrc/client/projectile.qc
deleted file mode 100644 (file)
index 52f6e23..0000000
+++ /dev/null
@@ -1,529 +0,0 @@
-.vector iorigin1, iorigin2;
-.float spawntime;
-.vector trail_oldorigin;
-.float trail_oldtime;
-.float fade_time, fade_rate;
-
-void SUB_Stop()
-{
-       self.move_velocity = self.move_avelocity = '0 0 0';
-       self.move_movetype = MOVETYPE_NONE;
-}
-
-.float alphamod;
-.float count; // set if clientside projectile
-.float cnt; // sound index
-.float gravity;
-.float snd_looping;
-.float silent;
-
-void Projectile_ResetTrail(vector to)
-{
-       self.trail_oldorigin = to;
-       self.trail_oldtime = time;
-}
-
-void Projectile_DrawTrail(vector to)
-{
-       vector from;
-       float t0;
-
-       from = self.trail_oldorigin;
-       t0 = self.trail_oldtime;
-       self.trail_oldorigin = to;
-       self.trail_oldtime = time;
-
-       // force the effect even for stationary firemine
-       if(self.cnt == PROJECTILE_FIREMINE)
-               if(from == to)
-                       from_z += 1;
-
-       if (self.traileffect)
-       {
-               particles_alphamin = particles_alphamax = particles_fade = sqrt(self.alpha);
-               boxparticles(self.traileffect, self, from, to, self.velocity, self.velocity, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE | PARTICLES_DRAWASTRAIL);
-       }
-}
-
-void Projectile_Draw()
-{
-       vector rot;
-       vector trailorigin;
-       float f;
-       float drawn;
-       float t;
-       float a;
-
-       f = self.move_flags;
-
-       if(self.count & 0x80)
-       {
-               //self.move_flags &= ~FL_ONGROUND;
-               if(self.move_movetype == MOVETYPE_NONE || self.move_movetype == MOVETYPE_FLY)
-                       Movetype_Physics_NoMatchServer();
-                       // the trivial movetypes do not have to match the
-                       // server's ticrate as they are ticrate independent
-                       // NOTE: this assumption is only true if MOVETYPE_FLY
-                       // projectiles detonate on impact. If they continue
-                       // moving, we might still be ticrate dependent.
-               else
-                       Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
-               if(!(self.move_flags & FL_ONGROUND))
-                       if(self.velocity != '0 0 0')
-                               self.move_angles = self.angles = vectoangles(self.velocity);
-       }
-       else
-       {
-               InterpolateOrigin_Do();
-       }
-
-       if(self.count & 0x80)
-       {
-               drawn = (time >= self.spawntime - 0.02);
-               t = max(time, self.spawntime);
-       }
-       else
-       {
-               drawn = (self.iflags & IFLAG_VALID);
-               t = time;
-       }
-
-       if(!(f & FL_ONGROUND))
-       {
-               rot = '0 0 0';
-               switch(self.cnt)
-               {
-                       /*
-                       case PROJECTILE_GRENADE:
-                               rot = '-2000 0 0'; // forward
-                               break;
-                       */
-                       case PROJECTILE_GRENADE_BOUNCING:
-                               rot = '0 -1000 0'; // sideways
-                               break;
-                       case PROJECTILE_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)
-       {
-               // Possibly add dlights here.
-               default:
-                       break;
-       }
-
-       self.drawmask = MASK_NORMAL;
-}
-
-void loopsound(entity e, float ch, string samp, float vol, float attn)
-{
-       if(self.silent)
-               return;
-
-       sound(e, ch, samp, vol, attn);
-       e.snd_looping = ch;
-}
-
-void Ent_RemoveProjectile()
-{
-       if(self.count & 0x80)
-       {
-               tracebox(self.origin, self.mins, self.maxs, self.origin + self.velocity * 0.05, MOVE_NORMAL, self);
-               Projectile_DrawTrail(trace_endpos);
-       }
-}
-
-void Ent_Projectile()
-{
-       float f;
-
-       // projectile properties:
-       //   kind (interpolated, or clientside)
-       //
-       //   modelindex
-       //   origin
-       //   scale
-       //   if clientside:
-       //     velocity
-       //     gravity
-       //   soundindex (hardcoded list)
-       //   effects
-       //
-       // projectiles don't send angles, because they always follow the velocity
-
-       f = ReadByte();
-       self.count = (f & 0x80);
-       self.iflags = (self.iflags & IFLAG_INTERNALMASK) | IFLAG_AUTOANGLES | IFLAG_ANGLES | IFLAG_ORIGIN;
-       self.solid = SOLID_TRIGGER;
-       //self.effects = EF_NOMODELFLAGS;
-
-       // this should make collisions with bmodels more exact, but it leads to
-       // projectiles no longer being able to lie on a bmodel
-       self.move_nomonsters = MOVE_WORLDONLY;
-       if(f & 0x40)
-               self.move_flags |= FL_ONGROUND;
-       else
-               self.move_flags &= ~FL_ONGROUND;
-
-       if(!self.move_time)
-       {
-               // for some unknown reason, we don't need to care for
-               // sv_gameplayfix_delayprojectiles here.
-               self.move_time = time;
-               self.spawntime = time;
-       }
-       else
-               self.move_time = max(self.move_time, time);
-
-       if(!(self.count & 0x80))
-               InterpolateOrigin_Undo();
-
-       if(f & 1)
-       {
-               self.origin_x = ReadCoord();
-               self.origin_y = ReadCoord();
-               self.origin_z = ReadCoord();
-               setorigin(self, self.origin);
-               if(self.count & 0x80)
-               {
-                       self.velocity_x = ReadCoord();
-                       self.velocity_y = ReadCoord();
-                       self.velocity_z = ReadCoord();
-                       if(f & 0x10)
-                               self.gravity = ReadCoord();
-                       else
-                               self.gravity = 0; // none
-                       self.move_origin = self.origin;
-                       self.move_velocity = self.velocity;
-               }
-
-               if(time == self.spawntime || (self.count & 0x80) || (f & 0x08))
-               {
-                       self.trail_oldorigin = self.origin;
-                       if(!(self.count & 0x80))
-                               InterpolateOrigin_Reset();
-               }
-
-               if(f & 0x20)
-               {
-                       self.fade_time = time + ReadByte() * ticrate;
-                       self.fade_rate = 1 / (ReadByte() * ticrate);
-               }
-               else
-               {
-                       self.fade_time = 0;
-                       self.fade_rate = 0;
-               }
-       }
-
-       if(f & 2)
-       {
-               self.cnt = ReadByte();
-
-               self.silent = (self.cnt & 0x80);
-               self.cnt = (self.cnt & 0x7F);
-
-               self.scale = 1;
-               self.traileffect = 0;
-               switch(self.cnt)
-               {
-                       case PROJECTILE_ELECTRO: setmodel(self, "models/ebomb.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
-                       case PROJECTILE_ROCKET: setmodel(self, "models/rocket.md3");self.traileffect = particleeffectnum("TR_ROCKET"); self.scale = 2; break;
-                       case PROJECTILE_CRYLINK: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
-                       case PROJECTILE_CRYLINK_BOUNCING: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
-                       case PROJECTILE_ELECTRO_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
-                       case PROJECTILE_GRENADE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
-                       case PROJECTILE_GRENADE_BOUNCING: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
-                       case PROJECTILE_MINE: setmodel(self, "models/mine.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
-                       case PROJECTILE_LASER: setmodel(self, "models/laser.mdl");self.traileffect = particleeffectnum(""); break;
-                       case PROJECTILE_HLAC: setmodel(self, "models/hlac_bullet.md3");self.traileffect = particleeffectnum(""); break;
-                       case PROJECTILE_PORTO_RED: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break;
-                       case PROJECTILE_PORTO_BLUE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break;
-                       case PROJECTILE_HOOKBOMB: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_KNIGHTSPIKE"); break;
-                       case PROJECTILE_HAGAR: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break;
-                       case PROJECTILE_HAGAR_BOUNCING: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break;
-                       case PROJECTILE_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/electro_fly.wav", VOL_BASE, ATTEN_NORM);
-                               self.mins = '0 0 -4';
-                               self.maxs = '0 0 -4';
-                               self.move_movetype = MOVETYPE_BOUNCE;
-                               self.move_touch = func_null;
-                               self.move_bounce_factor = g_balance_electro_secondary_bouncefactor;
-                               self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop;
-                               break;
-                       case PROJECTILE_ROCKET:
-                               loopsound(self, CH_SHOTS_SINGLE, "weapons/rocket_fly.wav", VOL_BASE, ATTEN_NORM);
-                               self.mins = '-3 -3 -3';
-                               self.maxs = '3 3 3';
-                               break;
-                       case PROJECTILE_GRENADE:
-                               self.mins = '-3 -3 -3';
-                               self.maxs = '3 3 3';
-                               break;
-                       case PROJECTILE_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_fly2.wav", VOL_BASE, ATTEN_NORM);
-                               self.mins = '-16 -16 -16';
-                               self.maxs = '16 16 16';
-                               break;
-                       case PROJECTILE_FIREMINE:
-                               loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly.wav", VOL_BASE, ATTEN_NORM);
-                               self.move_movetype = MOVETYPE_BOUNCE;
-                               self.move_touch = func_null;
-                               self.mins = '-4 -4 -4';
-                               self.maxs = '4 4 4';
-                               break;
-                       case PROJECTILE_TAG:
-                               self.mins = '-2 -2 -2';
-                               self.maxs = '2 2 2';
-                               break;
-                       case PROJECTILE_FLAC:
-                               self.mins = '-2 -2 -2';
-                               self.maxs = '2 2 2';
-                               break;
-                       case PROJECTILE_SEEKER:
-                               loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM);
-                               self.mins = '-4 -4 -4';
-                               self.maxs = '4 4 4';
-                               break;
-            case PROJECTILE_RAPTORBOMB:
-                               self.mins = '-3 -3 -3';
-                               self.maxs = '3 3 3';
-                               break;
-            case PROJECTILE_RAPTORBOMBLET:
-                               break;
-            case PROJECTILE_RAPTORCANNON:
-                               break;
-            case PROJECTILE_SPIDERROCKET:
-                loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM);
-                               break;
-            case PROJECTILE_WAKIROCKET:
-                loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM);
-                               break;
-            /*
-            case PROJECTILE_WAKICANNON:
-                               break;
-                       case PROJECTILE_BUMBLE_GUN:
-                               // only new engines support sound moving with object
-                               loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTEN_NORM);
-                               self.mins = '0 0 -4';
-                               self.maxs = '0 0 -4';
-                               self.move_movetype = MOVETYPE_BOUNCE;
-                               self.move_touch = func_null;
-                               self.move_bounce_factor = g_balance_electro_secondary_bouncefactor;
-                               self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop;
-                               break;
-                       */
-                       default:
-                               break;
-               }
-               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");
-
-}
diff --git a/qcsrc/client/projectile.qh b/qcsrc/client/projectile.qh
deleted file mode 100644 (file)
index 70c8ba0..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-.float traileffect;
-void Projectile_ResetTrail(vector to);
-void Projectile_DrawTrail(vector to);
diff --git a/qcsrc/client/weapons/projectile.qc b/qcsrc/client/weapons/projectile.qc
new file mode 100644 (file)
index 0000000..52f6e23
--- /dev/null
@@ -0,0 +1,529 @@
+.vector iorigin1, iorigin2;
+.float spawntime;
+.vector trail_oldorigin;
+.float trail_oldtime;
+.float fade_time, fade_rate;
+
+void SUB_Stop()
+{
+       self.move_velocity = self.move_avelocity = '0 0 0';
+       self.move_movetype = MOVETYPE_NONE;
+}
+
+.float alphamod;
+.float count; // set if clientside projectile
+.float cnt; // sound index
+.float gravity;
+.float snd_looping;
+.float silent;
+
+void Projectile_ResetTrail(vector to)
+{
+       self.trail_oldorigin = to;
+       self.trail_oldtime = time;
+}
+
+void Projectile_DrawTrail(vector to)
+{
+       vector from;
+       float t0;
+
+       from = self.trail_oldorigin;
+       t0 = self.trail_oldtime;
+       self.trail_oldorigin = to;
+       self.trail_oldtime = time;
+
+       // force the effect even for stationary firemine
+       if(self.cnt == PROJECTILE_FIREMINE)
+               if(from == to)
+                       from_z += 1;
+
+       if (self.traileffect)
+       {
+               particles_alphamin = particles_alphamax = particles_fade = sqrt(self.alpha);
+               boxparticles(self.traileffect, self, from, to, self.velocity, self.velocity, 1, PARTICLES_USEALPHA | PARTICLES_USEFADE | PARTICLES_DRAWASTRAIL);
+       }
+}
+
+void Projectile_Draw()
+{
+       vector rot;
+       vector trailorigin;
+       float f;
+       float drawn;
+       float t;
+       float a;
+
+       f = self.move_flags;
+
+       if(self.count & 0x80)
+       {
+               //self.move_flags &= ~FL_ONGROUND;
+               if(self.move_movetype == MOVETYPE_NONE || self.move_movetype == MOVETYPE_FLY)
+                       Movetype_Physics_NoMatchServer();
+                       // the trivial movetypes do not have to match the
+                       // server's ticrate as they are ticrate independent
+                       // NOTE: this assumption is only true if MOVETYPE_FLY
+                       // projectiles detonate on impact. If they continue
+                       // moving, we might still be ticrate dependent.
+               else
+                       Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
+               if(!(self.move_flags & FL_ONGROUND))
+                       if(self.velocity != '0 0 0')
+                               self.move_angles = self.angles = vectoangles(self.velocity);
+       }
+       else
+       {
+               InterpolateOrigin_Do();
+       }
+
+       if(self.count & 0x80)
+       {
+               drawn = (time >= self.spawntime - 0.02);
+               t = max(time, self.spawntime);
+       }
+       else
+       {
+               drawn = (self.iflags & IFLAG_VALID);
+               t = time;
+       }
+
+       if(!(f & FL_ONGROUND))
+       {
+               rot = '0 0 0';
+               switch(self.cnt)
+               {
+                       /*
+                       case PROJECTILE_GRENADE:
+                               rot = '-2000 0 0'; // forward
+                               break;
+                       */
+                       case PROJECTILE_GRENADE_BOUNCING:
+                               rot = '0 -1000 0'; // sideways
+                               break;
+                       case PROJECTILE_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)
+       {
+               // Possibly add dlights here.
+               default:
+                       break;
+       }
+
+       self.drawmask = MASK_NORMAL;
+}
+
+void loopsound(entity e, float ch, string samp, float vol, float attn)
+{
+       if(self.silent)
+               return;
+
+       sound(e, ch, samp, vol, attn);
+       e.snd_looping = ch;
+}
+
+void Ent_RemoveProjectile()
+{
+       if(self.count & 0x80)
+       {
+               tracebox(self.origin, self.mins, self.maxs, self.origin + self.velocity * 0.05, MOVE_NORMAL, self);
+               Projectile_DrawTrail(trace_endpos);
+       }
+}
+
+void Ent_Projectile()
+{
+       float f;
+
+       // projectile properties:
+       //   kind (interpolated, or clientside)
+       //
+       //   modelindex
+       //   origin
+       //   scale
+       //   if clientside:
+       //     velocity
+       //     gravity
+       //   soundindex (hardcoded list)
+       //   effects
+       //
+       // projectiles don't send angles, because they always follow the velocity
+
+       f = ReadByte();
+       self.count = (f & 0x80);
+       self.iflags = (self.iflags & IFLAG_INTERNALMASK) | IFLAG_AUTOANGLES | IFLAG_ANGLES | IFLAG_ORIGIN;
+       self.solid = SOLID_TRIGGER;
+       //self.effects = EF_NOMODELFLAGS;
+
+       // this should make collisions with bmodels more exact, but it leads to
+       // projectiles no longer being able to lie on a bmodel
+       self.move_nomonsters = MOVE_WORLDONLY;
+       if(f & 0x40)
+               self.move_flags |= FL_ONGROUND;
+       else
+               self.move_flags &= ~FL_ONGROUND;
+
+       if(!self.move_time)
+       {
+               // for some unknown reason, we don't need to care for
+               // sv_gameplayfix_delayprojectiles here.
+               self.move_time = time;
+               self.spawntime = time;
+       }
+       else
+               self.move_time = max(self.move_time, time);
+
+       if(!(self.count & 0x80))
+               InterpolateOrigin_Undo();
+
+       if(f & 1)
+       {
+               self.origin_x = ReadCoord();
+               self.origin_y = ReadCoord();
+               self.origin_z = ReadCoord();
+               setorigin(self, self.origin);
+               if(self.count & 0x80)
+               {
+                       self.velocity_x = ReadCoord();
+                       self.velocity_y = ReadCoord();
+                       self.velocity_z = ReadCoord();
+                       if(f & 0x10)
+                               self.gravity = ReadCoord();
+                       else
+                               self.gravity = 0; // none
+                       self.move_origin = self.origin;
+                       self.move_velocity = self.velocity;
+               }
+
+               if(time == self.spawntime || (self.count & 0x80) || (f & 0x08))
+               {
+                       self.trail_oldorigin = self.origin;
+                       if(!(self.count & 0x80))
+                               InterpolateOrigin_Reset();
+               }
+
+               if(f & 0x20)
+               {
+                       self.fade_time = time + ReadByte() * ticrate;
+                       self.fade_rate = 1 / (ReadByte() * ticrate);
+               }
+               else
+               {
+                       self.fade_time = 0;
+                       self.fade_rate = 0;
+               }
+       }
+
+       if(f & 2)
+       {
+               self.cnt = ReadByte();
+
+               self.silent = (self.cnt & 0x80);
+               self.cnt = (self.cnt & 0x7F);
+
+               self.scale = 1;
+               self.traileffect = 0;
+               switch(self.cnt)
+               {
+                       case PROJECTILE_ELECTRO: setmodel(self, "models/ebomb.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
+                       case PROJECTILE_ROCKET: setmodel(self, "models/rocket.md3");self.traileffect = particleeffectnum("TR_ROCKET"); self.scale = 2; break;
+                       case PROJECTILE_CRYLINK: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
+                       case PROJECTILE_CRYLINK_BOUNCING: setmodel(self, "models/plasmatrail.mdl");self.traileffect = particleeffectnum("TR_CRYLINKPLASMA"); break;
+                       case PROJECTILE_ELECTRO_BEAM: setmodel(self, "models/elaser.mdl");self.traileffect = particleeffectnum("TR_NEXUIZPLASMA"); break;
+                       case PROJECTILE_GRENADE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
+                       case PROJECTILE_GRENADE_BOUNCING: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
+                       case PROJECTILE_MINE: setmodel(self, "models/mine.md3");self.traileffect = particleeffectnum("TR_GRENADE"); break;
+                       case PROJECTILE_LASER: setmodel(self, "models/laser.mdl");self.traileffect = particleeffectnum(""); break;
+                       case PROJECTILE_HLAC: setmodel(self, "models/hlac_bullet.md3");self.traileffect = particleeffectnum(""); break;
+                       case PROJECTILE_PORTO_RED: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break;
+                       case PROJECTILE_PORTO_BLUE: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_WIZSPIKE"); self.scale = 4; break;
+                       case PROJECTILE_HOOKBOMB: setmodel(self, "models/grenademodel.md3");self.traileffect = particleeffectnum("TR_KNIGHTSPIKE"); break;
+                       case PROJECTILE_HAGAR: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break;
+                       case PROJECTILE_HAGAR_BOUNCING: setmodel(self, "models/hagarmissile.mdl");self.traileffect = particleeffectnum("tr_hagar"); self.scale = 0.75; break;
+                       case PROJECTILE_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/electro_fly.wav", VOL_BASE, ATTEN_NORM);
+                               self.mins = '0 0 -4';
+                               self.maxs = '0 0 -4';
+                               self.move_movetype = MOVETYPE_BOUNCE;
+                               self.move_touch = func_null;
+                               self.move_bounce_factor = g_balance_electro_secondary_bouncefactor;
+                               self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop;
+                               break;
+                       case PROJECTILE_ROCKET:
+                               loopsound(self, CH_SHOTS_SINGLE, "weapons/rocket_fly.wav", VOL_BASE, ATTEN_NORM);
+                               self.mins = '-3 -3 -3';
+                               self.maxs = '3 3 3';
+                               break;
+                       case PROJECTILE_GRENADE:
+                               self.mins = '-3 -3 -3';
+                               self.maxs = '3 3 3';
+                               break;
+                       case PROJECTILE_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_fly2.wav", VOL_BASE, ATTEN_NORM);
+                               self.mins = '-16 -16 -16';
+                               self.maxs = '16 16 16';
+                               break;
+                       case PROJECTILE_FIREMINE:
+                               loopsound(self, CH_SHOTS_SINGLE, "weapons/fireball_fly.wav", VOL_BASE, ATTEN_NORM);
+                               self.move_movetype = MOVETYPE_BOUNCE;
+                               self.move_touch = func_null;
+                               self.mins = '-4 -4 -4';
+                               self.maxs = '4 4 4';
+                               break;
+                       case PROJECTILE_TAG:
+                               self.mins = '-2 -2 -2';
+                               self.maxs = '2 2 2';
+                               break;
+                       case PROJECTILE_FLAC:
+                               self.mins = '-2 -2 -2';
+                               self.maxs = '2 2 2';
+                               break;
+                       case PROJECTILE_SEEKER:
+                               loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM);
+                               self.mins = '-4 -4 -4';
+                               self.maxs = '4 4 4';
+                               break;
+            case PROJECTILE_RAPTORBOMB:
+                               self.mins = '-3 -3 -3';
+                               self.maxs = '3 3 3';
+                               break;
+            case PROJECTILE_RAPTORBOMBLET:
+                               break;
+            case PROJECTILE_RAPTORCANNON:
+                               break;
+            case PROJECTILE_SPIDERROCKET:
+                loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM);
+                               break;
+            case PROJECTILE_WAKIROCKET:
+                loopsound(self, CH_SHOTS_SINGLE, "weapons/tag_rocket_fly.wav", VOL_BASE, ATTEN_NORM);
+                               break;
+            /*
+            case PROJECTILE_WAKICANNON:
+                               break;
+                       case PROJECTILE_BUMBLE_GUN:
+                               // only new engines support sound moving with object
+                               loopsound(self, CH_SHOTS_SINGLE, "weapons/electro_fly.wav", VOL_BASE, ATTEN_NORM);
+                               self.mins = '0 0 -4';
+                               self.maxs = '0 0 -4';
+                               self.move_movetype = MOVETYPE_BOUNCE;
+                               self.move_touch = func_null;
+                               self.move_bounce_factor = g_balance_electro_secondary_bouncefactor;
+                               self.move_bounce_stopspeed = g_balance_electro_secondary_bouncestop;
+                               break;
+                       */
+                       default:
+                               break;
+               }
+               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");
+
+}
diff --git a/qcsrc/client/weapons/projectile.qh b/qcsrc/client/weapons/projectile.qh
new file mode 100644 (file)
index 0000000..70c8ba0
--- /dev/null
@@ -0,0 +1,3 @@
+.float traileffect;
+void Projectile_ResetTrail(vector to);
+void Projectile_DrawTrail(vector to);
index 5e8accbd2912ece956d6e9bc36cb1e0092b03afd..1c32c86befbeefdfd74f35f48608a396fdeb1234 100644 (file)
@@ -239,12 +239,12 @@ void GenericCommand_dumpnotifs(float request)
 
                        if(filename == "")
                        {
-                               filename = "notifications.cfg";
+                               filename = "notifications_dump.cfg";
                                alsoprint = FALSE;
                        }
                        else if(filename == "-")
                        {
-                               filename = "notifications.cfg";
+                               filename = "notifications_dump.cfg";
                                alsoprint = TRUE;
                        }
                        fh = fopen(filename, FILE_WRITE);
@@ -269,7 +269,60 @@ void GenericCommand_dumpnotifs(float request)
                case CMD_REQUEST_USAGE:
                {
                        print(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " dumpnotifs [filename]"));
-                       print("  Where 'filename' is the file to write (default is notifications.cfg),\n");
+                       print("  Where 'filename' is the file to write (default is notifications_dump.cfg),\n");
+                       print("  if supplied with '-' output to console as well as default,\n");
+                       print("  if left blank, it will only write to default.\n");
+                       return;
+               }
+       }
+}
+
+void GenericCommand_dumpweapons(float request) // WEAPONTODO: make this work with other progs than just server
+{
+       switch(request)
+       {
+               case CMD_REQUEST_COMMAND:
+               {
+                       #ifdef SVQC
+                       wep_config_file = -1;
+                       wep_config_alsoprint = -1;
+                       string filename = argv(1);
+                       
+                       if(filename == "")
+                       {
+                               filename = "weapons_dump.cfg";
+                               wep_config_alsoprint = FALSE;
+                       }
+                       else if(filename == "-")
+                       {
+                               filename = "weapons_dump.cfg";
+                               wep_config_alsoprint = TRUE;
+                       }
+                       wep_config_file = fopen(filename, FILE_WRITE);
+                       
+                       if(wep_config_file >= 0)
+                       {
+                               Dump_Weapon_Settings();
+                               print(sprintf("Dumping weapons... File located in ^2data/data/%s^7.\n", filename));
+                               fclose(wep_config_file);
+                               wep_config_file = -1;
+                               wep_config_alsoprint = -1;
+                       }
+                       else
+                       {
+                               print(sprintf("^1Error: ^7Could not open file '%s'!\n", filename));
+                       }
+                       #else
+                       print(_("Weapons dump command only works with sv_cmd.\n"));
+                       #endif
+                       return;
+               }
+                       
+               default:
+               case CMD_REQUEST_USAGE:
+               {
+                       print(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " dumpweapons [filename]"));
+                       print("  Where 'filename' is the file to write (default is weapons_dump.cfg),\n");
                        print("  if supplied with '-' output to console as well as default,\n");
                        print("  if left blank, it will only write to default.\n");
                        return;
@@ -585,6 +638,7 @@ void GenericCommand_(float request)
        GENERIC_COMMAND("addtolist", GenericCommand_addtolist(request, arguments), "Add a string to a cvar") \
        GENERIC_COMMAND("dumpcommands", GenericCommand_dumpcommands(request), "Dump all commands on the program to *_cmd_dump.txt") \
        GENERIC_COMMAND("dumpnotifs", GenericCommand_dumpnotifs(request), "Dump all notifications into notifications_dump.txt") \
+       GENERIC_COMMAND("dumpweapons", GenericCommand_dumpweapons(request), "Dump all weapons into weapons_dump.txt") \
        GENERIC_COMMAND("maplist", GenericCommand_maplist(request, arguments), "Automatic control of maplist") \
        GENERIC_COMMAND("nextframe", GenericCommand_nextframe(request, arguments, command), "Execute the given command next frame of this VM") \
        GENERIC_COMMAND("qc_curl", GenericCommand_qc_curl(request, arguments), "Queries a URL") \
index 55d16953f6a04d4a5e4f9bc8c289b5981654704e..1cb3038b17e8f94806074005bc0c569bc20a29f7 100644 (file)
@@ -32,7 +32,7 @@ const float TE_CSQC_PICTURE = 100;
 const float TE_CSQC_RACE = 101;
 const float TE_CSQC_ZCURVEPARTICLES = 102;
 const float TE_CSQC_NEXGUNBEAMPARTICLE = 103;
-const float TE_CSQC_LIGHTNINGARC = 104;
+const float TE_CSQC_ARC = 104;
 const float TE_CSQC_TEAMNAGGER = 105;
 const float TE_CSQC_PINGPLREPORT = 106;
 const float TE_CSQC_TARGET_MUSIC = 107;
@@ -42,6 +42,7 @@ const float TE_CSQC_MINELAYER_MAXMINES = 110;
 const float TE_CSQC_HAGAR_MAXROCKETS = 111;
 const float TE_CSQC_VEHICLESETUP = 112;
 const float TE_CSQC_SVNOTICE = 113;
+const float TE_CSQC_SHOCKWAVEPARTICLE = 114;
 
 const float RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder
 const float RACE_NET_CHECKPOINT_CLEAR = 1;
@@ -86,8 +87,8 @@ const float ENT_CLIENT_WARPZONE = 24;
 const float ENT_CLIENT_WARPZONE_CAMERA = 25;
 const float ENT_CLIENT_TRIGGER_MUSIC = 26;
 const float ENT_CLIENT_HOOK = 27;
-const float ENT_CLIENT_LGBEAM = 28;
-const float ENT_CLIENT_GAUNTLET = 29;
+const float ENT_CLIENT_ELECTRO_BEAM = 28;
+const float ENT_CLIENT_ARC_BEAM = 29;
 const float ENT_CLIENT_ACCURACY = 30;
 const float ENT_CLIENT_SHOWNAMES = 31;
 const float ENT_CLIENT_WARPZONE_TELEPORTED = 32;
@@ -385,25 +386,6 @@ const float WATERLEVEL_NONE = 0;
 const float WATERLEVEL_WETFEET = 1;
 const float WATERLEVEL_SWIMMING = 2;
 const float WATERLEVEL_SUBMERGED = 3;
-
-const float MAX_SHOT_DISTANCE = 32768;
-
-// weapon requests
-const float WR_SETUP = 1; // (SVQC) setup weapon data
-const float WR_THINK = 2; // (SVQC) logic to run every frame
-const float WR_CHECKAMMO1 = 3; // (SVQC) checks ammo for weapon
-const float WR_CHECKAMMO2 = 4; // (SVQC) checks ammo for weapon
-const float WR_AIM = 5; // (SVQC) runs bot aiming code for this weapon
-const float WR_PRECACHE = 6; // (CSQC and SVQC) precaches models/sounds used by this weapon
-const float WR_SUICIDEMESSAGE = 7; // (SVQC) notification number for suicide message (may inspect w_deathtype for details)
-const float WR_KILLMESSAGE = 8; // (SVQC) notification number for kill message (may inspect w_deathtype for details)
-const float WR_RELOAD = 9; // (SVQC) does not need to do anything
-const float WR_RESETPLAYER = 10; // (SVQC) does not need to do anything
-const float WR_IMPACTEFFECT = 11; // (CSQC) impact effect
-const float WR_SWITCHABLE = 12; // (CSQC) impact effect
-const float WR_PLAYERDEATH = 13; // (SVQC) does not need to do anything
-const float WR_GONETHINK = 14; // (SVQC) logic to run every frame, also if no longer having the weapon as long as the switch away has not been performed
-
 #define SERVERFLAG_ALLOW_FULLBRIGHT 1
 #define SERVERFLAG_TEAMPLAY 2
 #define SERVERFLAG_PLAYERSTATS 4
diff --git a/qcsrc/common/explosion_equation.qc b/qcsrc/common/explosion_equation.qc
deleted file mode 100644 (file)
index df71154..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-float explosion_calcpush_getmultiplier(vector explosion_v, vector target_v)
-{
-       float a;
-       a  = explosion_v * (explosion_v - target_v);
-
-       if(a <= 0)
-               // target is too fast to be hittable by this
-               return 0;
-
-       a /= (explosion_v * explosion_v);
-               // we know we can divide by this, or above a would be == 0
-
-       return a;
-}
-
-#if 0
-vector explosion_calcpush(vector explosion_v, float explosion_m, vector target_v, float target_m, float elasticity)
-{
-       // solution of the equations:
-       //    v'                = v + a vp             // central hit
-       //    m*v'   + mp*vp'   = m*v + mp*vp          // conservation of momentum
-       //    m*v'^2 + mp*vp'^2 = m*v^2 + mp*vp^2      // conservation of energy (ELASTIC hit)
-       // -> a = 0                                    // case 1: did not hit
-       // -> a = 2*mp*(vp^2 - vp.v) / ((m+mp) * vp^2) // case 2: did hit
-       //                                             // non-elastic hits are somewhere between these two
-
-       // this would be physically correct, but we don't do that
-       return explosion_v * explosion_calcpush_getmultiplier(explosion_v, target_v) * (
-               (1 + elasticity) * (
-                       explosion_m
-               ) / (
-                       target_m + explosion_m
-               )
-       ); // note: this factor is at least 0, at most 2
-}
-#endif
-
-// simplified formula, tuned so that if the target has velocity 0, we get exactly the original force
-vector damage_explosion_calcpush(vector explosion_f, vector target_v, float speedfactor)
-{
-       // if below 1, the formulas make no sense (and would cause superjumps)
-       if(speedfactor < 1)
-               return explosion_f;
-
-#if 0
-       float m;
-       // find m so that
-       //   speedfactor * (1 + e) * m / (1 + m) == 1
-       m = 1 / ((1 + 0) * speedfactor - 1);
-       vector v;
-       v = explosion_calcpush(explosion_f * speedfactor, m, target_v, 1, 0);
-       // the factor we then get is:
-       //   1
-       print(sprintf("MASS: %f\nv: %v -> %v\nENERGY BEFORE == %f + %f = %f\nENERGY AFTER >= %f\n",
-               m,
-               target_v, target_v + v,
-               target_v * target_v, m * explosion_f * speedfactor * explosion_f * speedfactor, target_v * target_v + m * explosion_f * speedfactor * explosion_f * speedfactor,
-               (target_v + v) * (target_v + v)));
-       return v;
-#endif
-       return explosion_f * explosion_calcpush_getmultiplier(explosion_f * speedfactor, target_v);
-}
diff --git a/qcsrc/common/explosion_equation.qh b/qcsrc/common/explosion_equation.qh
deleted file mode 100644 (file)
index c8630cd..0000000
+++ /dev/null
@@ -1 +0,0 @@
-vector damage_explosion_calcpush(vector explosion_f, vector target_v, float speedfactor);
diff --git a/qcsrc/common/items.qc b/qcsrc/common/items.qc
deleted file mode 100644 (file)
index 1917307..0000000
+++ /dev/null
@@ -1,251 +0,0 @@
-// WEAPON PLUGIN SYSTEM
-entity weapon_info[WEP_MAXCOUNT];
-entity dummy_weapon_info;
-
-#if WEP_MAXCOUNT > 72
-# error Kein Weltraum links auf dem Gerät
-#endif
-
-WepSet WepSet_FromWeapon(float a) {
-       a -= WEP_FIRST;
-#if WEP_MAXCOUNT > 24
-       if(a >= 24) {
-               a -= 24;
-#if WEP_MAXCOUNT > 48
-               if(a >= 24) {
-                       a -= 24;
-                       return '0 0 1' * power2of(a);
-               }
-#endif
-               return '0 1 0' * power2of(a);
-       }
-#endif
-       return '1 0 0' * power2of(a);
-}
-#ifdef SVQC
-void WepSet_AddStat()
-{
-       addstat(STAT_WEAPONS, AS_INT, weapons_x);
-#if WEP_MAXCOUNT > 24
-       addstat(STAT_WEAPONS2, AS_INT, weapons_y);
-#if WEP_MAXCOUNT > 48
-       addstat(STAT_WEAPONS3, AS_INT, weapons_z);
-#endif
-#endif
-}
-void WriteWepSet(float dst, WepSet w)
-{
-#if WEP_MAXCOUNT > 48
-       WriteInt72_t(dst, w);
-#elif WEP_MAXCOUNT > 24
-       WriteInt48_t(dst, w);
-#else
-       WriteInt24_t(dst, w_x);
-#endif
-}
-#endif
-#ifdef CSQC
-WepSet WepSet_GetFromStat()
-{
-       WepSet w = '0 0 0';
-       w_x = getstati(STAT_WEAPONS);
-#if WEP_MAXCOUNT > 24
-       w_y = getstati(STAT_WEAPONS2);
-#if WEP_MAXCOUNT > 48
-       w_z = getstati(STAT_WEAPONS3);
-#endif
-#endif
-       return w;
-}
-WepSet ReadWepSet()
-{
-#if WEP_MAXCOUNT > 48
-       return ReadInt72_t();
-#elif WEP_MAXCOUNT > 24
-       return ReadInt48_t();
-#else
-       return ReadInt24_t() * '1 0 0';
-#endif
-}
-#endif
-
-void register_weapon(float id, WepSet bit, float(float) func, float ammotype, float i, float weapontype, float pickupbasevalue, string modelname, string shortname, string wname)
-{
-       entity e;
-       weapon_info[id - 1] = e = spawn();
-       e.classname = "weapon_info";
-       e.weapon = id;
-       e.weapons = bit;
-       e.netname = shortname;
-       e.message = wname;
-       e.items = ammotype;
-       e.weapon_func = func;
-       e.mdl = modelname;
-       e.model = strzone(strcat("models/weapons/g_", modelname, ".md3"));
-       e.spawnflags = weapontype;
-       e.model2 = strzone(strcat("wpn-", e.mdl));
-       e.impulse = i;
-       e.bot_pickupbasevalue = pickupbasevalue;
-       if(ammotype & IT_SHELLS)
-               e.ammo_field = ammo_shells;
-       else if(ammotype & IT_NAILS)
-               e.ammo_field = ammo_nails;
-       else if(ammotype & IT_ROCKETS)
-               e.ammo_field = ammo_rockets;
-       else if(ammotype & IT_CELLS)
-               e.ammo_field = ammo_cells;
-       else if(ammotype & IT_FUEL)
-               e.ammo_field = ammo_fuel;
-       else
-               e.ammo_field = ammo_batteries;
-}
-float w_null(float dummy)
-{
-       return 0;
-}
-void register_weapons_done()
-{
-       dummy_weapon_info = spawn();
-       dummy_weapon_info.classname = "weapon_info";
-       dummy_weapon_info.weapon = 0; // you can recognize dummies by this
-       dummy_weapon_info.weapons = '0 0 0';
-       dummy_weapon_info.netname = "";
-       dummy_weapon_info.message = "AOL CD Thrower";
-       dummy_weapon_info.items = 0;
-       dummy_weapon_info.weapon_func = w_null;
-       dummy_weapon_info.mdl = "";
-       dummy_weapon_info.model = "";
-       dummy_weapon_info.spawnflags = 0;
-       dummy_weapon_info.model2 = "";
-       dummy_weapon_info.impulse = -1;
-       dummy_weapon_info.bot_pickupbasevalue = 0;
-
-       float i;
-       weaponorder_byid = "";
-       for(i = WEP_MAXCOUNT; i >= 1; --i)
-               if(weapon_info[i-1])
-                       weaponorder_byid = strcat(weaponorder_byid, " ", ftos(i));
-       weaponorder_byid = strzone(substring(weaponorder_byid, 1, strlen(weaponorder_byid) - 1));
-}
-entity get_weaponinfo(float id)
-{
-       entity w;
-       if(id < WEP_FIRST || id > WEP_LAST)
-               return dummy_weapon_info;
-       w = weapon_info[id - 1];
-       if(w)
-               return w;
-       return dummy_weapon_info;
-}
-string W_FixWeaponOrder(string order, float complete)
-{
-       return fixPriorityList(order, WEP_FIRST, WEP_LAST, 230 - WEP_FIRST, complete);
-}
-string W_NameWeaponOrder_MapFunc(string s)
-{
-       entity wi;
-       if(s == "0" || stof(s))
-       {
-               wi = get_weaponinfo(stof(s));
-               if(wi != dummy_weapon_info)
-                       return wi.netname;
-       }
-       return s;
-}
-string W_NameWeaponOrder(string order)
-{
-       return mapPriorityList(order, W_NameWeaponOrder_MapFunc);
-}
-string W_NumberWeaponOrder_MapFunc(string s)
-{
-       float i;
-       if(s == "0" || stof(s))
-               return s;
-       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
-               if(s == get_weaponinfo(i).netname)
-                       return ftos(i);
-       return s;
-}
-string W_NumberWeaponOrder(string order)
-{
-       return mapPriorityList(order, W_NumberWeaponOrder_MapFunc);
-}
-
-float W_FixWeaponOrder_BuildImpulseList_buf[WEP_MAXCOUNT];
-string W_FixWeaponOrder_BuildImpulseList_order;
-void W_FixWeaponOrder_BuildImpulseList_swap(float i, float j, entity pass)
-{
-       float h;
-       h = W_FixWeaponOrder_BuildImpulseList_buf[i];
-       W_FixWeaponOrder_BuildImpulseList_buf[i] = W_FixWeaponOrder_BuildImpulseList_buf[j];
-       W_FixWeaponOrder_BuildImpulseList_buf[j] = h;
-}
-float W_FixWeaponOrder_BuildImpulseList_cmp(float i, float j, entity pass)
-{
-       entity e1, e2;
-       float d;
-       e1 = get_weaponinfo(W_FixWeaponOrder_BuildImpulseList_buf[i]);
-       e2 = get_weaponinfo(W_FixWeaponOrder_BuildImpulseList_buf[j]);
-       d = mod(e1.impulse + 9, 10) - mod(e2.impulse + 9, 10);
-       if(d != 0)
-               return -d; // high impulse first!
-       return
-               strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "), sprintf(" %d ", W_FixWeaponOrder_BuildImpulseList_buf[i]), 0)
-               -
-               strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "), sprintf(" %d ", W_FixWeaponOrder_BuildImpulseList_buf[j]), 0)
-               ; // low char index first!
-}
-string W_FixWeaponOrder_BuildImpulseList(string o)
-{
-       float i;
-       W_FixWeaponOrder_BuildImpulseList_order = o;
-       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
-               W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST] = i;
-       heapsort(WEP_LAST - WEP_FIRST + 1, W_FixWeaponOrder_BuildImpulseList_swap, W_FixWeaponOrder_BuildImpulseList_cmp, world);
-       o = "";
-       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
-               o = strcat(o, " ", ftos(W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST]));
-       W_FixWeaponOrder_BuildImpulseList_order = string_null;
-       return substring(o, 1, -1);
-}
-
-string W_FixWeaponOrder_AllowIncomplete(string order)
-{
-       return W_FixWeaponOrder(order, 0);
-}
-
-string W_FixWeaponOrder_ForceComplete(string order)
-{
-       if(order == "")
-               order = W_NumberWeaponOrder(cvar_defstring("cl_weaponpriority"));
-       return W_FixWeaponOrder(order, 1);
-}
-
-void W_RandomWeapons(entity e, float n)
-{
-       float i, j;
-       WepSet remaining;
-       WepSet result;
-       remaining = e.weapons;
-       result = '0 0 0';
-       for(i = 0; i < n; ++i)
-       {
-               RandomSelection_Init();
-               for(j = WEP_FIRST; j <= WEP_LAST; ++j)
-                       if(remaining & WepSet_FromWeapon(j))
-                               RandomSelection_Add(world, j, string_null, 1, 1);
-               result |= WepSet_FromWeapon(RandomSelection_chosen_float);
-               remaining &= ~WepSet_FromWeapon(RandomSelection_chosen_float);
-       }
-       e.weapons = result;
-}
-
-string W_Name(float weaponid)
-{
-       return (get_weaponinfo(weaponid)).message;
-}
-
-float W_AmmoItemCode(float wpn)
-{
-       return (get_weaponinfo(wpn)).items & IT_AMMO;
-}
diff --git a/qcsrc/common/items.qh b/qcsrc/common/items.qh
deleted file mode 100644 (file)
index 35cc002..0000000
+++ /dev/null
@@ -1,153 +0,0 @@
-const float BOT_PICKUP_RATING_LOW      = 2500;
-const float BOT_PICKUP_RATING_MID      = 5000;
-const float BOT_PICKUP_RATING_HIGH     = 10000;
-
-const float WEP_TYPE_OTHER           =  0x00; // not for damaging people
-const float WEP_TYPE_SPLASH          =  0x01; // splash damage
-const float WEP_TYPE_HITSCAN         =  0x02; // hitscan
-const float WEP_TYPEMASK            =  0x0F;
-const float WEP_FLAG_CANCLIMB       =  0x10; // can be used for movement
-const float WEP_FLAG_NORMAL         =  0x20; // in "most weapons" set
-const float WEP_FLAG_HIDDEN         =  0x40; // hides from menu
-const float WEP_FLAG_RELOADABLE     =  0x80; // can has reload
-const float WEP_FLAG_SUPERWEAPON    = 0x100; // powerup timer
-const float WEP_FLAG_MUTATORBLOCKED = 0x200; // hides from impulse 99 etc. (mutators are allowed to clear this flag)
-
-const float    IT_UNLIMITED_WEAPON_AMMO     = 1;
-// when this bit is set, using a weapon does not reduce ammo. Checkpoints can give this powerup.
-const float    IT_UNLIMITED_SUPERWEAPONS    = 2;
-// when this bit is set, superweapons don't expire. Checkpoints can give this powerup.
-const float   IT_CTF_SHIELDED              = 4; // set for the flag shield
-const float   IT_USING_JETPACK             = 8; // confirmation that button is pressed
-const float   IT_JETPACK                   = 16; // actual item
-const float   IT_FUEL_REGEN                = 32; // fuel regeneration trigger
-WANT_CONST float   IT_SHELLS                    = 256;
-WANT_CONST float   IT_NAILS                     = 512;
-WANT_CONST float   IT_ROCKETS                   = 1024;
-WANT_CONST float   IT_CELLS                     = 2048;
-const float   IT_SUPERWEAPON               = 4096;
-const float   IT_FUEL                      = 128;
-const float   IT_STRENGTH                  = 8192;
-const float   IT_INVINCIBLE                = 16384;
-const float   IT_HEALTH                    = 32768;
-// union:
-       // for items:
-       WANT_CONST float        IT_KEY1                                 = 131072;
-       WANT_CONST float        IT_KEY2                                 = 262144;
-       // for players:
-       const float     IT_RED_FLAG_TAKEN               = 32768;
-       const float     IT_RED_FLAG_LOST                = 65536;
-       const float     IT_RED_FLAG_CARRYING            = 98304;
-       const float     IT_BLUE_FLAG_TAKEN              = 131072;
-       const float     IT_BLUE_FLAG_LOST               = 262144;
-       const float     IT_BLUE_FLAG_CARRYING   = 393216;
-// end
-const float   IT_5HP                       = 524288;
-const float   IT_25HP                      = 1048576;
-const float   IT_ARMOR_SHARD               = 2097152;
-const float   IT_ARMOR                     = 4194304;
-
-const float   IT_AMMO                      = 3968; // IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS | IT_FUEL;
-const float   IT_PICKUPMASK                = 51; // IT_FUEL_REGEN | IT_JETPACK | IT_UNLIMITED_AMMO; // strength and invincible are handled separately
-const float   IT_UNLIMITED_AMMO            = 3; // IT_UNLIMITED_SUPERWEAPONS | IT_UNLIMITED_WEAPON_AMMO;
-
-const float AMMO_COUNT = 4; // amount of ammo types to show in the inventory panel
-
-// variables:
-string weaponorder_byid;
-
-// functions:
-entity get_weaponinfo(float id);
-string W_FixWeaponOrder(string order, float complete);
-string W_NameWeaponOrder(string order);
-string W_NumberWeaponOrder(string order);
-
-// ammo types
-.float ammo_shells;
-.float ammo_nails;
-.float ammo_rockets;
-.float ammo_cells;
-.float ammo_fuel;
-.float ammo_batteries; // dummy
-
-// Weapon sets
-typedef vector WepSet;
-WepSet WepSet_FromWeapon(float a);
-#ifdef SVQC
-void WepSet_AddStat();
-void WriteWepSet(float dest, WepSet w);
-#endif
-#ifdef CSQC
-WepSet WepSet_GetFromStat();
-WepSet ReadWepSet();
-#endif
-
-// Weapon name macros
-#define WEP_FIRST 1
-#define WEP_MAXCOUNT 24 // Increase as needed. Can be up to three times as much.
-float WEP_COUNT;
-float WEP_LAST;
-WepSet WEPSET_ALL;
-WepSet WEPSET_SUPERWEAPONS;
-
-// entity properties of weaponinfo:
-.float weapon; // WEP_...
-.WepSet weapons; // WEPSET_...
-.string netname; // short name
-.string message; // human readable name
-.float items; // IT_...
-.float(float) weapon_func; // w_...
-.string mdl; // modelname without g_, v_, w_
-.string model; // full name of g_ model
-.float spawnflags; // WEPSPAWNFLAG_... combined
-.float impulse; // weapon impulse
-.float bot_pickupbasevalue; // bot weapon priority
-.string model2; // wpn- sprite name
-..float ammo_field; // main ammo field
-
-// dynamic weapon adding
-float w_null(float dummy);
-void register_weapon(float id, WepSet bit, float(float) func, float ammotype, float i, float weapontype, float pickupbasevalue, string modelname, string shortname, string wname);
-void register_weapons_done();
-
-#define REGISTER_WEAPON_2(id,bit,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \
-       float id; \
-       WepSet bit; \
-       float func(float); \
-       void RegisterWeapons_##id() \
-       { \
-               WEP_LAST = (id = WEP_FIRST + WEP_COUNT); \
-               bit = WepSet_FromWeapon(id); \
-               WEPSET_ALL |= bit; \
-               if((weapontype) & WEP_FLAG_SUPERWEAPON) \
-                       WEPSET_SUPERWEAPONS |= bit; \
-               ++WEP_COUNT; \
-               register_weapon(id,bit,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname); \
-       } \
-       ACCUMULATE_FUNCTION(RegisterWeapons, RegisterWeapons_##id)
-#ifdef MENUQC
-#define REGISTER_WEAPON(id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \
-       REGISTER_WEAPON_2(WEP_##id,WEPSET_##id,w_null,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname)
-#else
-#define REGISTER_WEAPON(id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \
-       REGISTER_WEAPON_2(WEP_##id,WEPSET_##id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname)
-#endif
-
-#include "../server/w_all.qc"
-
-#undef REGISTER_WEAPON
-ACCUMULATE_FUNCTION(RegisterWeapons, register_weapons_done);
-
-
-string W_FixWeaponOrder(string order, float complete);
-string W_NumberWeaponOrder(string order);
-string W_NameWeaponOrder(string order);
-string W_FixWeaponOrder_BuildImpulseList(string o);
-string W_FixWeaponOrder_AllowIncomplete(string order);
-string W_FixWeaponOrder_ForceComplete(string order);
-
-void W_RandomWeapons(entity e, float n);
-
-string W_Name(float weaponid);
-
-float W_AmmoItemCode(float wpn);
index d1503a7b6b8bae9ea53c37c7bce2717ac64931f4..f2f6505679ca8c11167f451730b0a85a3c1945b5 100644 (file)
@@ -2558,6 +2558,30 @@ void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t
                queue_start.FindConnectedComponent_processing = 0;
 }
 
+#ifdef SVQC
+vector combine_to_vector(float x, float y, float z)
+{
+       vector result; result_x = x; result_y = y; result_z = z;
+       return result;
+}
+
+vector get_corner_position(entity box, float corner)
+{
+       switch(corner)
+       {
+               case 1: return combine_to_vector(box.absmin_x, box.absmin_y, box.absmin_z);
+               case 2: return combine_to_vector(box.absmax_x, box.absmin_y, box.absmin_z);
+               case 3: return combine_to_vector(box.absmin_x, box.absmax_y, box.absmin_z);
+               case 4: return combine_to_vector(box.absmin_x, box.absmin_y, box.absmax_z);
+               case 5: return combine_to_vector(box.absmax_x, box.absmax_y, box.absmin_z);
+               case 6: return combine_to_vector(box.absmin_x, box.absmax_y, box.absmax_z);
+               case 7: return combine_to_vector(box.absmax_x, box.absmin_y, box.absmax_z);
+               case 8: return combine_to_vector(box.absmax_x, box.absmax_y, box.absmax_z);
+               default: return '0 0 0';
+       }
+}
+#endif
+
 // todo: this sucks, lets find a better way to do backtraces?
 #ifndef MENUQC
 void backtrace(string msg)
index a9f9095c7a40492e6615258364cc0ca059c08620..0239c18af638f519de47a539a3e6a87192aae463 100644 (file)
@@ -364,6 +364,10 @@ typedef entity(entity cur, entity near, entity pass) findNextEntityNearFunction_
 typedef float(entity a, entity b, entity pass) isConnectedFunction_t;
 void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass);
 
+#ifdef SVQC
+vector get_corner_position(entity box, float corner);
+#endif
+
 // expand multiple arguments into one argument by stripping parenthesis
 #define XPD(...) __VA_ARGS__
 
diff --git a/qcsrc/common/weapons/all.qh b/qcsrc/common/weapons/all.qh
new file mode 100644 (file)
index 0000000..de6266f
--- /dev/null
@@ -0,0 +1,26 @@
+// ONLY EVER ADD NEW WEAPONS AT THE END. IF YOU REMOVE ONE, PUT THE LAST ONE ON
+// ITS PLACE. THIS IS TO AVOID UNNECESSARY RENUMBERING OF WEAPON IMPULSES.
+// IF YOU DISREGARD THIS NOTICE, I'LL KILL YOU WITH THE @!#%'N TUBA
+
+// core weapons
+#include "w_blaster.qc"
+#include "w_shockwave.qc"
+#include "w_machinegun.qc"
+#include "w_mortar.qc"
+#include "w_minelayer.qc"
+#include "w_electro.qc"
+#include "w_arc.qc"
+#include "w_crylink.qc"
+#include "w_nex.qc"
+#include "w_hagar.qc"
+#include "w_devastator.qc"
+
+// other weapons
+#include "w_porto.qc"
+#include "w_minstanex.qc"
+#include "w_hook.qc"
+#include "w_hlac.qc"
+#include "w_tuba.qc"
+#include "w_rifle.qc"
+#include "w_fireball.qc"
+#include "w_seeker.qc"
diff --git a/qcsrc/common/weapons/calculations.qc b/qcsrc/common/weapons/calculations.qc
new file mode 100644 (file)
index 0000000..183a5e5
--- /dev/null
@@ -0,0 +1,261 @@
+// =============================
+//  Explosion Force Calculation
+// =============================
+
+float explosion_calcpush_getmultiplier(vector explosion_v, vector target_v)
+{
+       float a;
+       a  = explosion_v * (explosion_v - target_v);
+
+       if(a <= 0)
+               // target is too fast to be hittable by this
+               return 0;
+
+       a /= (explosion_v * explosion_v);
+               // we know we can divide by this, or above a would be == 0
+
+       return a;
+}
+
+#if 0
+vector explosion_calcpush(vector explosion_v, float explosion_m, vector target_v, float target_m, float elasticity)
+{
+       // solution of the equations:
+       //    v'                = v + a vp             // central hit
+       //    m*v'   + mp*vp'   = m*v + mp*vp          // conservation of momentum
+       //    m*v'^2 + mp*vp'^2 = m*v^2 + mp*vp^2      // conservation of energy (ELASTIC hit)
+       // -> a = 0                                    // case 1: did not hit
+       // -> a = 2*mp*(vp^2 - vp.v) / ((m+mp) * vp^2) // case 2: did hit
+       //                                             // non-elastic hits are somewhere between these two
+
+       // this would be physically correct, but we don't do that
+       return explosion_v * explosion_calcpush_getmultiplier(explosion_v, target_v) * (
+               (1 + elasticity) * (
+                       explosion_m
+               ) / (
+                       target_m + explosion_m
+               )
+       ); // note: this factor is at least 0, at most 2
+}
+#endif
+
+// simplified formula, tuned so that if the target has velocity 0, we get exactly the original force
+vector damage_explosion_calcpush(vector explosion_f, vector target_v, float speedfactor)
+{
+       // if below 1, the formulas make no sense (and would cause superjumps)
+       if(speedfactor < 1)
+               return explosion_f;
+
+#if 0
+       float m;
+       // find m so that
+       //   speedfactor * (1 + e) * m / (1 + m) == 1
+       m = 1 / ((1 + 0) * speedfactor - 1);
+       vector v;
+       v = explosion_calcpush(explosion_f * speedfactor, m, target_v, 1, 0);
+       // the factor we then get is:
+       //   1
+       print(sprintf("MASS: %f\nv: %v -> %v\nENERGY BEFORE == %f + %f = %f\nENERGY AFTER >= %f\n",
+               m,
+               target_v, target_v + v,
+               target_v * target_v, m * explosion_f * speedfactor * explosion_f * speedfactor, target_v * target_v + m * explosion_f * speedfactor * explosion_f * speedfactor,
+               (target_v + v) * (target_v + v)));
+       return v;
+#endif
+       return explosion_f * explosion_calcpush_getmultiplier(explosion_f * speedfactor, target_v);
+}
+
+
+// =========================
+//  Shot Spread Calculation
+// =========================
+
+vector cliptoplane(vector v, vector p)
+{
+       return v - (v * p) * p;
+}
+
+vector solve_cubic_pq(float p, float q)
+{
+       float D, u, v, a;
+       D = q*q/4.0 + p*p*p/27.0;
+       if(D < 0)
+       {
+               // irreducibilis
+               a = 1.0/3.0 * acos(-q/2.0 * sqrt(-27.0/(p*p*p)));
+               u = sqrt(-4.0/3.0 * p);
+               // a in range 0..pi/3
+               // cos(a)
+               // cos(a + 2pi/3)
+               // cos(a + 4pi/3)
+               return
+                       u *
+                       (
+                               '1 0 0' * cos(a + 2.0/3.0*M_PI)
+                               +
+                               '0 1 0' * cos(a + 4.0/3.0*M_PI)
+                               +
+                               '0 0 1' * cos(a)
+                       );
+       }
+       else if(D == 0)
+       {
+               // simple
+               if(p == 0)
+                       return '0 0 0';
+               u = 3*q/p;
+               v = -u/2;
+               if(u >= v)
+                       return '1 1 0' * v + '0 0 1' * u;
+               else
+                       return '0 1 1' * v + '1 0 0' * u;
+       }
+       else
+       {
+               // cardano
+               u = cbrt(-q/2.0 + sqrt(D));
+               v = cbrt(-q/2.0 - sqrt(D));
+               return '1 1 1' * (u + v);
+       }
+}
+vector solve_cubic_abcd(float a, float b, float c, float d)
+{
+       // y = 3*a*x + b
+       // x = (y - b) / 3a
+       float p, q;
+       vector v;
+       p = (9*a*c - 3*b*b);
+       q = (27*a*a*d - 9*a*b*c + 2*b*b*b);
+       v = solve_cubic_pq(p, q);
+       v = (v -  b * '1 1 1') * (1.0 / (3.0 * a));
+       if(a < 0)
+               v += '1 0 -1' * (v_z - v_x); // swap x, z
+       return v;
+}
+
+vector findperpendicular(vector v)
+{
+       vector p;
+       p_x = v_z;
+       p_y = -v_x;
+       p_z = v_y;
+       return normalize(cliptoplane(p, v));
+}
+
+vector W_CalculateSpread(vector forward, float spread, float spreadfactor, float spreadstyle)
+{
+       float sigma;
+       vector v1 = '0 0 0', v2;
+       float dx, dy, r;
+       float sstyle;
+       spread *= spreadfactor; //g_weaponspreadfactor;
+       if(spread <= 0)
+               return forward;
+       sstyle = spreadstyle; //autocvar_g_projectiles_spread_style;
+       
+       if(sstyle == 0)
+       {
+               // this is the baseline for the spread value!
+               // standard deviation: sqrt(2/5)
+               // density function: sqrt(1-r^2)
+               return forward + randomvec() * spread;
+       }
+       else if(sstyle == 1)
+       {
+               // same thing, basically
+               return normalize(forward + cliptoplane(randomvec() * spread, forward));
+       }
+       else if(sstyle == 2)
+       {
+               // circle spread... has at sigma=1 a standard deviation of sqrt(1/2)
+               sigma = spread * 0.89442719099991587855; // match baseline stddev
+               v1 = findperpendicular(forward);
+               v2 = cross(forward, v1);
+               // random point on unit circle
+               dx = random() * 2 * M_PI;
+               dy = sin(dx);
+               dx = cos(dx);
+               // radius in our dist function
+               r = random();
+               r = sqrt(r);
+               return normalize(forward + (v1 * dx + v2 * dy) * r * sigma);
+       }
+       else if(sstyle == 3) // gauss 3d
+       {
+               sigma = spread * 0.44721359549996; // match baseline stddev
+               // note: 2D gaussian has sqrt(2) times the stddev of 1D, so this factor is right
+               v1 = forward;
+               v1_x += gsl_ran_gaussian(sigma);
+               v1_y += gsl_ran_gaussian(sigma);
+               v1_z += gsl_ran_gaussian(sigma);
+               return v1;
+       }
+       else if(sstyle == 4) // gauss 2d
+       {
+               sigma = spread * 0.44721359549996; // match baseline stddev
+               // note: 2D gaussian has sqrt(2) times the stddev of 1D, so this factor is right
+               v1_x = gsl_ran_gaussian(sigma);
+               v1_y = gsl_ran_gaussian(sigma);
+               v1_z = gsl_ran_gaussian(sigma);
+               return normalize(forward + cliptoplane(v1, forward));
+       }
+       else if(sstyle == 5) // 1-r
+       {
+               sigma = spread * 1.154700538379252; // match baseline stddev
+               v1 = findperpendicular(forward);
+               v2 = cross(forward, v1);
+               // random point on unit circle
+               dx = random() * 2 * M_PI;
+               dy = sin(dx);
+               dx = cos(dx);
+               // radius in our dist function
+               r = random();
+               r = solve_cubic_abcd(-2, 3, 0, -r) * '0 1 0';
+               return normalize(forward + (v1 * dx + v2 * dy) * r * sigma);
+       }
+       else if(sstyle == 6) // 1-r^2
+       {
+               sigma = spread * 1.095445115010332; // match baseline stddev
+               v1 = findperpendicular(forward);
+               v2 = cross(forward, v1);
+               // random point on unit circle
+               dx = random() * 2 * M_PI;
+               dy = sin(dx);
+               dx = cos(dx);
+               // radius in our dist function
+               r = random();
+               r = sqrt(1 - r);
+               r = sqrt(1 - r);
+               return normalize(forward + (v1 * dx + v2 * dy) * r * sigma);
+       }
+       else if(sstyle == 7) // (1-r) (2-r)
+       {
+               sigma = spread * 1.224744871391589; // match baseline stddev
+               v1 = findperpendicular(forward);
+               v2 = cross(forward, v1);
+               // random point on unit circle
+               dx = random() * 2 * M_PI;
+               dy = sin(dx);
+               dx = cos(dx);
+               // radius in our dist function
+               r = random();
+               r = 1 - sqrt(r);
+               r = 1 - sqrt(r);
+               return normalize(forward + (v1 * dx + v2 * dy) * r * sigma);
+       }
+       else
+               error("g_projectiles_spread_style must be 0 (sphere), 1 (flattened sphere), 2 (circle), 3 (gauss 3D), 4 (gauss plane), 5 (linear falloff), 6 (quadratic falloff), 7 (stronger falloff)!");
+       return '0 0 0';
+       /*
+        * how to derive falloff functions:
+        * rho(r) := (2-r) * (1-r);
+        * a : 0;
+        * b : 1;
+        * rhor(r) := r * rho(r);
+        * cr(t) := integrate(rhor(r), r, a, t);
+        * scr(t) := integrate(rhor(r) * r^2, r, a, t);
+        * variance : scr(b) / cr(b);
+        * solve(cr(r) = rand * cr(b), r), programmmode:false;
+        * sqrt(0.4 / variance), numer;
+        */
+}
diff --git a/qcsrc/common/weapons/calculations.qh b/qcsrc/common/weapons/calculations.qh
new file mode 100644 (file)
index 0000000..9a6dd1c
--- /dev/null
@@ -0,0 +1,2 @@
+vector damage_explosion_calcpush(vector explosion_f, vector target_v, float speedfactor);
+vector W_CalculateSpread(vector forward, float spread, float spreadfactor, float spreadstyle);
diff --git a/qcsrc/common/weapons/config.qc b/qcsrc/common/weapons/config.qc
new file mode 100644 (file)
index 0000000..5fec9ca
--- /dev/null
@@ -0,0 +1,61 @@
+// ==========================
+//  Balance Config Generator
+// ==========================
+
+void W_Config_Queue_Swap(float root, float child, entity pass)
+{
+       string oldroot = wep_config_queue[root];
+       wep_config_queue[root] = wep_config_queue[child];
+       wep_config_queue[child] = oldroot;
+}
+
+float W_Config_Queue_Compare(float root, float child, entity pass)
+{
+       float i, r, c;
+
+       for(i = 1; i <= 100; ++i)
+       {
+               r = str2chr(wep_config_queue[root], i);
+               c = str2chr(wep_config_queue[child], i);
+               if(r == c) { continue; }
+               else if(c > r) { return -1; }
+               else { return 1; }
+       }
+       
+       return 0;
+}
+
+void Dump_Weapon_Settings(void)
+{
+       float i, x, totalsettings = 0;
+       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+       {
+               // step 1: clear the queue
+               WEP_CONFIG_COUNT = 0;
+               for(x = 0; x <= MAX_WEP_CONFIG; ++x)
+                       { wep_config_queue[x] = string_null; }
+
+               // step 2: build new queue
+               WEP_ACTION(i, WR_CONFIG);
+
+               // step 3: sort queue
+               heapsort(WEP_CONFIG_COUNT, W_Config_Queue_Swap, W_Config_Queue_Compare, world);
+               
+               // step 4: write queue
+               WEP_CONFIG_WRITETOFILE(sprintf("// {{{ #%d: %s\n", i, W_Name(i)))
+               for(x = 0; x <= WEP_CONFIG_COUNT; ++x) { WEP_CONFIG_WRITETOFILE(wep_config_queue[x]) }
+               WEP_CONFIG_WRITETOFILE("// }}}\n")
+
+               // step 5: debug info
+               print(sprintf("#%d: %s: %d settings...\n", i, W_Name(i), WEP_CONFIG_COUNT));
+               totalsettings += WEP_CONFIG_COUNT;
+       }
+
+       // clear queue now that we're finished
+       WEP_CONFIG_COUNT = 0;
+       for(x = 0; x <= MAX_WEP_CONFIG; ++x)
+               { wep_config_queue[x] = string_null; }
+
+       // extra information
+       print(sprintf("Totals: %d weapons, %d settings\n", (i - 1), totalsettings));
+}
diff --git a/qcsrc/common/weapons/config.qh b/qcsrc/common/weapons/config.qh
new file mode 100644 (file)
index 0000000..a6cb643
--- /dev/null
@@ -0,0 +1,57 @@
+// ==========================
+//  Balance Config Generator
+// ==========================
+
+void Dump_Weapon_Settings(void);
+float wep_config_file;
+float wep_config_alsoprint;
+
+#define MAX_WEP_CONFIG 256
+float WEP_CONFIG_COUNT;
+string wep_config_queue[MAX_WEP_CONFIG];
+
+#define WEP_CONFIG_QUEUE(a) { \
+       wep_config_queue[WEP_CONFIG_COUNT] = a; \
+       ++WEP_CONFIG_COUNT; }
+
+#define WEP_CONFIG_WRITETOFILE(a) { \
+       fputs(wep_config_file, a); \
+       if(wep_config_alsoprint) { print(a); } }
+
+#define WEP_CONFIG_WRITE_CVARS(weapon,mode,name) \
+       #if mode == MO_PRI \
+               { WEP_CONFIG_QUEUE( \
+                       sprintf("set g_balance_%s_primary_%s %g\n", #weapon, #name, \
+                       cvar(sprintf("g_balance_%s_primary_%s", #weapon, #name)))) } \
+       #endif \
+       #if mode == MO_SEC \
+               { WEP_CONFIG_QUEUE( \
+                       sprintf("set g_balance_%s_secondary_%s %g\n", #weapon, #name, \
+                       cvar(sprintf("g_balance_%s_secondary_%s", #weapon, #name)))) } \
+       #endif \
+       #if mode == MO_BOTH \
+               { WEP_CONFIG_QUEUE( \
+                       sprintf("set g_balance_%s_primary_%s %g\n", #weapon, #name, \
+                       cvar(sprintf("g_balance_%s_primary_%s", #weapon, #name)))) } \
+               { WEP_CONFIG_QUEUE( \
+                       sprintf("set g_balance_%s_secondary_%s %g\n", #weapon, #name, \
+                       cvar(sprintf("g_balance_%s_secondary_%s", #weapon, #name)))) } \
+       #endif \
+       #if mode == MO_NONE \
+               { WEP_CONFIG_QUEUE( \
+                       sprintf("set g_balance_%s_%s %g\n", #weapon, #name, \
+                       cvar(sprintf("g_balance_%s_%s", #weapon, #name)))) } \
+       #endif
+
+// WEAPONTODO: remove prop
+#define WEP_CONFIG_WRITE_PROPS(weapon,name) \
+               { WEP_CONFIG_QUEUE( \
+                       sprintf("set g_balance_%s_%s %g\n", #weapon, #name, \
+                       cvar(sprintf("g_balance_%s_%s", #weapon, #name)))) }
+
+#define WEP_CONFIG_SETTINGS(wepsettings) \
+       #define WEP_ADD_CVAR(weapon,mode,name) WEP_CONFIG_WRITE_CVARS(weapon,mode,name) \
+       #define WEP_ADD_PROP(weapon,prop,name) WEP_CONFIG_WRITE_PROPS(weapon,name) \
+       wepsettings \
+       #undef WEP_ADD_CVAR \
+       #undef WEP_ADD_PROP
diff --git a/qcsrc/common/weapons/w_arc.qc b/qcsrc/common/weapons/w_arc.qc
new file mode 100644 (file)
index 0000000..a827246
--- /dev/null
@@ -0,0 +1,333 @@
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id */ ARC,
+/* function */ w_arc,
+/* ammotype */ IT_CELLS,
+/* impulse  */ 5,
+/* flags    */ WEP_FLAG_NORMAL,
+/* rating   */ BOT_PICKUP_RATING_HIGH,
+/* model    */ "arc",
+/* netname  */ "arc",
+/* fullname */ _("Arc")
+);
+
+#define ARC_SETTINGS(weapon) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, ammo) \
+       WEP_ADD_CVAR(weapon, MO_PRI,  animtime) \
+       WEP_ADD_CVAR(weapon, MO_PRI,  damage) \
+       WEP_ADD_CVAR(weapon, MO_PRI,  falloff_halflifedist) \
+       WEP_ADD_CVAR(weapon, MO_PRI,  falloff_maxdist) \
+       WEP_ADD_CVAR(weapon, MO_PRI,  falloff_mindist) \
+       WEP_ADD_CVAR(weapon, MO_PRI,  force) \
+       WEP_ADD_CVAR(weapon, MO_PRI,  range) \
+       WEP_ADD_CVAR(weapon, MO_PRI,  refire) \
+       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)
+
+#ifndef MENUQC
+vector arc_shotorigin[4];
+#endif
+#ifdef SVQC
+ARC_SETTINGS(arc)
+void ArcInit();
+.vector hook_start, hook_end; // used for beam
+.entity arc_beam; // used for beam
+.float BUTTON_ATCK_prev; // for better animation control
+.float lg_fire_prev; // for better animation control
+#endif
+#else
+#ifdef SVQC
+void spawnfunc_weapon_arc() { weapon_defaultspawnfunc(WEP_ARC); }
+
+float W_Arc_Beam_Send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_ARC_BEAM);
+       sf = sf & 0x7F;
+       if(sound_allowed(MSG_BROADCAST, self.owner))
+               sf |= 0x80;
+       WriteByte(MSG_ENTITY, sf);
+       if(sf & 1)
+       {
+               WriteByte(MSG_ENTITY, num_for_edict(self.owner));
+               WriteCoord(MSG_ENTITY, WEP_CVAR_PRI(arc, 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;
+}
+
+void W_Arc_Beam_Think()
+{
+       self.owner.lg_fire_prev = time;
+       if (self != self.owner.arc_beam)
+       {
+               remove(self);
+               return;
+       }
+       if (self.owner.weaponentity.state != WS_INUSE || (self.owner.ammo_cells <= 0 && !(self.owner.items & IT_UNLIMITED_WEAPON_AMMO)) || self.owner.deadflag != DEAD_NO || !self.owner.BUTTON_ATCK || self.owner.freezetag_frozen)
+       {
+               if(self == self.owner.arc_beam)
+                       self.owner.arc_beam = world;
+               remove(self);
+               return;
+       }
+
+       self.nextthink = time;
+
+       makevectors(self.owner.v_angle);
+
+       float dt, f;
+       dt = frametime;
+       if not(self.owner.items & IT_UNLIMITED_WEAPON_AMMO)
+       {
+               if(WEP_CVAR_PRI(arc, ammo))
+               {
+                       dt = min(dt, self.owner.ammo_cells / WEP_CVAR_PRI(arc, ammo));
+                       self.owner.ammo_cells = max(0, self.owner.ammo_cells - WEP_CVAR_PRI(arc, ammo) * frametime);
+               }
+       }
+
+       W_SetupShot_Range(self.owner, TRUE, 0, "", 0, WEP_CVAR_PRI(arc, damage) * dt, WEP_CVAR_PRI(arc, range));
+       WarpZone_traceline_antilag(self.owner, w_shotorg, w_shotend, MOVE_NORMAL, self.owner, ANTILAG_LATENCY(self.owner));
+
+       // apply the damage
+       if(trace_ent)
+       {
+               vector force;
+               force = w_shotdir * WEP_CVAR_PRI(arc, force);
+
+               f = ExponentialFalloff(WEP_CVAR_PRI(arc, falloff_mindist), WEP_CVAR_PRI(arc, falloff_maxdist), WEP_CVAR_PRI(arc, falloff_halflifedist), vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos) - w_shotorg));
+
+               if(accuracy_isgooddamage(self.owner, trace_ent))
+                       accuracy_add(self.owner, WEP_ARC, 0, WEP_CVAR_PRI(arc, damage) * dt * f);
+               Damage (trace_ent, self.owner, self.owner, WEP_CVAR_PRI(arc, damage) * dt * f, WEP_ARC, trace_endpos, force * dt);
+       }
+
+       // draw effect
+       if(w_shotorg != self.hook_start)
+       {
+               self.SendFlags |= 2;
+               self.hook_start = w_shotorg;
+       }
+       if(w_shotend != self.hook_end)
+       {
+               self.SendFlags |= 4;
+               self.hook_end = w_shotend;
+       }
+}
+
+// Attack functions ========================= 
+void W_Arc_Attack1 (void)
+{
+       // only play fire sound if 0.5 sec has passed since player let go the fire button
+       if(time - self.lg_fire_prev > 0.5)
+               sound (self, CH_WEAPON_A, "weapons/lgbeam_fire.wav", VOL_BASE, ATTN_NORM);
+
+       entity beam, oldself;
+
+       self.arc_beam = beam = spawn();
+       beam.classname = "W_Arc_Beam";
+       beam.solid = SOLID_NOT;
+       beam.think = W_Arc_Beam_Think;
+       beam.owner = self;
+       beam.movetype = MOVETYPE_NONE;
+       beam.shot_spread = 1;
+       beam.bot_dodge = TRUE;
+       beam.bot_dodgerating = WEP_CVAR_PRI(arc, damage);
+       Net_LinkEntity(beam, FALSE, 0, W_Arc_Beam_Send);
+
+       oldself = self;
+       self = beam;
+       self.think();
+       self = oldself;
+}
+
+float w_arc(float req)
+{
+       switch(req)
+       {
+               case WR_AIM:
+               {
+                       self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE);
+                       /*
+                       self.BUTTON_ATCK=FALSE;
+                       self.BUTTON_ATCK2=FALSE;
+                       if(vlen(self.origin-self.enemy.origin) > 1000)
+                               self.bot_aim_whichfiretype = 0;
+                       if(self.bot_aim_whichfiretype == 0)
+                       {
+                               float shoot;
+
+                               if(autocvar_g_balance_arc_primary_speed)
+                                       shoot = bot_aim(autocvar_g_balance_arc_primary_speed, 0, autocvar_g_balance_arc_primary_lifetime, FALSE);
+                               else
+                                       shoot = bot_aim(1000000, 0, 0.001, FALSE);
+
+                               if(shoot)
+                               {
+                                       self.BUTTON_ATCK = TRUE;
+                                       if(random() < 0.01) self.bot_aim_whichfiretype = 1;
+                               }
+                       }
+                       else // todo
+                       {
+                               //if(bot_aim(autocvar_g_balance_arc_secondary_speed, autocvar_g_balance_grenadelauncher_secondary_speed_up, autocvar_g_balance_arc_secondary_lifetime, TRUE))
+                               //{
+                               //      self.BUTTON_ATCK2 = TRUE;
+                               //      if(random() < 0.03) self.bot_aim_whichfiretype = 0;
+                               //}
+                       }
+                       */
+                       
+                       return TRUE;
+               }
+               case WR_THINK:
+               {
+                       if (self.BUTTON_ATCK)
+                       {
+                               if(self.BUTTON_ATCK_prev) // TODO: Find another way to implement this!
+                                       /*if(self.animstate_startframe == self.anim_shoot_x && self.animstate_numframes == self.anim_shoot_y)
+                                               weapon_thinkf(WFRAME_DONTCHANGE, autocvar_g_balance_arc_primary_animtime, w_ready);
+                                       else*/
+                                               weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(arc, animtime), w_ready);
+                               
+                               if (weapon_prepareattack(0, 0))
+                               {
+                                       if ((!self.arc_beam) || wasfreed(self.arc_beam))
+                                               W_Arc_Attack1();
+                                       
+                                       if(!self.BUTTON_ATCK_prev)
+                                       {
+                                               weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(arc, animtime), w_ready);
+                                               self.BUTTON_ATCK_prev = 1;
+                                       }
+                               }
+                       } 
+                       else // todo
+                       {
+                               if (self.BUTTON_ATCK_prev != 0)
+                               {
+                                       weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(arc, animtime), w_ready);
+                                       ATTACK_FINISHED(self) = time + WEP_CVAR_PRI(arc, refire) * W_WeaponRateFactor();
+                               }
+                               self.BUTTON_ATCK_prev = 0;
+                       }
+
+                       //if (self.BUTTON_ATCK2)
+                               //if (weapon_prepareattack(1, autocvar_g_balance_arc_secondary_refire))
+                               //{
+                               //      W_Arc_Attack2();
+                               //      self.arc_count = autocvar_g_balance_arc_secondary_count;
+                               //      weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_arc_secondary_animtime, w_arc_checkattack);
+                               //      self.arc_secondarytime = time + autocvar_g_balance_arc_secondary_refire2 * W_WeaponRateFactor();
+                               //}
+                               
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_model ("models/weapons/g_arc.md3");
+                       precache_model ("models/weapons/v_arc.md3");
+                       precache_model ("models/weapons/h_arc.iqm");
+                       //precache_sound ("weapons/arc_bounce.wav");
+                       precache_sound ("weapons/arc_fire.wav");
+                       precache_sound ("weapons/arc_fire2.wav");
+                       precache_sound ("weapons/arc_impact.wav");
+                       //precache_sound ("weapons/arc_impact_combo.wav");
+                       //precache_sound ("weapons/W_Arc_Beam_fire.wav");
+                       return TRUE;
+               }
+               case WR_CHECKAMMO1:
+               {
+                       return !WEP_CVAR_PRI(arc, ammo) || (self.ammo_cells > 0);
+               }
+               case WR_CHECKAMMO2:
+               {
+                       return self.ammo_cells >= WEP_CVAR_SEC(arc, ammo);
+               }
+               case WR_CONFIG:
+               {
+                       WEP_CONFIG_SETTINGS(ARC_SETTINGS(arc))
+                       return TRUE;
+               }
+               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;
+                       }
+               }
+               case WR_RESETPLAYER:
+               {
+                       //self.arc_secondarytime = time;
+                       return TRUE;
+               }
+       }
+       return TRUE;
+}
+
+void ArcInit()
+{
+       WEP_ACTION(WEP_ARC, WR_INIT);
+       arc_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 1);
+       arc_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 2);
+       arc_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 3);
+       arc_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ARC), FALSE, FALSE, 4);
+       WEP_SET_PROPS(ARC_SETTINGS(arc), WEP_ARC)
+}
+#endif
+#ifdef CSQC
+float w_arc(float req)
+{
+       switch(req)
+       {
+               case WR_IMPACTEFFECT:
+               {
+                       vector org2;
+                       org2 = w_org + w_backoff * 6;
+                       
+                       if(w_deathtype & HITTYPE_SECONDARY)
+                       {
+                               pointparticles(particleeffectnum("arc_ballexplode"), org2, '0 0 0', 1);
+                               if(!w_issilent)
+                                       sound(self, CH_SHOTS, "weapons/arc_impact.wav", VOL_BASE, ATTN_NORM);
+                       }
+                       else
+                       {
+                               pointparticles(particleeffectnum("arc_impact"), org2, '0 0 0', 1);
+                               if(!w_issilent)
+                                       sound(self, CH_SHOTS, "weapons/arc_impact.wav", VOL_BASE, ATTN_NORM);
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_sound("weapons/arc_impact.wav");
+                       precache_sound("weapons/arc_impact_combo.wav");
+                       return TRUE;
+               }
+       }
+       return TRUE;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/w_blaster.qc b/qcsrc/common/weapons/w_blaster.qc
new file mode 100644 (file)
index 0000000..eddcbae
--- /dev/null
@@ -0,0 +1,252 @@
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id */ LASER,
+/* function */ W_Laser,
+/* ammotype */ 0,
+/* impulse  */ 1,
+/* flags    */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
+/* rating   */ 0,
+/* model    */ "laser",
+/* netname  */ "laser",
+/* fullname */ _("Blaster")
+);
+#else
+#ifdef SVQC
+void spawnfunc_weapon_laser() { weapon_defaultspawnfunc(WEP_LASER); }
+void(float imp) W_SwitchWeapon;
+void() W_LastWeapon;
+
+void SendCSQCShockwaveParticle(vector endpos) 
+{
+       //endpos = WarpZone_UnTransformOrigin(transform, endpos);
+       
+       WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
+       WriteByte(MSG_BROADCAST, TE_CSQC_SHOCKWAVEPARTICLE);
+       WriteCoord(MSG_BROADCAST, w_shotorg_x);
+       WriteCoord(MSG_BROADCAST, w_shotorg_y);
+       WriteCoord(MSG_BROADCAST, w_shotorg_z);
+       WriteCoord(MSG_BROADCAST, endpos_x);
+       WriteCoord(MSG_BROADCAST, endpos_y);
+       WriteCoord(MSG_BROADCAST, endpos_z);
+       WriteByte(MSG_BROADCAST, bound(0, autocvar_g_balance_laser_shockwave_spread_max, 255));
+       WriteByte(MSG_BROADCAST, bound(0, autocvar_g_balance_laser_shockwave_spread_min, 255));
+       WriteByte(MSG_BROADCAST, num_for_edict(self));
+}
+
+void W_Laser_Touch()
+{
+       PROJECTILE_TOUCH;
+
+       self.event_damage = func_null;
+       
+       if(self.dmg)
+               RadiusDamage(self, self.realowner, autocvar_g_balance_laser_secondary_damage, autocvar_g_balance_laser_secondary_edgedamage, autocvar_g_balance_laser_secondary_radius, world, world, autocvar_g_balance_laser_secondary_force, self.projectiledeathtype, other);
+       else
+               RadiusDamage(self, self.realowner, autocvar_g_balance_laser_primary_damage, autocvar_g_balance_laser_primary_edgedamage, autocvar_g_balance_laser_primary_radius, world, world, autocvar_g_balance_laser_primary_force, self.projectiledeathtype, other);
+
+       remove(self);
+}
+
+void W_Laser_Think()
+{
+       self.movetype = MOVETYPE_FLY;
+       self.think = SUB_Remove;
+       
+       if(self.dmg)
+               self.nextthink = time + autocvar_g_balance_laser_secondary_lifetime;
+       else
+               self.nextthink = time + autocvar_g_balance_laser_primary_lifetime;
+               
+       CSQCProjectile(self, TRUE, PROJECTILE_LASER, TRUE);
+}
+
+void W_Laser_Attack(float issecondary)
+{
+       entity missile;
+       vector s_forward;
+       float a;
+
+       a = autocvar_g_balance_laser_primary_shotangle;
+       s_forward = v_forward * cos(a * DEG2RAD) + v_up * sin(a * DEG2RAD);
+
+       //if(nodamage)
+       //      W_SetupShot_Dir(self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, 0);
+       /*else*/if(issecondary == 1)
+               W_SetupShot_Dir(self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_secondary_damage);
+       else
+               W_SetupShot_Dir(self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_primary_damage);
+       pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+       missile = spawn();
+       missile.owner = missile.realowner = self;
+       missile.classname = "laserbolt";
+       missile.dmg = 0;
+       missile.bot_dodge = TRUE;
+       missile.bot_dodgerating = autocvar_g_balance_laser_primary_damage;
+
+       PROJECTILE_MAKETRIGGER(missile);
+       missile.projectiledeathtype = WEP_LASER;
+
+       setorigin(missile, w_shotorg);
+       setsize(missile, '0 0 0', '0 0 0');
+
+       W_SETUPPROJECTILEVELOCITY(missile, g_balance_laser_primary);
+       missile.angles = vectoangles(missile.velocity);
+       //missile.glow_color = 250; // 244, 250
+       //missile.glow_size = 120;
+       missile.touch = W_Laser_Touch;
+
+       missile.flags = FL_PROJECTILE;
+       missile.missile_flags = MIF_SPLASH; 
+
+       missile.think = W_Laser_Think;
+       missile.nextthink = time + autocvar_g_balance_laser_primary_delay;
+
+       other = missile; MUTATOR_CALLHOOK(EditProjectile);
+
+       if(time >= missile.nextthink)
+       {
+               entity oldself;
+               oldself = self;
+               self = missile;
+               self.think();
+               self = oldself;
+       }
+}
+
+float W_Laser(float request)
+{
+       switch(request)
+       {
+               case WR_AIM:
+               {
+                       if((autocvar_g_balance_laser_secondary == 2) && (vlen(self.origin-self.enemy.origin) <= autocvar_g_balance_laser_melee_range))
+                               self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE);
+                       else
+                               self.BUTTON_ATCK = bot_aim(1000000, 0, 1, FALSE);
+                       return TRUE;
+               }
+               
+               case WR_THINK:
+               {
+                       if(autocvar_g_balance_laser_reload_ammo && self.clip_load < 1) // forced reload
+                               WEP_ACTION(self.weapon, WR_RELOAD);
+                       else if(self.BUTTON_ATCK)
+                       {
+                               if(weapon_prepareattack(0, autocvar_g_balance_laser_primary_refire))
+                               {
+                                       W_DecreaseAmmo(ammo_none, 1, TRUE);
+
+                                       if not(autocvar_g_balance_laser_primary)
+                                               W_Laser_Shockwave();
+                                       else
+                                               W_Laser_Attack(FALSE);
+
+                                       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_laser_primary_animtime, w_ready);
+                               }
+                       }
+                       else if(self.BUTTON_ATCK2)
+                       {
+                               switch(autocvar_g_balance_laser_secondary)
+                               {
+                                       case 0: // switch to last used weapon
+                                       {
+                                               if(self.switchweapon == WEP_LASER) // don't do this if already switching
+                                                       W_LastWeapon();
+
+                                               break;
+                                       }
+
+                                       case 1: // normal projectile secondary
+                                       {
+                                               if(weapon_prepareattack(1, autocvar_g_balance_laser_secondary_refire))
+                                               {
+                                                       W_DecreaseAmmo(ammo_none, 1, TRUE);
+                                                       W_Laser_Attack(TRUE);
+                                                       weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_laser_secondary_animtime, w_ready);
+                                               }
+
+                                               break;
+                                       }
+
+                                       case 2: // melee attack secondary
+                                       {
+                                               if(!self.crouch) // we are not currently crouching; this fixes an exploit where your melee anim is not visible, and besides wouldn't make much sense
+                                               if(weapon_prepareattack(1, autocvar_g_balance_laser_melee_refire))
+                                               {
+                                                       // attempt forcing playback of the anim by switching to another anim (that we never play) here...
+                                                       W_Laser_Melee();
+                                                       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_laser_melee_animtime, w_ready);
+                                               }
+                                       }
+                               }
+                       }
+                       return TRUE;
+               }
+               
+               case WR_INIT: 
+               {
+                       precache_model("models/weapons/g_laser.md3");
+                       precache_model("models/weapons/v_laser.md3");
+                       precache_model("models/weapons/h_laser.iqm");
+                       precache_sound("weapons/lasergun_fire.wav");
+                       return TRUE;
+               }
+               
+               case WR_SETUP:
+               {
+                       self.current_ammo = ammo_none;
+                       return TRUE;
+               }
+               
+               case WR_CHECKAMMO1:
+               case WR_CHECKAMMO2:
+               {
+                       return TRUE; // laser has infinite ammo
+               }
+               
+               case WR_RELOAD:
+               {
+                       W_Reload(0, "weapons/reload.wav");
+                       return TRUE;
+               }
+               
+               case WR_SUICIDEMESSAGE:
+               {
+                       return WEAPON_LASER_SUICIDE;
+               }
+               
+               case WR_KILLMESSAGE:
+               {
+                       return WEAPON_LASER_MURDER;
+               }
+       }
+       
+       return TRUE;
+}
+#endif
+#ifdef CSQC
+float W_Laser(float request)
+{
+       switch(request)
+       {
+               case WR_IMPACTEFFECT:
+               {
+                       vector org2;
+                       org2 = w_org + w_backoff * 6;
+                       pointparticles(particleeffectnum("new_laser_impact"), org2, w_backoff * 1000, 1);
+                       if(!w_issilent) { sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM); }
+                       return TRUE;
+               }
+               
+               case WR_INIT:
+               {
+                       precache_sound("weapons/laserimpact.wav");
+                       return TRUE;
+               }
+       }
+       
+       return TRUE;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/w_crylink.qc b/qcsrc/common/weapons/w_crylink.qc
new file mode 100644 (file)
index 0000000..4e46f1f
--- /dev/null
@@ -0,0 +1,712 @@
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id */ CRYLINK,
+/* function */ w_crylink,
+/* ammotype */ IT_CELLS,
+/* impulse  */ 6,
+/* flags    */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH,
+/* rating   */ BOT_PICKUP_RATING_MID,
+/* model    */ "crylink",
+/* netname  */ "crylink",
+/* fullname */ _("Crylink")
+);
+
+#define CRYLINK_SETTINGS(weapon) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, ammo) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, animtime) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, damage) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, edgedamage) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, radius) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, force) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, spread) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, refire) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, speed) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, shots) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, bounces) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, bouncedamagefactor) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, middle_lifetime) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, middle_fadetime) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, other_lifetime) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, other_fadetime) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, linkexplode) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, joindelay) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, joinspread) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, joinexplode) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, joinexplode_damage) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, joinexplode_edgedamage) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, joinexplode_radius) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, joinexplode_force) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  spreadtype) \
+       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
+CRYLINK_SETTINGS(crylink)
+.float gravity;
+.float crylink_waitrelease;
+.entity crylink_lastgroup;
+
+.entity queuenext;
+.entity queueprev;
+#endif
+#else
+#ifdef SVQC
+void spawnfunc_weapon_crylink() { weapon_defaultspawnfunc(WEP_CRYLINK); }
+
+void W_Crylink_CheckLinks(entity e)
+{
+       float i;
+       entity p;
+
+       if(e == world)
+               error("W_Crylink_CheckLinks: entity is world");
+       if(e.classname != "spike" || wasfreed(e))
+               error(sprintf("W_Crylink_CheckLinks: entity is not a spike but a %s (freed: %d)", e.classname, wasfreed(e)));
+
+       p = e;
+       for(i = 0; i < 1000; ++i)
+       {
+               if(p.queuenext.queueprev != p || p.queueprev.queuenext != p)
+                       error("W_Crylink_CheckLinks: queue is inconsistent");
+               p = p.queuenext;
+               if(p == e)
+                       break;
+       }
+       if(i >= 1000)
+               error("W_Crylink_CheckLinks: infinite chain");
+}
+
+void W_Crylink_Dequeue_Raw(entity own, entity prev, entity me, entity next)
+{
+       W_Crylink_CheckLinks(next);
+       if(me == own.crylink_lastgroup)
+               own.crylink_lastgroup = ((me == next) ? world : next);
+       prev.queuenext = next;
+       next.queueprev = prev;
+       me.classname = "spike_oktoremove";
+       if(me != next)
+               W_Crylink_CheckLinks(next);
+}
+
+void W_Crylink_Dequeue(entity e)
+{
+       W_Crylink_Dequeue_Raw(e.realowner, e.queueprev, e, e.queuenext);
+}
+
+void W_Crylink_Reset(void)
+{
+       W_Crylink_Dequeue(self);
+       remove(self);
+}
+
+// force projectile to explode
+void W_Crylink_LinkExplode (entity e, entity e2)
+{
+       float a;
+
+       if(e == e2)
+               return;
+
+       a = bound(0, 1 - (time - e.fade_time) * e.fade_rate, 1);
+
+       if(e == e.realowner.crylink_lastgroup)
+               e.realowner.crylink_lastgroup = world;
+               
+       float isprimary = !(e.projectiledeathtype & HITTYPE_SECONDARY);
+               
+       RadiusDamage(e, e.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * a, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * a, WEP_CVAR_BOTH(crylink, isprimary, radius), world, world, WEP_CVAR_BOTH(crylink, isprimary, force) * a, e.projectiledeathtype, other);
+
+       W_Crylink_LinkExplode(e.queuenext, e2);
+
+       e.classname = "spike_oktoremove";
+       remove (e);
+}
+
+// adjust towards center
+// returns the origin where they will meet... and the time till the meeting is
+// stored in w_crylink_linkjoin_time.
+// could possibly network this origin and time, and display a special particle
+// effect when projectiles meet there :P
+// jspeed: joining speed (calculate this as join spread * initial speed)
+float w_crylink_linkjoin_time;
+vector W_Crylink_LinkJoin(entity e, float jspeed)
+{
+       vector avg_origin, avg_velocity;
+       vector targ_origin;
+       float avg_dist, n;
+       entity p;
+
+       // FIXME remove this debug code
+       W_Crylink_CheckLinks(e);
+
+       w_crylink_linkjoin_time = 0;
+
+       avg_origin = e.origin;
+       avg_velocity = e.velocity;
+       n = 1;
+       for(p = e; (p = p.queuenext) != e; )
+       {
+               avg_origin += WarpZone_RefSys_TransformOrigin(p, e, p.origin);
+               avg_velocity += WarpZone_RefSys_TransformVelocity(p, e, p.velocity);
+               ++n;
+       }
+       avg_origin *= (1.0 / n);
+       avg_velocity *= (1.0 / n);
+
+       if(n < 2)
+               return avg_origin; // nothing to do
+
+       // yes, mathematically we can do this in ONE step, but beware of 32bit floats...
+       avg_dist = pow(vlen(e.origin - avg_origin), 2);
+       for(p = e; (p = p.queuenext) != e; )
+               avg_dist += pow(vlen(WarpZone_RefSys_TransformOrigin(p, e, p.origin) - avg_origin), 2);
+       avg_dist *= (1.0 / n);
+       avg_dist = sqrt(avg_dist);
+
+       if(avg_dist == 0)
+               return avg_origin; // no change needed
+
+       if(jspeed == 0)
+       {
+               e.velocity = avg_velocity;
+               UpdateCSQCProjectile(e);
+               for(p = e; (p = p.queuenext) != e; )
+               {
+                       p.velocity = WarpZone_RefSys_TransformVelocity(e, p, avg_velocity);
+                       UpdateCSQCProjectile(p);
+               }
+               targ_origin = avg_origin + 1000000000 * normalize(avg_velocity); // HUUUUUUGE
+       }
+       else
+       {
+               w_crylink_linkjoin_time = avg_dist / jspeed;
+               targ_origin = avg_origin + w_crylink_linkjoin_time * avg_velocity;
+
+               e.velocity = (targ_origin - e.origin) * (1.0 / w_crylink_linkjoin_time);
+               UpdateCSQCProjectile(e);
+               for(p = e; (p = p.queuenext) != e; )
+               {
+                       p.velocity = WarpZone_RefSys_TransformVelocity(e, p, (targ_origin - WarpZone_RefSys_TransformOrigin(p, e, p.origin)) * (1.0 / w_crylink_linkjoin_time));
+                       UpdateCSQCProjectile(p);
+               }
+
+               // analysis:
+               //   jspeed -> +infinity:
+               //      w_crylink_linkjoin_time -> +0
+               //      targ_origin -> avg_origin
+               //      p->velocity -> HUEG towards center
+               //   jspeed -> 0:
+               //      w_crylink_linkjoin_time -> +/- infinity
+               //      targ_origin -> avg_velocity * +/- infinity
+               //      p->velocity -> avg_velocity
+               //   jspeed -> -infinity:
+               //      w_crylink_linkjoin_time -> -0
+               //      targ_origin -> avg_origin
+               //      p->velocity -> HUEG away from center
+       }
+
+       W_Crylink_CheckLinks(e);
+
+       return targ_origin;
+}
+
+void W_Crylink_LinkJoinEffect_Think()
+{
+       // is there at least 2 projectiles very close?
+       entity e, p;
+       float n;
+       e = self.owner.crylink_lastgroup;
+       n = 0;
+       if(e)
+       {
+               if(vlen(e.origin - self.origin) < vlen(e.velocity) * frametime)
+                       ++n;
+               for(p = e; (p = p.queuenext) != e; )
+               {
+                       if(vlen(p.origin - self.origin) < vlen(p.velocity) * frametime)
+                               ++n;
+               }
+               if(n >= 2)
+               {
+                       float isprimary = !(e.projectiledeathtype & HITTYPE_SECONDARY);
+                       
+                       if(WEP_CVAR_BOTH(crylink, isprimary, joinexplode))
+                       {
+                               n /= WEP_CVAR_BOTH(crylink, isprimary, shots);
+                               RadiusDamage(e, e.realowner, WEP_CVAR_BOTH(crylink, isprimary, joinexplode_damage) * n,
+                                                                                        WEP_CVAR_BOTH(crylink, isprimary, joinexplode_edgedamage) * n,
+                                                                                        WEP_CVAR_BOTH(crylink, isprimary, joinexplode_radius) * n, e.realowner, world,
+                                                                                        WEP_CVAR_BOTH(crylink, isprimary, joinexplode_force) * n, e.projectiledeathtype, other);
+                               pointparticles(particleeffectnum("crylink_joinexplode"), self.origin, '0 0 0', n);
+                       }
+               }
+       }
+       remove(self);
+}
+
+float W_Crylink_Touch_WouldHitFriendly(entity projectile, float rad)
+{
+       entity head = WarpZone_FindRadius((projectile.origin + (projectile.mins + projectile.maxs) * 0.5), rad + MAX_DAMAGEEXTRARADIUS, FALSE);
+       float hit_friendly = 0;
+       float hit_enemy = 0;
+
+       while(head)
+       {
+               if((head.takedamage != DAMAGE_NO) && (head.deadflag == DEAD_NO))
+               {
+                       if(SAME_TEAM(head, projectile.realowner))
+                               ++hit_friendly;
+                       else
+                               ++hit_enemy;
+               }
+                       
+               head = head.chain;
+       }
+
+       return (hit_enemy ? FALSE : hit_friendly);
+}
+
+// NO bounce protection, as bounces are limited!
+void W_Crylink_Touch (void)
+{
+       float finalhit;
+       float f;
+       float isprimary = !(self.projectiledeathtype & HITTYPE_SECONDARY);
+       PROJECTILE_TOUCH;
+
+       float a;
+       a = bound(0, 1 - (time - self.fade_time) * self.fade_rate, 1);
+
+       finalhit = ((self.cnt <= 0) || (other.takedamage != DAMAGE_NO));
+       if(finalhit)
+               f = 1;
+       else
+               f = WEP_CVAR_BOTH(crylink, isprimary, bouncedamagefactor);
+       if(a)
+               f *= a;
+
+       float totaldamage = RadiusDamage(self, self.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * f, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * f, WEP_CVAR_BOTH(crylink, isprimary, radius), world, world, WEP_CVAR_BOTH(crylink, isprimary, force) * f, self.projectiledeathtype, other);
+               
+       if(totaldamage && ((WEP_CVAR_BOTH(crylink, isprimary, linkexplode) == 2) || ((WEP_CVAR_BOTH(crylink, isprimary, linkexplode) == 1) && !W_Crylink_Touch_WouldHitFriendly(self, WEP_CVAR_BOTH(crylink, isprimary, radius)))))
+       {
+               if(self == self.realowner.crylink_lastgroup)
+                       self.realowner.crylink_lastgroup = world;
+               W_Crylink_LinkExplode(self.queuenext, self);
+               self.classname = "spike_oktoremove";
+               remove (self);
+               return;
+       }
+       else if(finalhit)
+       {
+               // just unlink
+               W_Crylink_Dequeue(self);
+               remove(self);
+               return;
+       }
+       self.cnt = self.cnt - 1;
+       self.angles = vectoangles(self.velocity);
+       self.owner = world;
+       self.projectiledeathtype |= HITTYPE_BOUNCE;
+       // commented out as it causes a little hitch...
+       //if(proj.cnt == 0)
+       //      CSQCProjectile(proj, TRUE, PROJECTILE_CRYLINK, TRUE);
+}
+
+void W_Crylink_Fadethink (void)
+{
+       W_Crylink_Dequeue(self);
+       remove(self);
+}
+
+void W_Crylink_Attack (void)
+{
+       float counter, shots;
+       entity proj, prevproj, firstproj;
+       vector s;
+       vector forward, right, up;
+       float maxdmg;
+
+       W_DecreaseAmmo(ammo_cells, autocvar_g_balance_crylink_primary_ammo, autocvar_g_balance_crylink_reload_ammo);
+
+       maxdmg = WEP_CVAR_PRI(crylink, damage) * WEP_CVAR_PRI(crylink, shots);
+       maxdmg *= 1 + WEP_CVAR_PRI(crylink, bouncedamagefactor) * WEP_CVAR_PRI(crylink, bounces);
+       if(WEP_CVAR_PRI(crylink, joinexplode))
+               maxdmg += WEP_CVAR_PRI(crylink, joinexplode_damage);
+
+       W_SetupShot (self, FALSE, 2, "weapons/crylink_fire.wav", CH_WEAPON_A, maxdmg);
+       forward = v_forward;
+       right = v_right;
+       up = v_up;
+
+       shots = WEP_CVAR_PRI(crylink, shots);
+       pointparticles(particleeffectnum("crylink_muzzleflash"), w_shotorg, w_shotdir * 1000, shots);
+       proj = prevproj = firstproj = world;
+       for(counter = 0; counter < shots; ++counter)
+       {
+               proj = spawn ();
+               proj.reset = W_Crylink_Reset;
+               proj.realowner = proj.owner = self;
+               proj.classname = "spike";
+               proj.bot_dodge = TRUE;
+               proj.bot_dodgerating = WEP_CVAR_PRI(crylink, damage);
+               if(shots == 1) {
+                       proj.queuenext = proj;
+                       proj.queueprev = proj;
+               }
+               else if(counter == 0) { // first projectile, store in firstproj for now
+                       firstproj = proj;
+               }
+               else if(counter == shots - 1) { // last projectile, link up with first projectile
+                       prevproj.queuenext = proj;
+                       firstproj.queueprev = proj;
+                       proj.queuenext = firstproj;
+                       proj.queueprev = prevproj;
+               }
+               else { // else link up with previous projectile
+                       prevproj.queuenext = proj;
+                       proj.queueprev = prevproj;
+               }
+
+               prevproj = proj;
+
+               proj.movetype = MOVETYPE_BOUNCEMISSILE;
+               PROJECTILE_MAKETRIGGER(proj);
+               proj.projectiledeathtype = WEP_CRYLINK;
+               //proj.gravity = 0.001;
+
+               setorigin (proj, w_shotorg);
+               setsize(proj, '0 0 0', '0 0 0');
+
+
+               s = '0 0 0';
+               if (counter == 0)
+                       s = '0 0 0';
+               else
+               {
+                       makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1)));
+                       s_y = v_forward_x;
+                       s_z = v_forward_y;
+               }
+               s = s * WEP_CVAR_PRI(crylink, spread) * g_weaponspreadfactor;
+               W_SetupProjectileVelocityEx(proj, w_shotdir + right * s_y + up * s_z, v_up, WEP_CVAR_PRI(crylink, speed), 0, 0, 0, FALSE);
+               proj.touch = W_Crylink_Touch;
+
+               proj.think = W_Crylink_Fadethink;
+               if(counter == 0)
+               {
+                       proj.fade_time = time + WEP_CVAR_PRI(crylink, middle_lifetime);
+                       proj.fade_rate = 1 / WEP_CVAR_PRI(crylink, middle_fadetime);
+                       proj.nextthink = time + WEP_CVAR_PRI(crylink, middle_lifetime) + WEP_CVAR_PRI(crylink, middle_fadetime);
+               }
+               else
+               {
+                       proj.fade_time = time + WEP_CVAR_PRI(crylink, other_lifetime);
+                       proj.fade_rate = 1 / WEP_CVAR_PRI(crylink, other_fadetime);
+                       proj.nextthink = time + WEP_CVAR_PRI(crylink, other_lifetime) + WEP_CVAR_PRI(crylink, other_fadetime);
+               }
+               proj.teleport_time = time + WEP_CVAR_PRI(crylink, joindelay);
+               proj.cnt = WEP_CVAR_PRI(crylink, bounces);
+               //proj.scale = 1 + 1 * proj.cnt;
+
+               proj.angles = vectoangles (proj.velocity);
+
+               //proj.glow_size = 20;
+
+               proj.flags = FL_PROJECTILE;
+    proj.missile_flags = MIF_SPLASH;
+    
+               CSQCProjectile(proj, TRUE, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), TRUE);
+
+               other = proj; MUTATOR_CALLHOOK(EditProjectile);
+       }
+       if(WEP_CVAR_PRI(crylink, joinspread) != 0)
+       {
+               self.crylink_lastgroup = proj;
+               W_Crylink_CheckLinks(proj);
+               self.crylink_waitrelease = 1;
+       }
+}
+
+void W_Crylink_Attack2 (void)
+{
+       float counter, shots;
+       entity proj, prevproj, firstproj;
+       vector s;
+       vector forward, right, up;
+       float maxdmg;
+
+       W_DecreaseAmmo(ammo_cells, autocvar_g_balance_crylink_secondary_ammo, autocvar_g_balance_crylink_reload_ammo);
+
+       maxdmg = WEP_CVAR_SEC(crylink, damage) * WEP_CVAR_SEC(crylink, shots);
+       maxdmg *= 1 + WEP_CVAR_SEC(crylink, bouncedamagefactor) * WEP_CVAR_SEC(crylink, bounces);
+       if(WEP_CVAR_SEC(crylink, joinexplode))
+               maxdmg += WEP_CVAR_SEC(crylink, joinexplode_damage);
+
+       W_SetupShot (self, FALSE, 2, "weapons/crylink_fire2.wav", CH_WEAPON_A, maxdmg);
+       forward = v_forward;
+       right = v_right;
+       up = v_up;
+
+       shots = WEP_CVAR_SEC(crylink, shots);
+       pointparticles(particleeffectnum("crylink_muzzleflash"), w_shotorg, w_shotdir * 1000, shots);
+       proj = prevproj = firstproj = world;
+       for(counter = 0; counter < shots; ++counter)
+       {
+               proj = spawn ();
+               proj.reset = W_Crylink_Reset;
+               proj.realowner = proj.owner = self;
+               proj.classname = "spike";
+               proj.bot_dodge = TRUE;
+               proj.bot_dodgerating = WEP_CVAR_SEC(crylink, damage);
+               if(shots == 1) {
+                       proj.queuenext = proj;
+                       proj.queueprev = proj;
+               }
+               else if(counter == 0) { // first projectile, store in firstproj for now
+                       firstproj = proj;
+               }
+               else if(counter == shots - 1) { // last projectile, link up with first projectile
+                       prevproj.queuenext = proj;
+                       firstproj.queueprev = proj;
+                       proj.queuenext = firstproj;
+                       proj.queueprev = prevproj;
+               }
+               else { // else link up with previous projectile
+                       prevproj.queuenext = proj;
+                       proj.queueprev = prevproj;
+               }
+
+               prevproj = proj;
+
+               proj.movetype = MOVETYPE_BOUNCEMISSILE;
+               PROJECTILE_MAKETRIGGER(proj);
+               proj.projectiledeathtype = WEP_CRYLINK | HITTYPE_SECONDARY;
+               //proj.gravity = 0.001;
+
+               setorigin (proj, w_shotorg);
+               setsize(proj, '0 0 0', '0 0 0');
+
+               if(WEP_CVAR_SEC(crylink, spreadtype) == 1)
+               {
+                       s = '0 0 0';
+                       if (counter == 0)
+                               s = '0 0 0';
+                       else
+                       {
+                               makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1)));
+                               s_y = v_forward_x;
+                               s_z = v_forward_y;
+                       }
+                       s = s * WEP_CVAR_SEC(crylink, spread) * g_weaponspreadfactor;
+                       s = w_shotdir + right * s_y + up * s_z;
+               }
+               else
+               {
+                       s = (w_shotdir + (((counter + 0.5) / shots) * 2 - 1) * v_right * WEP_CVAR_SEC(crylink, spread) * g_weaponspreadfactor);
+               }
+
+               W_SetupProjectileVelocityEx(proj, s, v_up, WEP_CVAR_SEC(crylink, speed), 0, 0, 0, FALSE);
+               proj.touch = W_Crylink_Touch;
+               proj.think = W_Crylink_Fadethink;
+               if(counter == (shots - 1) / 2)
+               {
+                       proj.fade_time = time + WEP_CVAR_SEC(crylink, middle_lifetime);
+                       proj.fade_rate = 1 / WEP_CVAR_SEC(crylink, middle_fadetime);
+                       proj.nextthink = time + WEP_CVAR_SEC(crylink, middle_lifetime) + WEP_CVAR_SEC(crylink, middle_fadetime);
+               }
+               else
+               {
+                       proj.fade_time = time + WEP_CVAR_SEC(crylink, other_lifetime);
+                       proj.fade_rate = 1 / WEP_CVAR_SEC(crylink, other_fadetime);
+                       proj.nextthink = time + WEP_CVAR_SEC(crylink, other_lifetime) + WEP_CVAR_SEC(crylink, other_fadetime);
+               }
+               proj.teleport_time = time + WEP_CVAR_SEC(crylink, joindelay);
+               proj.cnt = WEP_CVAR_SEC(crylink, bounces);
+               //proj.scale = 1 + 1 * proj.cnt;
+
+               proj.angles = vectoangles (proj.velocity);
+
+               //proj.glow_size = 20;
+
+               proj.flags = FL_PROJECTILE;
+        proj.missile_flags = MIF_SPLASH;
+        
+               CSQCProjectile(proj, TRUE, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), TRUE);
+
+               other = proj; MUTATOR_CALLHOOK(EditProjectile);
+       }
+       if(WEP_CVAR_SEC(crylink, joinspread) != 0)
+       {
+               self.crylink_lastgroup = proj;
+               W_Crylink_CheckLinks(proj);
+               self.crylink_waitrelease = 2;
+       }
+}
+
+float w_crylink(float req)
+{
+       float ammo_amount;
+       switch(req)
+       {
+               case WR_AIM:
+               {
+                       if (random() < 0.10)
+                               self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(crylink, speed), 0, WEP_CVAR_PRI(crylink, middle_lifetime), FALSE);
+                       else
+                               self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_SEC(crylink, speed), 0, WEP_CVAR_SEC(crylink, middle_lifetime), FALSE);
+                               
+                       return TRUE;
+               }
+               case WR_THINK:
+               {
+                       if(autocvar_g_balance_crylink_reload_ammo && self.clip_load < min(autocvar_g_balance_crylink_primary_ammo, autocvar_g_balance_crylink_secondary_ammo)) // forced reload
+                               WEP_ACTION(self.weapon, WR_RELOAD);
+
+                       if (self.BUTTON_ATCK)
+                       {
+                               if (self.crylink_waitrelease != 1)
+                               if (weapon_prepareattack(0, WEP_CVAR_PRI(crylink, refire)))
+                               {
+                                       W_Crylink_Attack();
+                                       weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(crylink, animtime), w_ready);
+                               }
+                       }
+
+                       if(self.BUTTON_ATCK2 && autocvar_g_balance_crylink_secondary)
+                       {
+                               if (self.crylink_waitrelease != 2)
+                               if (weapon_prepareattack(1, WEP_CVAR_SEC(crylink, refire)))
+                               {
+                                       W_Crylink_Attack2();
+                                       weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(crylink, animtime), w_ready);
+                               }
+                       }
+
+                       if ((self.crylink_waitrelease == 1 && !self.BUTTON_ATCK) || (self.crylink_waitrelease == 2 && !self.BUTTON_ATCK2))
+                       {
+                               if (!self.crylink_lastgroup || time > self.crylink_lastgroup.teleport_time)
+                               {
+                                       // fired and released now!
+                                       if(self.crylink_lastgroup)
+                                       {
+                                               vector pos;
+                                               entity linkjoineffect;
+                                               float isprimary = (self.crylink_waitrelease == 1);
+                                               
+                                               pos = W_Crylink_LinkJoin(self.crylink_lastgroup, WEP_CVAR_BOTH(crylink, isprimary, joinspread) * WEP_CVAR_BOTH(crylink, isprimary, speed));
+
+                                               linkjoineffect = spawn();
+                                               linkjoineffect.think = W_Crylink_LinkJoinEffect_Think;
+                                               linkjoineffect.classname = "linkjoineffect";
+                                               linkjoineffect.nextthink = time + w_crylink_linkjoin_time;
+                                               linkjoineffect.owner = self;
+                                               setorigin(linkjoineffect, pos);
+                                       }
+                                       self.crylink_waitrelease = 0;
+                                       if(!w_crylink(WR_CHECKAMMO1) && !w_crylink(WR_CHECKAMMO2))
+                                       if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
+                                       {
+                                               // ran out of ammo!
+                                               self.cnt = WEP_CRYLINK;
+                                               self.switchweapon = w_getbestweapon(self);
+                                       }
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_model ("models/weapons/g_crylink.md3");
+                       precache_model ("models/weapons/v_crylink.md3");
+                       precache_model ("models/weapons/h_crylink.iqm");
+                       precache_sound ("weapons/crylink_fire.wav");
+                       precache_sound ("weapons/crylink_fire2.wav");
+                       precache_sound ("weapons/crylink_linkjoin.wav");
+                       WEP_SET_PROPS(CRYLINK_SETTINGS(crylink), WEP_CRYLINK)
+                       return TRUE;
+               }
+               case WR_SETUP:
+               {
+                       self.current_ammo = ammo_cells;
+                       return TRUE;
+               }
+               case WR_CHECKAMMO1:
+               {
+                       // don't "run out of ammo" and switch weapons while waiting for release
+                       if(self.crylink_lastgroup && self.crylink_waitrelease)
+                               return TRUE;
+
+                       ammo_amount = self.ammo_cells >= autocvar_g_balance_crylink_primary_ammo;
+                       ammo_amount += self.(weapon_load[WEP_CRYLINK]) >= autocvar_g_balance_crylink_primary_ammo;
+                       return ammo_amount;
+               }
+               case WR_CHECKAMMO2:
+               {
+                       // don't "run out of ammo" and switch weapons while waiting for release
+                       if(self.crylink_lastgroup && self.crylink_waitrelease)
+                               return TRUE;
+
+                       ammo_amount = self.ammo_cells >= autocvar_g_balance_crylink_secondary_ammo;
+                       ammo_amount += self.(weapon_load[WEP_CRYLINK]) >= autocvar_g_balance_crylink_secondary_ammo;
+                       return ammo_amount;
+               }
+               case WR_CONFIG:
+               {
+                       WEP_CONFIG_SETTINGS(CRYLINK_SETTINGS(crylink))
+                       return TRUE;
+               }
+               case WR_RELOAD:
+               {
+                       W_Reload(min(autocvar_g_balance_crylink_primary_ammo, autocvar_g_balance_crylink_secondary_ammo), "weapons/reload.wav");
+                       return TRUE;
+               }
+               case WR_SUICIDEMESSAGE:
+               {
+                       return WEAPON_CRYLINK_SUICIDE;
+               }
+               case WR_KILLMESSAGE:
+               {
+                       return WEAPON_CRYLINK_MURDER;
+               }
+       }
+       return TRUE;
+}
+#endif
+#ifdef CSQC
+float w_crylink(float req)
+{
+       switch(req)
+       {
+               case WR_IMPACTEFFECT:
+               {
+                       vector org2;
+                       org2 = w_org + w_backoff * 2;
+                       if(w_deathtype & HITTYPE_SECONDARY)
+                       {
+                               pointparticles(particleeffectnum("crylink_impact"), org2, '0 0 0', 1);
+                               if(!w_issilent)
+                                       sound(self, CH_SHOTS, "weapons/crylink_impact2.wav", VOL_BASE, ATTN_NORM);
+                       }
+                       else
+                       {
+                               pointparticles(particleeffectnum("crylink_impactbig"), org2, '0 0 0', 1);
+                               if(!w_issilent)
+                                       sound(self, CH_SHOTS, "weapons/crylink_impact.wav", VOL_BASE, ATTN_NORM);
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_sound("weapons/crylink_impact2.wav");
+                       precache_sound("weapons/crylink_impact.wav");
+                       return TRUE;
+               }
+       }
+       return TRUE;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/w_devastator.qc b/qcsrc/common/weapons/w_devastator.qc
new file mode 100644 (file)
index 0000000..b764d94
--- /dev/null
@@ -0,0 +1,551 @@
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id */ DEVASTATOR,
+/* function */ W_Devastator,
+/* ammotype */ IT_ROCKETS,
+/* impulse  */ 9,
+/* flags    */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
+/* rating   */ BOT_PICKUP_RATING_HIGH,
+/* model    */ "rl",
+/* netname  */ "devastator",
+/* fullname */ _("Devastator")
+);
+
+#define DEVASTATOR_SETTINGS(weapon) \
+       WEP_ADD_CVAR(weapon, MO_NONE, ammo) \
+       WEP_ADD_CVAR(weapon, MO_NONE, animtime) \
+       WEP_ADD_CVAR(weapon, MO_NONE, damage) \
+       WEP_ADD_CVAR(weapon, MO_NONE, damageforcescale) \
+       WEP_ADD_CVAR(weapon, MO_NONE, detonatedelay) \
+       WEP_ADD_CVAR(weapon, MO_NONE, edgedamage) \
+       WEP_ADD_CVAR(weapon, MO_NONE, force) \
+       WEP_ADD_CVAR(weapon, MO_NONE, guidedelay) \
+       WEP_ADD_CVAR(weapon, MO_NONE, guidegoal) \
+       WEP_ADD_CVAR(weapon, MO_NONE, guiderate) \
+       WEP_ADD_CVAR(weapon, MO_NONE, guideratedelay) \
+       WEP_ADD_CVAR(weapon, MO_NONE, guidestop) \
+       WEP_ADD_CVAR(weapon, MO_NONE, health) \
+       WEP_ADD_CVAR(weapon, MO_NONE, lifetime) \
+       WEP_ADD_CVAR(weapon, MO_NONE, radius) \
+       WEP_ADD_CVAR(weapon, MO_NONE, refire) \
+       WEP_ADD_CVAR(weapon, MO_NONE, remote_damage) \
+       WEP_ADD_CVAR(weapon, MO_NONE, remote_edgedamage) \
+       WEP_ADD_CVAR(weapon, MO_NONE, remote_force) \
+       WEP_ADD_CVAR(weapon, MO_NONE, remote_radius) \
+       WEP_ADD_CVAR(weapon, MO_NONE, speed) \
+       WEP_ADD_CVAR(weapon, MO_NONE, speedaccel) \
+       WEP_ADD_CVAR(weapon, MO_NONE, speedstart) \
+       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
+DEVASTATOR_SETTINGS(devastator)
+.float rl_release;
+.float rl_detonate_later;
+#endif
+#else
+#ifdef SVQC
+void spawnfunc_weapon_devastator() { weapon_defaultspawnfunc(WEP_DEVASTATOR); }
+void spawnfunc_weapon_rocketlauncher() { spawnfunc_weapon_devastator(); }
+
+void W_Devastator_Unregister()
+{
+       if(self.realowner && self.realowner.lastrocket == self)
+       {
+               self.realowner.lastrocket = world;
+               // self.realowner.rl_release = 1;
+       }
+}
+
+void W_Devastator_Explode()
+{
+       W_Devastator_Unregister();
+
+       if(other.takedamage == DAMAGE_AIM)
+               if(IS_PLAYER(other))
+                       if(DIFF_TEAM(self.realowner, other))
+                               if(other.deadflag == DEAD_NO)
+                                       if(IsFlying(other))
+                                               Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT);
+
+       self.event_damage = func_null;
+       self.takedamage = DAMAGE_NO;
+
+       RadiusDamage (self, self.realowner, WEP_CVAR(devastator, damage), WEP_CVAR(devastator, edgedamage), WEP_CVAR(devastator, radius), world, world, WEP_CVAR(devastator, force), self.projectiledeathtype, other);
+
+       if (self.realowner.weapon == WEP_DEVASTATOR)
+       {
+               if(self.realowner.ammo_rockets < WEP_CVAR(devastator, ammo))
+               {
+                       self.realowner.cnt = WEP_DEVASTATOR;
+                       ATTACK_FINISHED(self.realowner) = time;
+                       self.realowner.switchweapon = w_getbestweapon(self.realowner);
+               }
+       }
+       remove (self);
+}
+
+void W_Devastator_DoRemoteExplode()
+{
+       W_Devastator_Unregister();
+
+       self.event_damage = func_null;
+       self.takedamage = DAMAGE_NO;
+
+       RadiusDamage (self, self.realowner, WEP_CVAR(devastator, remote_damage), WEP_CVAR(devastator, remote_edgedamage), WEP_CVAR(devastator, remote_radius), world, world, WEP_CVAR(devastator, remote_force), self.projectiledeathtype | HITTYPE_BOUNCE, world);
+
+       if (self.realowner.weapon == WEP_DEVASTATOR)
+       {
+               if(self.realowner.ammo_rockets < WEP_CVAR(devastator, ammo))
+               {
+                       self.realowner.cnt = WEP_DEVASTATOR;
+                       ATTACK_FINISHED(self.realowner) = time;
+                       self.realowner.switchweapon = w_getbestweapon(self.realowner);
+               }
+       }
+       remove (self);
+}
+
+void W_Devastator_RemoteExplode()
+{
+       if(self.realowner.deadflag == DEAD_NO)
+       if(self.realowner.lastrocket)
+       {
+               if((self.spawnshieldtime >= 0)
+                       ? (time >= self.spawnshieldtime) // timer
+                       : (vlen(NearestPointOnBox(self.realowner, self.origin) - self.origin) > WEP_CVAR(devastator, remote_radius)) // safety device
+               )
+               {
+                       W_Devastator_DoRemoteExplode();
+               }
+       }
+}
+
+vector W_Devastator_SteerTo(vector thisdir, vector goaldir, float maxturn_cos)
+{
+       if(thisdir * goaldir > maxturn_cos)
+               return goaldir;
+       if(thisdir * goaldir < -0.9998) // less than 1 degree and opposite
+               return thisdir; // refuse to guide (better than letting a numerical error happen)
+       float f, m2;
+       vector v;
+       // solve:
+       //   g = normalize(thisdir + goaldir * X)
+       //   thisdir * g = maxturn
+       //
+       //   gg = thisdir + goaldir * X
+       //   (thisdir * gg)^2 = maxturn^2 * (gg * gg)
+       //
+       //   (1 + (thisdir * goaldir) * X)^2 = maxturn^2 * (1 + X*X + 2 * X * thisdir * goaldir)
+       f = thisdir * goaldir;
+       //   (1 + f * X)^2 = maxturn^2 * (1 + X*X + 2 * X * f)
+       //   0 = (m^2 - f^2) * x^2 + (2 * f * (m^2 - 1)) * x + (m^2 - 1)
+       m2 = maxturn_cos * maxturn_cos;
+       v = solve_quadratic(m2 - f * f, 2 * f * (m2 - 1), m2 - 1);
+       return normalize(thisdir + goaldir * v_y); // the larger solution!
+}
+// assume thisdir == -goaldir:
+//   f == -1
+//   v = solve_qadratic(m2 - 1, -2 * (m2 - 1), m2 - 1)
+//   (m2 - 1) x^2 - 2 * (m2 - 1) * x + (m2 - 1) = 0
+//   x^2 - 2 * x + 1 = 0
+//   (x - 1)^2 = 0
+//   x = 1
+//   normalize(thisdir + goaldir)
+//   normalize(0)
+
+void W_Devastator_Think (void)
+{
+       vector desireddir, olddir, newdir, desiredorigin, goal;
+#if 0
+       float cosminang, cosmaxang, cosang;
+#endif
+       float velspeed, f;
+       self.nextthink = time;
+       if (time > self.cnt)
+       {
+               other = world;
+               self.projectiledeathtype |= HITTYPE_BOUNCE;
+               W_Devastator_Explode ();
+               return;
+       }
+
+       // accelerate
+       makevectors(self.angles_x * '-1 0 0' + self.angles_y * '0 1 0');
+       velspeed = WEP_CVAR(devastator, speed) * g_weaponspeedfactor - (self.velocity * v_forward);
+       if (velspeed > 0)
+               self.velocity = self.velocity + v_forward * min(WEP_CVAR(devastator, speedaccel) * g_weaponspeedfactor * frametime, velspeed);
+
+       // laser guided, or remote detonation
+       if (self.realowner.weapon == WEP_DEVASTATOR)
+       {
+               if(self == self.realowner.lastrocket)
+               if not(self.realowner.rl_release)
+               if not(self.BUTTON_ATCK2)
+               if(WEP_CVAR(devastator, guiderate))
+               if(time > self.pushltime)
+               if(self.realowner.deadflag == DEAD_NO)
+               {
+                       f = WEP_CVAR(devastator, guideratedelay);
+                       if(f)
+                               f = bound(0, (time - self.pushltime) / f, 1);
+                       else
+                               f = 1;
+
+                       velspeed = vlen(self.velocity);
+
+                       makevectors(self.realowner.v_angle);
+                       desireddir = WarpZone_RefSys_TransformVelocity(self.realowner, self, v_forward);
+                       desiredorigin = WarpZone_RefSys_TransformOrigin(self.realowner, self, self.realowner.origin + self.realowner.view_ofs);
+                       olddir = normalize(self.velocity);
+
+                       // now it gets tricky... we want to move like some curve to approximate the target direction
+                       // but we are limiting the rate at which we can turn!
+                       goal = desiredorigin + ((self.origin - desiredorigin) * desireddir + WEP_CVAR(devastator, guidegoal)) * desireddir;
+                       newdir = W_Devastator_SteerTo(olddir, normalize(goal - self.origin), cos(WEP_CVAR(devastator, guiderate) * f * frametime * DEG2RAD));
+
+                       self.velocity = newdir * velspeed;
+                       self.angles = vectoangles(self.velocity);
+
+                       if(!self.count)
+                       {
+                               pointparticles(particleeffectnum("rocket_guide"), self.origin, self.velocity, 1);
+                               // TODO add a better sound here
+                               sound (self.realowner, CH_WEAPON_B, "weapons/rocket_mode.wav", VOL_BASE, ATTN_NORM);
+                               self.count = 1;
+                       }
+               }
+
+               if(self.rl_detonate_later)
+                       W_Devastator_RemoteExplode();
+       }
+
+       if(self.csqcprojectile_clientanimate == 0)
+               UpdateCSQCProjectile(self);
+}
+
+void W_Devastator_Touch (void)
+{
+       if(WarpZone_Projectile_Touch())
+       {
+               if(wasfreed(self))
+                       W_Devastator_Unregister();
+               return;
+       }
+       W_Devastator_Unregister();
+       W_Devastator_Explode ();
+}
+
+void W_Devastator_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+       if (self.health <= 0)
+               return;
+       
+       if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
+               return; // g_projectiles_damage says to halt
+               
+       self.health = self.health - damage;
+       self.angles = vectoangles(self.velocity);
+       
+       if (self.health <= 0)
+               W_PrepareExplosionByDamage(attacker, W_Devastator_Explode);
+}
+
+void W_Devastator_Attack (void)
+{
+       entity missile;
+       entity flash;
+
+       W_DecreaseAmmo(ammo_rockets, WEP_CVAR(devastator, ammo), WEP_CVAR(devastator, reload_ammo));
+
+       W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', FALSE, 5, "weapons/rocket_fire.wav", CH_WEAPON_A, WEP_CVAR(devastator, damage));
+       pointparticles(particleeffectnum("rocketlauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+       missile = WarpZone_RefSys_SpawnSameRefSys(self);
+       missile.owner = missile.realowner = self;
+       self.lastrocket = missile;
+       if(WEP_CVAR(devastator, detonatedelay) >= 0)
+               missile.spawnshieldtime = time + WEP_CVAR(devastator, detonatedelay);
+       else
+               missile.spawnshieldtime = -1;
+       missile.pushltime = time + WEP_CVAR(devastator, guidedelay);
+       missile.classname = "rocket";
+       missile.bot_dodge = TRUE;
+       missile.bot_dodgerating = WEP_CVAR(devastator, damage) * 2; // * 2 because it can be detonated inflight which makes it even more dangerous
+
+       missile.takedamage = DAMAGE_YES;
+       missile.damageforcescale = WEP_CVAR(devastator, damageforcescale);
+       missile.health = WEP_CVAR(devastator, health);
+       missile.event_damage = W_Devastator_Damage;
+       missile.damagedbycontents = TRUE;
+
+       missile.movetype = MOVETYPE_FLY;
+       PROJECTILE_MAKETRIGGER(missile);
+       missile.projectiledeathtype = WEP_DEVASTATOR;
+       setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot
+
+       setorigin (missile, w_shotorg - v_forward * 3); // move it back so it hits the wall at the right point
+       W_SetupProjectileVelocity(missile, WEP_CVAR(devastator, speedstart), 0);
+       missile.angles = vectoangles (missile.velocity);
+
+       missile.touch = W_Devastator_Touch;
+       missile.think = W_Devastator_Think;
+       missile.nextthink = time;
+       missile.cnt = time + WEP_CVAR(devastator, lifetime);
+       missile.flags = FL_PROJECTILE;
+       missile.missile_flags = MIF_SPLASH; 
+
+       CSQCProjectile(missile, WEP_CVAR(devastator, guiderate) == 0 && WEP_CVAR(devastator, speedaccel) == 0, PROJECTILE_ROCKET, FALSE); // because of fly sound
+
+       // muzzle flash for 1st person view
+       flash = spawn ();
+       setmodel (flash, "models/flash.md3"); // precision set below
+       SUB_SetFade (flash, time, 0.1);
+       flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
+       W_AttachToShotorg(flash, '5 0 0');
+
+       // common properties
+       other = missile; MUTATOR_CALLHOOK(EditProjectile);
+}
+
+float W_Devastator(float req)
+{
+       entity rock;
+       float rockfound;
+       float ammo_amount;
+       switch(req)
+       {
+               case WR_AIM: // WEAPONTODO: rewrite this, it's WAY too complicated for what it should be
+               {
+                       // aim and decide to fire if appropriate
+                       self.BUTTON_ATCK = bot_aim(WEP_CVAR(devastator, speed), 0, WEP_CVAR(devastator, lifetime), FALSE);
+                       if(skill >= 2) // skill 0 and 1 bots won't detonate rockets!
+                       {
+                               // decide whether to detonate rockets
+                               entity missile, targetlist, targ;
+                               float edgedamage, coredamage, edgeradius, recipricoledgeradius, d;
+                               float selfdamage, teamdamage, enemydamage;
+                               edgedamage = WEP_CVAR(devastator, edgedamage);
+                               coredamage = WEP_CVAR(devastator, damage);
+                               edgeradius = WEP_CVAR(devastator, radius);
+                               recipricoledgeradius = 1 / edgeradius;
+                               selfdamage = 0;
+                               teamdamage = 0;
+                               enemydamage = 0;
+                               targetlist = findchainfloat(bot_attack, TRUE);
+                               missile = find(world, classname, "rocket");
+                               while (missile)
+                               {
+                                       if (missile.realowner != self)
+                                       {
+                                               missile = find(missile, classname, "rocket");
+                                               continue;
+                                       }
+                                       targ = targetlist;
+                                       while (targ)
+                                       {
+                                               d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - missile.origin);
+                                               d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000);
+                                               // count potential damage according to type of target
+                                               if (targ == self)
+                                                       selfdamage = selfdamage + d;
+                                               else if (targ.team == self.team && teamplay)
+                                                       teamdamage = teamdamage + d;
+                                               else if (bot_shouldattack(targ))
+                                                       enemydamage = enemydamage + d;
+                                               targ = targ.chain;
+                                       }
+                                       missile = find(missile, classname, "rocket");
+                               }
+                               float desirabledamage;
+                               desirabledamage = enemydamage;
+                               if (time > self.invincible_finished && time > self.spawnshieldtime)
+                                       desirabledamage = desirabledamage - selfdamage * autocvar_g_balance_selfdamagepercent;
+                               if (teamplay && self.team)
+                                       desirabledamage = desirabledamage - teamdamage;
+
+                               missile = find(world, classname, "rocket");
+                               while (missile)
+                               {
+                                       if (missile.realowner != self)
+                                       {
+                                               missile = find(missile, classname, "rocket");
+                                               continue;
+                                       }
+                                       makevectors(missile.v_angle);
+                                       targ = targetlist;
+                                       if (skill > 9) // normal players only do this for the target they are tracking
+                                       {
+                                               targ = targetlist;
+                                               while (targ)
+                                               {
+                                                       if (
+                                                               (v_forward * normalize(missile.origin - targ.origin)< 0.1)
+                                                               && desirabledamage > 0.1*coredamage
+                                                       )self.BUTTON_ATCK2 = TRUE;
+                                                       targ = targ.chain;
+                                               }
+                                       }else{
+                                               float distance; distance= bound(300,vlen(self.origin-self.enemy.origin),30000);
+                                               //As the distance gets larger, a correct detonation gets near imposible
+                                               //Bots are assumed to use the rocket spawnfunc_light to see if the rocket gets near a player
+                                               if(v_forward * normalize(missile.origin - self.enemy.origin)< 0.1)
+                                                       if(IS_PLAYER(self.enemy))
+                                                               if(desirabledamage >= 0.1*coredamage)
+                                                                       if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1))
+                                                                               self.BUTTON_ATCK2 = TRUE;
+                                       //      dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n");
+                                       }
+
+                                       missile = find(missile, classname, "rocket");
+                               }
+                               // if we would be doing at X percent of the core damage, detonate it
+                               // but don't fire a new shot at the same time!
+                               if (desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events
+                                       self.BUTTON_ATCK2 = TRUE;
+                               if ((skill > 6.5) && (selfdamage > self.health))
+                                       self.BUTTON_ATCK2 = FALSE;
+                               //if(self.BUTTON_ATCK2 == TRUE)
+                               //      dprint(ftos(desirabledamage),"\n");
+                               if (self.BUTTON_ATCK2 == TRUE) self.BUTTON_ATCK = FALSE;
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_THINK:
+               {
+                       if(WEP_CVAR(devastator, reload_ammo) && self.clip_load < WEP_CVAR(devastator, ammo)) // forced reload
+                               WEP_ACTION(self.weapon, WR_RELOAD);
+                       else
+                       {
+                               if (self.BUTTON_ATCK)
+                               {
+                                       if(self.rl_release || WEP_CVAR(devastator, guidestop))
+                                       if(weapon_prepareattack(0, WEP_CVAR(devastator, refire)))
+                                       {
+                                               W_Devastator_Attack();
+                                               weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(devastator, animtime), w_ready);
+                                               self.rl_release = 0;
+                                       }
+                               }
+                               else
+                                       self.rl_release = 1;
+
+                               if (self.BUTTON_ATCK2)
+                               {
+                                       rockfound = 0;
+                                       for(rock = world; (rock = find(rock, classname, "rocket")); ) if(rock.realowner == self)
+                                       {
+                                               if(!rock.rl_detonate_later)
+                                               {
+                                                       rock.rl_detonate_later = TRUE;
+                                                       rockfound = 1;
+                                               }
+                                       }
+                                       if(rockfound)
+                                               sound (self, CH_WEAPON_B, "weapons/rocket_det.wav", VOL_BASE, ATTN_NORM);
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       if(autocvar_sv_precacheweapons)
+                       {
+                               precache_model("models/flash.md3");
+                               precache_model("models/weapons/g_rl.md3");
+                               precache_model("models/weapons/v_rl.md3");
+                               precache_model("models/weapons/h_rl.iqm");
+                               precache_sound("weapons/rocket_det.wav");
+                               precache_sound("weapons/rocket_fire.wav");
+                               precache_sound("weapons/rocket_mode.wav");
+                       }
+                       WEP_SET_PROPS(DEVASTATOR_SETTINGS(devastator), WEP_DEVASTATOR)
+                       return TRUE;
+               }
+               case WR_SETUP:
+               {
+                       self.current_ammo = ammo_rockets;
+                       self.rl_release = 1;
+                       return TRUE;
+               }
+               case WR_CHECKAMMO1:
+               {
+                       // don't switch while guiding a missile
+                       if (ATTACK_FINISHED(self) <= time || self.weapon != WEP_DEVASTATOR)
+                       {
+                               ammo_amount = FALSE;
+                               if(WEP_CVAR(devastator, reload_ammo))
+                               {
+                                       if(self.ammo_rockets < WEP_CVAR(devastator, ammo) && self.(weapon_load[WEP_DEVASTATOR]) < WEP_CVAR(devastator, ammo))
+                                               ammo_amount = TRUE;
+                               }
+                               else if(self.ammo_rockets < WEP_CVAR(devastator, ammo))
+                                       ammo_amount = TRUE;
+                               return !ammo_amount;
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_CHECKAMMO2:
+               {
+                       return FALSE;
+               }
+               case WR_CONFIG:
+               {
+                       WEP_CONFIG_SETTINGS(DEVASTATOR_SETTINGS(devastator))
+                       return TRUE;
+               }
+               case WR_RESETPLAYER:
+               {
+                       self.rl_release = 0;
+                       return TRUE;
+               }
+               case WR_RELOAD:
+               {
+                       W_Reload(WEP_CVAR(devastator, ammo), "weapons/reload.wav");
+                       return TRUE;
+               }
+               case WR_SUICIDEMESSAGE:
+               {
+                       return WEAPON_ROCKETLAUNCHER_SUICIDE;
+               }
+               case WR_KILLMESSAGE:
+               {
+                       if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH))
+                               return WEAPON_ROCKETLAUNCHER_MURDER_SPLASH;
+                       else
+                               return WEAPON_ROCKETLAUNCHER_MURDER_DIRECT;
+               }
+       }
+       return TRUE;
+}
+#endif
+#ifdef CSQC
+float W_Devastator(float req)
+{
+       switch(req)
+       {
+               case WR_IMPACTEFFECT:
+               {
+                       vector org2;
+                       org2 = w_org + w_backoff * 12;
+                       pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1);
+                       if(!w_issilent)
+                               sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
+                               
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_sound("weapons/rocket_impact.wav");
+                       return TRUE;
+               }
+       }
+       return TRUE;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/w_electro.qc b/qcsrc/common/weapons/w_electro.qc
new file mode 100644 (file)
index 0000000..75ddd79
--- /dev/null
@@ -0,0 +1,644 @@
+#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];
+var float autocvar_g_balance_electro_combo_comboradius_thruwall = 200;
+#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, !autocvar_g_balance_electro_combo_comboradius_thruwall);
+       while (e)
+       {
+               if (e.classname == "plasma")
+               {
+                       // change owner to whoever caused the combo explosion
+                       WarpZone_TraceLine(org, e.origin, MOVE_NOMONSTERS, e);
+
+                       if((trace_fraction == 1) || (autocvar_g_balance_electro_combo_comboradius_thruwall > e.WarpZone_findradius_dist))
+                       {
+                               e.realowner = own;
+                               e.takedamage = DAMAGE_NO;
+                               e.classname = "plasma_chain";
+                               e.think = W_Plasma_Explode_Combo;
+                               e.nextthink = time + vlen(e.WarpZone_findradius_dist) / autocvar_g_balance_electro_combo_speed; // delay combo chains, looks cooler
+                       }
+               }
+               e = e.chain;
+       }
+}
+
+void W_Plasma_Explode (void)
+{
+       if(other.takedamage == DAMAGE_AIM)
+               if(IS_PLAYER(other))
+                       if(DIFF_TEAM(self.realowner, other))
+                               if(other.deadflag == DEAD_NO)
+                                       if(IsFlying(other))
+                                               Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_ELECTROBITCH);
+
+       self.event_damage = func_null;
+       self.takedamage = DAMAGE_NO;
+       if (self.movetype == MOVETYPE_BOUNCE)
+       {
+               RadiusDamage (self, self.realowner, autocvar_g_balance_electro_secondary_damage, autocvar_g_balance_electro_secondary_edgedamage, autocvar_g_balance_electro_secondary_radius, world, 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);
+               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 (!(owner_player.items & IT_UNLIMITED_WEAPON_AMMO))
+       {
+               if(autocvar_g_balance_electro_primary_ammo)
+               {
+                       if(autocvar_g_balance_electro_reload_ammo)
+                       {
+                               dt = min(dt, owner_player.clip_load / autocvar_g_balance_electro_primary_ammo);
+                               owner_player.clip_load = max(0, owner_player.clip_load - autocvar_g_balance_electro_primary_ammo * frametime);
+                               owner_player.(weapon_load[WEP_ELECTRO]) = owner_player.clip_load;
+                       }
+                       else
+                       {
+                               dt = min(dt, owner_player.ammo_cells / autocvar_g_balance_electro_primary_ammo);
+                               owner_player.ammo_cells = max(0, owner_player.ammo_cells - autocvar_g_balance_electro_primary_ammo * frametime);
+                       }
+               }
+       }
+
+       W_SetupShot_Range(owner_player, TRUE, 0, "", 0, autocvar_g_balance_electro_primary_damage * dt, autocvar_g_balance_electro_primary_range);
+       if(!lgbeam_owner_ent)
+       {
+               lgbeam_owner_ent = spawn();
+               lgbeam_owner_ent.classname = "lgbeam_owner_ent";
+       }
+       WarpZone_traceline_antilag(lgbeam_owner_ent, w_shotorg, w_shotend, MOVE_NORMAL, lgbeam_owner_ent, ANTILAG_LATENCY(owner_player));
+
+       // apply the damage
+       if(trace_ent)
+       {
+               vector force;
+               force = w_shotdir * autocvar_g_balance_electro_primary_force + '0 0 1' * autocvar_g_balance_electro_primary_force_up;
+
+               f = ExponentialFalloff(autocvar_g_balance_electro_primary_falloff_mindist, autocvar_g_balance_electro_primary_falloff_maxdist, autocvar_g_balance_electro_primary_falloff_halflifedist, vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos) - w_shotorg));
+
+               if(accuracy_isgooddamage(owner_player, trace_ent))
+                       accuracy_add(owner_player, WEP_ELECTRO, 0, autocvar_g_balance_electro_primary_damage * dt * f);
+               Damage (trace_ent, owner_player, owner_player, autocvar_g_balance_electro_primary_damage * dt * f, WEP_ELECTRO, trace_endpos, force * dt);
+       }
+       W_Plasma_TriggerCombo(trace_endpos, autocvar_g_balance_electro_primary_comboradius, owner_player);
+
+       // draw effect
+       if(w_shotorg != self.hook_start)
+       {
+               self.SendFlags |= 2;
+               self.hook_start = w_shotorg;
+       }
+       if(w_shotend != self.hook_end)
+       {
+               self.SendFlags |= 4;
+               self.hook_end = w_shotend;
+       }
+}
+
+// experimental lightning gun
+void W_Electro_Attack3 (void)
+{
+       // only play fire sound if 0.5 sec has passed since player let go the fire button
+       if(time - self.prevlgfire > 0.5)
+               sound (self, CH_WEAPON_A, "weapons/lgbeam_fire.wav", VOL_BASE, ATTEN_NORM);
+
+       entity beam, oldself;
+
+       self.lgbeam = beam = spawn();
+       beam.classname = "lgbeam";
+       beam.solid = SOLID_NOT;
+       beam.think = lgbeam_think;
+       beam.owner = beam.realowner = self;
+       beam.movetype = MOVETYPE_NONE;
+       beam.shot_spread = 0;
+       beam.bot_dodge = TRUE;
+       beam.bot_dodgerating = autocvar_g_balance_electro_primary_damage;
+       Net_LinkEntity(beam, FALSE, 0, lgbeam_send);
+
+       oldself = self;
+       self = beam;
+       self.think();
+       self = oldself;
+}
+
+void ElectroInit()
+{
+       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.wav", VOL_BASE, ATTEN_NORM);
+                       }
+                       else
+                       {
+                               if(w_deathtype & HITTYPE_BOUNCE)
+                               {
+                                       // this is sent as "primary (w_deathtype & HITTYPE_BOUNCE)" to distinguish it from (w_deathtype & HITTYPE_SECONDARY) bounced balls
+                                       pointparticles(particleeffectnum("electro_combo"), org2, '0 0 0', 1);
+                                       if(!w_issilent)
+                                               sound(self, CH_SHOTS, "weapons/electro_impact_combo.wav", VOL_BASE, ATTEN_NORM);
+                               }
+                               else
+                               {
+                                       pointparticles(particleeffectnum("electro_impact"), org2, '0 0 0', 1);
+                                       if(!w_issilent)
+                                               sound(self, CH_SHOTS, "weapons/electro_impact.wav", VOL_BASE, ATTEN_NORM);
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_sound("weapons/electro_impact.wav");
+                       precache_sound("weapons/electro_impact_combo.wav");
+                       return TRUE;
+               }
+       }
+       return TRUE;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/w_fireball.qc b/qcsrc/common/weapons/w_fireball.qc
new file mode 100644 (file)
index 0000000..ebdfa2b
--- /dev/null
@@ -0,0 +1,471 @@
+#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 || DIFF_TEAM(e, self))
+               {
+                       // can we see fireball?
+                       traceline(e.origin + e.view_ofs, self.origin, MOVE_NORMAL, e);
+                       if(/* trace_startsolid || */ trace_fraction != 1) // startsolid should be never happening anyway
+                               continue;
+                       // can we see player who shot fireball?
+                       traceline(e.origin + e.view_ofs, self.realowner.origin + self.realowner.view_ofs, MOVE_NORMAL, e);
+                       if(trace_ent != self.realowner)
+                       if(/* trace_startsolid || */ trace_fraction != 1)
+                               continue;
+                       dist = vlen(self.origin - e.origin - e.view_ofs);
+                       points = (1 - sqrt(dist / WEP_CVAR_PRI(fireball, bfgradius)));
+                       if(points <= 0)
+                               continue;
+                       dir = normalize(e.origin + e.view_ofs - self.origin);
+
+                       if(accuracy_isgooddamage(self.realowner, e))
+                               accuracy_add(self.realowner, WEP_FIREBALL, 0, WEP_CVAR_PRI(fireball, bfgdamage) * points);
+
+                       Damage(e, self, self.realowner, WEP_CVAR_PRI(fireball, bfgdamage) * points, self.projectiledeathtype | HITTYPE_BOUNCE | HITTYPE_SPLASH, e.origin + e.view_ofs, WEP_CVAR_PRI(fireball, bfgforce) * dir);
+                       pointparticles(particleeffectnum("fireball_bfgdamage"), e.origin, -1 * dir, 1);
+               }
+       }
+
+       remove (self);
+}
+
+void W_Fireball_TouchExplode (void)
+{
+       PROJECTILE_TOUCH;
+       W_Fireball_Explode ();
+}
+
+void W_Fireball_LaserPlay(float dt, float dist, float damage, float edgedamage, float burntime)
+{
+       entity e;
+       float d;
+       vector p;
+
+       if(damage <= 0)
+               return;
+
+       RandomSelection_Init();
+       for(e = WarpZone_FindRadius(self.origin, dist, TRUE); e; e = e.chain)
+       if(e != self.realowner) if(e.takedamage == DAMAGE_AIM) if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self))
+       {
+               p = e.origin;
+               p_x += e.mins_x + random() * (e.maxs_x - e.mins_x);
+               p_y += e.mins_y + random() * (e.maxs_y - e.mins_y);
+               p_z += e.mins_z + random() * (e.maxs_z - e.mins_z);
+               d = vlen(WarpZone_UnTransformOrigin(e, self.origin) - p);
+               if(d < dist)
+               {
+                       e.fireball_impactvec = p;
+                       RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e));
+               }
+       }
+       if(RandomSelection_chosen_ent)
+       {
+               d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec);
+               d = damage + (edgedamage - damage) * (d / dist);
+               Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE);
+               //trailparticles(self, particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec);
+               pointparticles(particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1);
+       }
+}
+
+void W_Fireball_Think()
+{
+       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_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
diff --git a/qcsrc/common/weapons/w_hagar.qc b/qcsrc/common/weapons/w_hagar.qc
new file mode 100644 (file)
index 0000000..7e08c1a
--- /dev/null
@@ -0,0 +1,546 @@
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id */ HAGAR,
+/* function */ w_hagar,
+/* ammotype */ IT_ROCKETS,
+/* impulse  */ 8,
+/* flags    */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
+/* rating   */ BOT_PICKUP_RATING_MID,
+/* model    */ "hagar",
+/* netname  */ "hagar",
+/* fullname */ _("Hagar")
+);
+
+#define HAGAR_SETTINGS(weapon) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, ammo) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, damage) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, edgedamage) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, force) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, radius) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, refire) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, speed) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, damageforcescale) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, health) \
+       WEP_ADD_CVAR(weapon, MO_PRI,  lifetime) \
+       WEP_ADD_CVAR(weapon, MO_NONE, secondary) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  spread) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  load) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  load_max) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  load_abort) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  load_animtime) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  load_hold) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  load_speed) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  load_releasedeath) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  load_spread) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  load_spread_bias) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  load_linkexplode) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  lifetime_min) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  lifetime_rand) \
+       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
+HAGAR_SETTINGS(hagar)
+#endif
+#else
+#ifdef SVQC
+void spawnfunc_weapon_hagar() { weapon_defaultspawnfunc(WEP_HAGAR); }
+
+// NO bounce protection, as bounces are limited!
+
+void W_Hagar_Explode (void)
+{
+       self.event_damage = func_null;
+       RadiusDamage (self, self.realowner, WEP_CVAR_PRI(hagar, damage), WEP_CVAR_PRI(hagar, edgedamage), WEP_CVAR_PRI(hagar, radius), world, world, WEP_CVAR_PRI(hagar, force), self.projectiledeathtype, other);
+
+       remove (self);
+}
+
+void W_Hagar_Explode2 (void)
+{
+       self.event_damage = func_null;
+       RadiusDamage (self, self.realowner, WEP_CVAR_SEC(hagar, damage), WEP_CVAR_SEC(hagar, edgedamage), WEP_CVAR_SEC(hagar, radius), world, world, WEP_CVAR_SEC(hagar, force), self.projectiledeathtype, other);
+
+       remove (self);
+}
+
+void W_Hagar_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+       if (self.health <= 0)
+               return;
+               
+       float is_linkexplode = ( ((inflictor.owner != world) ? (inflictor.owner == self.owner) : TRUE)
+               && (inflictor.projectiledeathtype & HITTYPE_SECONDARY) 
+               && (self.projectiledeathtype & HITTYPE_SECONDARY));
+       
+       if(is_linkexplode)
+               is_linkexplode = (is_linkexplode && WEP_CVAR_SEC(hagar, load_linkexplode));
+       else
+               is_linkexplode = -1; // not secondary load, so continue as normal without exception.
+
+       if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, is_linkexplode))
+               return; // g_projectiles_damage says to halt
+
+       self.health = self.health - damage;
+       self.angles = vectoangles(self.velocity);
+       
+       if (self.health <= 0)
+               W_PrepareExplosionByDamage(attacker, self.think);
+}
+
+void W_Hagar_Touch (void)
+{
+       PROJECTILE_TOUCH;
+       self.use ();
+}
+
+void W_Hagar_Touch2 (void)
+{
+       PROJECTILE_TOUCH;
+
+       if(self.cnt > 0 || other.takedamage == DAMAGE_AIM) {
+               self.use();
+       } else {
+               self.cnt++;
+               pointparticles(particleeffectnum("hagar_bounce"), self.origin, self.velocity, 1);
+               self.angles = vectoangles (self.velocity);
+               self.owner = world;
+               self.projectiledeathtype |= HITTYPE_BOUNCE;
+       }
+}
+
+void W_Hagar_Attack (void)
+{
+       entity missile;
+
+       W_DecreaseAmmo(ammo_rockets, WEP_CVAR_PRI(hagar, ammo), autocvar_g_balance_hagar_reload_ammo);
+
+       W_SetupShot (self, FALSE, 2, "weapons/hagar_fire.wav", CH_WEAPON_A, WEP_CVAR_PRI(hagar, damage));
+
+       pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+       missile = spawn ();
+       missile.owner = missile.realowner = self;
+       missile.classname = "missile";
+       missile.bot_dodge = TRUE;
+       missile.bot_dodgerating = WEP_CVAR_PRI(hagar, damage);
+       
+       missile.takedamage = DAMAGE_YES;
+       missile.health = WEP_CVAR_PRI(hagar, health);
+       missile.damageforcescale = WEP_CVAR_PRI(hagar, damageforcescale);
+       missile.event_damage = W_Hagar_Damage;
+       missile.damagedbycontents = TRUE;
+       
+       missile.touch = W_Hagar_Touch;
+       missile.use = W_Hagar_Explode;
+       missile.think = adaptor_think2use_hittype_splash;
+       missile.nextthink = time + WEP_CVAR_PRI(hagar, lifetime);
+       PROJECTILE_MAKETRIGGER(missile);
+       missile.projectiledeathtype = WEP_HAGAR;
+       setorigin (missile, w_shotorg);
+       setsize(missile, '0 0 0', '0 0 0');
+
+       missile.movetype = MOVETYPE_FLY;
+       W_SETUPPROJECTILEVELOCITY(missile, g_balance_hagar_primary);
+
+       missile.angles = vectoangles (missile.velocity);
+       missile.flags = FL_PROJECTILE;
+       missile.missile_flags = MIF_SPLASH; 
+
+       CSQCProjectile(missile, TRUE, PROJECTILE_HAGAR, TRUE);
+
+       other = missile; MUTATOR_CALLHOOK(EditProjectile);
+}
+
+void W_Hagar_Attack2 (void)
+{
+       entity missile;
+
+       W_DecreaseAmmo(ammo_rockets, WEP_CVAR_SEC(hagar, ammo), autocvar_g_balance_hagar_reload_ammo);
+
+       W_SetupShot (self, FALSE, 2, "weapons/hagar_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage));
+
+       pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+       missile = spawn ();
+       missile.owner = missile.realowner = self;
+       missile.classname = "missile";
+       missile.bot_dodge = TRUE;
+       missile.bot_dodgerating = WEP_CVAR_SEC(hagar, damage);
+       
+       missile.takedamage = DAMAGE_YES;
+       missile.health = WEP_CVAR_SEC(hagar, health);
+       missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale);
+       missile.event_damage = W_Hagar_Damage;
+       missile.damagedbycontents = TRUE;
+
+       missile.touch = W_Hagar_Touch2;
+       missile.cnt = 0;
+       missile.use = W_Hagar_Explode2;
+       missile.think = adaptor_think2use_hittype_splash;
+       missile.nextthink = time + WEP_CVAR_SEC(hagar, lifetime_min) + random() * WEP_CVAR_SEC(hagar, lifetime_rand);
+       PROJECTILE_MAKETRIGGER(missile);
+       missile.projectiledeathtype = WEP_HAGAR | HITTYPE_SECONDARY;
+       setorigin (missile, w_shotorg);
+       setsize(missile, '0 0 0', '0 0 0');
+
+       missile.movetype = MOVETYPE_BOUNCEMISSILE;
+       W_SETUPPROJECTILEVELOCITY(missile, g_balance_hagar_secondary);
+
+       missile.angles = vectoangles (missile.velocity);
+       missile.flags = FL_PROJECTILE;
+       missile.missile_flags = MIF_SPLASH; 
+
+       CSQCProjectile(missile, TRUE, PROJECTILE_HAGAR_BOUNCING, TRUE);
+
+       other = missile; MUTATOR_CALLHOOK(EditProjectile);
+}
+
+.float hagar_loadstep, hagar_loadblock, hagar_loadbeep, hagar_warning;
+void W_Hagar_Attack2_Load_Release (void)
+{
+       // time to release the rockets we've loaded
+
+       entity missile;
+       float counter, shots, spread_pershot;
+       vector s;
+       vector forward, right, up;
+
+       if(!self.hagar_load)
+               return;
+
+       weapon_prepareattack_do(1, WEP_CVAR_SEC(hagar, refire));
+
+       W_SetupShot (self, FALSE, 2, "weapons/hagar_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage));
+       pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+       forward = v_forward;
+       right = v_right;
+       up = v_up;
+
+       shots = self.hagar_load;
+       missile = world;
+       for(counter = 0; counter < shots; ++counter)
+       {
+               missile = spawn ();
+               missile.owner = missile.realowner = self;
+               missile.classname = "missile";
+               missile.bot_dodge = TRUE;
+               missile.bot_dodgerating = WEP_CVAR_SEC(hagar, damage);
+               
+               missile.takedamage = DAMAGE_YES;
+               missile.health = WEP_CVAR_SEC(hagar, health);
+               missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale);
+               missile.event_damage = W_Hagar_Damage;
+               missile.damagedbycontents = TRUE;
+
+               missile.touch = W_Hagar_Touch; // not bouncy
+               missile.use = W_Hagar_Explode2;
+               missile.think = adaptor_think2use_hittype_splash;
+               missile.nextthink = time + WEP_CVAR_SEC(hagar, lifetime_min) + random() * WEP_CVAR_SEC(hagar, lifetime_rand);
+               PROJECTILE_MAKETRIGGER(missile);
+               missile.projectiledeathtype = WEP_HAGAR | HITTYPE_SECONDARY;
+               setorigin (missile, w_shotorg);
+               setsize(missile, '0 0 0', '0 0 0');
+               missile.movetype = MOVETYPE_FLY;
+               missile.missile_flags = MIF_SPLASH; 
+               
+               // per-shot spread calculation: the more shots there are, the less spread is applied (based on the bias cvar)
+               spread_pershot = ((shots - 1) / (WEP_CVAR_SEC(hagar, load_max) - 1)); 
+               spread_pershot = (1 - (spread_pershot * WEP_CVAR_SEC(hagar, load_spread_bias)));
+               spread_pershot = (WEP_CVAR_SEC(hagar, spread) * spread_pershot * g_weaponspreadfactor);
+               
+               // pattern spread calculation
+               s = '0 0 0';
+               if (counter == 0)
+                       s = '0 0 0';
+               else
+               {
+                       makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1)));
+                       s_y = v_forward_x;
+                       s_z = v_forward_y;
+               }
+               s = s * WEP_CVAR_SEC(hagar, load_spread) * g_weaponspreadfactor;
+               
+               W_SetupProjectileVelocityEx(missile, w_shotdir + right * s_y + up * s_z, v_up, WEP_CVAR_SEC(hagar, speed), 0, 0, spread_pershot, FALSE);
+
+               missile.angles = vectoangles (missile.velocity);
+               missile.flags = FL_PROJECTILE;
+
+               CSQCProjectile(missile, TRUE, PROJECTILE_HAGAR, TRUE);
+
+               other = missile; MUTATOR_CALLHOOK(EditProjectile);
+       }
+
+       weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(hagar, load_animtime), w_ready);
+       self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, refire) * W_WeaponRateFactor();
+       self.hagar_load = 0;
+}
+
+void W_Hagar_Attack2_Load (void)
+{
+       // loadable hagar secondary attack, must always run each frame
+       
+       if(time < game_starttime)
+               return;
+
+       float loaded, enough_ammo;
+       loaded = self.hagar_load >= WEP_CVAR_SEC(hagar, load_max);
+
+       // this is different than WR_CHECKAMMO when it comes to reloading
+       if(autocvar_g_balance_hagar_reload_ammo)
+               enough_ammo = self.(weapon_load[WEP_HAGAR]) >= WEP_CVAR_SEC(hagar, ammo);
+       else
+               enough_ammo = self.ammo_rockets >= WEP_CVAR_SEC(hagar, ammo);
+
+       if(self.BUTTON_ATCK2)
+       {
+               if(self.BUTTON_ATCK && WEP_CVAR_SEC(hagar, load_abort))
+               {
+                       if(self.hagar_load)
+                       {
+                               // if we pressed primary fire while loading, unload all rockets and abort
+                               self.weaponentity.state = WS_READY;
+                               W_DecreaseAmmo(ammo_rockets, WEP_CVAR_SEC(hagar, ammo) * self.hagar_load * -1, autocvar_g_balance_hagar_reload_ammo); // give back ammo
+                               self.hagar_load = 0;
+                               sound(self, CH_WEAPON_A, "weapons/hagar_beep.wav", VOL_BASE, ATTN_NORM);
+
+                               // pause until we can load rockets again, once we re-press the alt fire button
+                               self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_speed) * W_WeaponRateFactor();
+
+                               // require letting go of the alt fire button before we can load again
+                               self.hagar_loadblock = TRUE;
+                       }
+               }
+               else
+               {
+                       // check if we can attempt to load another rocket
+                       if(!loaded && enough_ammo)
+                       {
+                               if(!self.hagar_loadblock && self.hagar_loadstep < time)
+                               {
+                                       W_DecreaseAmmo(ammo_rockets, WEP_CVAR_SEC(hagar, ammo), autocvar_g_balance_hagar_reload_ammo);
+                                       self.weaponentity.state = WS_INUSE;
+                                       self.hagar_load += 1;
+                                       sound(self, CH_WEAPON_B, "weapons/hagar_load.wav", VOL_BASE * 0.8, ATTN_NORM); // sound is too loud according to most
+
+                                       if (self.hagar_load >= WEP_CVAR_SEC(hagar, load_max))
+                                               self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_hold) * W_WeaponRateFactor();
+                                       else
+                                               self.hagar_loadstep = time + WEP_CVAR_SEC(hagar, load_speed) * W_WeaponRateFactor();
+                               }
+                       }
+                       else if(!self.hagar_loadbeep && self.hagar_load) // prevents the beep from playing each frame
+                       {
+                               // if this is the last rocket we can load, play a beep sound to notify the player
+                               sound(self, CH_WEAPON_A, "weapons/hagar_beep.wav", VOL_BASE, ATTN_NORM);
+                               self.hagar_loadbeep = TRUE;
+                       }
+               }
+       }
+       else if(self.hagar_loadblock)
+       {
+               // the alt fire button has been released, so re-enable loading if blocked
+               self.hagar_loadblock = FALSE;
+       }
+
+       if(self.hagar_load)
+       {
+               // play warning sound if we're about to release
+               if((loaded || !enough_ammo) && self.hagar_loadstep - 0.5 < time && WEP_CVAR_SEC(hagar, load_hold) >= 0)
+               {
+                       if(!self.hagar_warning && self.hagar_load) // prevents the beep from playing each frame
+                       {
+                               // we're about to automatically release after holding time, play a beep sound to notify the player
+                               sound(self, CH_WEAPON_A, "weapons/hagar_beep.wav", VOL_BASE, ATTN_NORM);
+                               self.hagar_warning = TRUE;
+                       }
+               }
+               
+               // release if player let go of button or if they've held it in too long
+               if(!self.BUTTON_ATCK2 || ((loaded || !enough_ammo) && self.hagar_loadstep < time && WEP_CVAR_SEC(hagar, load_hold) >= 0))
+               {
+                       self.weaponentity.state = WS_READY;
+                       W_Hagar_Attack2_Load_Release();
+               }
+       }
+       else
+       {
+               self.hagar_loadbeep = FALSE;
+               self.hagar_warning = FALSE;
+       }
+
+       // we aren't checking ammo during an attack, so we must do it here
+       if not(WEP_ACTION(self.weapon, WR_CHECKAMMO1) + WEP_ACTION(self.weapon, WR_CHECKAMMO2))
+       {
+               // note: this doesn't force the switch
+               W_SwitchToOtherWeapon(self);
+               return;
+       }
+}
+
+float w_hagar(float req)
+{
+       float ammo_amount;
+       switch(req)
+       {
+               case WR_AIM:
+               {
+                       if (random()>0.15)
+                               self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(hagar, speed), 0, WEP_CVAR_PRI(hagar, lifetime), FALSE);
+                       else // not using secondary_speed since these are only 15% and should cause some ricochets without re-aiming
+                               self.BUTTON_ATCK2 = bot_aim(WEP_CVAR_PRI(hagar, speed), 0, WEP_CVAR_PRI(hagar, lifetime), FALSE);
+                               
+                       return TRUE;
+               }
+               case WR_THINK:
+               {
+                       float loadable_secondary;
+                       loadable_secondary = (WEP_CVAR_SEC(hagar, load) && WEP_CVAR(hagar, secondary));
+
+                       if (loadable_secondary)
+                               W_Hagar_Attack2_Load(); // must always run each frame
+                       if(autocvar_g_balance_hagar_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo))) // forced reload
+                               WEP_ACTION(self.weapon, WR_RELOAD);
+                       else if (self.BUTTON_ATCK && !self.hagar_load && !self.hagar_loadblock) // not while secondary is loaded or awaiting reset
+                       {
+                               if (weapon_prepareattack(0, WEP_CVAR_PRI(hagar, refire)))
+                               {
+                                       W_Hagar_Attack();
+                                       weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hagar, refire), w_ready);
+                               }
+                       }
+                       else if (self.BUTTON_ATCK2 && !loadable_secondary && WEP_CVAR(hagar, secondary))
+                       {
+                               if (weapon_prepareattack(1, WEP_CVAR_SEC(hagar, refire)))
+                               {
+                                       W_Hagar_Attack2();
+                                       weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(hagar, refire), w_ready);
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_GONETHINK:
+               {
+                       // we lost the weapon and want to prepare switching away
+                       if(self.hagar_load)
+                       {
+                               self.weaponentity.state = WS_READY;
+                               W_Hagar_Attack2_Load_Release();
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_model ("models/weapons/g_hagar.md3");
+                       precache_model ("models/weapons/v_hagar.md3");
+                       precache_model ("models/weapons/h_hagar.iqm");
+                       precache_sound ("weapons/hagar_fire.wav");
+                       precache_sound ("weapons/hagar_load.wav");
+                       precache_sound ("weapons/hagar_beep.wav");
+                       WEP_SET_PROPS(HAGAR_SETTINGS(hagar), WEP_HAGAR)
+                       return TRUE;
+               }
+               case WR_SETUP:
+               {
+                       self.current_ammo = ammo_rockets;
+                       self.hagar_loadblock = FALSE;
+
+                       if(self.hagar_load)
+                       {
+                               W_DecreaseAmmo(ammo_rockets, WEP_CVAR_SEC(hagar, ammo) * self.hagar_load * -1, autocvar_g_balance_hagar_reload_ammo); // give back ammo if necessary
+                               self.hagar_load = 0;
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_CHECKAMMO1:
+               {
+                       ammo_amount = self.ammo_rockets >= WEP_CVAR_PRI(hagar, ammo);
+                       ammo_amount += self.(weapon_load[WEP_HAGAR]) >= WEP_CVAR_PRI(hagar, ammo);
+                       return ammo_amount;
+               }
+               case WR_CHECKAMMO2:
+               {
+                       ammo_amount = self.ammo_rockets >= WEP_CVAR_SEC(hagar, ammo);
+                       ammo_amount += self.(weapon_load[WEP_HAGAR]) >= WEP_CVAR_SEC(hagar, ammo);
+                       return ammo_amount;
+               }
+               case WR_CONFIG:
+               {
+                       WEP_CONFIG_SETTINGS(HAGAR_SETTINGS(hagar))
+                       return TRUE;
+               }
+               case WR_RESETPLAYER:
+               {
+                       self.hagar_load = 0;
+                       return TRUE;
+               }
+               case WR_PLAYERDEATH:
+               {
+                       // if we have any rockets loaded when we die, release them
+                       if(self.hagar_load && WEP_CVAR_SEC(hagar, load_releasedeath))
+                               W_Hagar_Attack2_Load_Release();
+                               
+                       return TRUE;
+               }
+               case WR_RELOAD:
+               {
+                       if not(self.hagar_load) // require releasing loaded rockets first
+                               W_Reload(min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo)), "weapons/reload.wav");
+                               
+                       return TRUE;
+               }
+               case WR_SUICIDEMESSAGE:
+               {
+                       return WEAPON_HAGAR_SUICIDE;
+               }
+               case WR_KILLMESSAGE:
+               {
+                       if(w_deathtype & HITTYPE_SECONDARY)
+                               return WEAPON_HAGAR_MURDER_BURST;
+                       else
+                               return WEAPON_HAGAR_MURDER_SPRAY;
+               }
+       }
+       return TRUE;
+}
+#endif
+#ifdef CSQC
+float w_hagar(float req)
+{
+       switch(req)
+       {
+               case WR_IMPACTEFFECT:
+               {
+                       vector org2;
+                       org2 = w_org + w_backoff * 6;
+                       pointparticles(particleeffectnum("hagar_explode"), org2, '0 0 0', 1);
+                       if(!w_issilent)
+                       {
+                               if (w_random<0.15)
+                                       sound(self, CH_SHOTS, "weapons/hagexp1.wav", VOL_BASE, ATTN_NORM);
+                               else if (w_random<0.7)
+                                       sound(self, CH_SHOTS, "weapons/hagexp2.wav", VOL_BASE, ATTN_NORM);
+                               else
+                                       sound(self, CH_SHOTS, "weapons/hagexp3.wav", VOL_BASE, ATTN_NORM);
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_sound("weapons/hagexp1.wav");
+                       precache_sound("weapons/hagexp2.wav");
+                       precache_sound("weapons/hagexp3.wav");
+                       return TRUE;
+               }
+       }
+       return TRUE;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/w_hlac.qc b/qcsrc/common/weapons/w_hlac.qc
new file mode 100644 (file)
index 0000000..097be08
--- /dev/null
@@ -0,0 +1,304 @@
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id */ HLAC,
+/* function */ w_hlac,
+/* ammotype */ IT_CELLS,
+/* impulse  */ 6,
+/* flags    */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH,
+/* rating   */ BOT_PICKUP_RATING_MID,
+/* model    */ "hlac",
+/* netname  */ "hlac",
+/* fullname */ _("Heavy Laser Assault Cannon")
+);
+
+#define HLAC_SETTINGS(weapon) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, ammo) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, animtime) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, damage) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, edgedamage) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, force) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, lifetime) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, radius) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, refire) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, speed) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, spread_crouchmod) \
+       WEP_ADD_CVAR(weapon, MO_PRI,  spread_add) \
+       WEP_ADD_CVAR(weapon, MO_PRI,  spread_max) \
+       WEP_ADD_CVAR(weapon, MO_PRI,  spread_min) \
+       WEP_ADD_CVAR(weapon, MO_NONE, secondary) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  shots) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  spread) \
+       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
+HLAC_SETTINGS(hlac)
+#endif
+#else
+#ifdef SVQC
+void spawnfunc_weapon_hlac() { weapon_defaultspawnfunc(WEP_HLAC); }
+
+void W_HLAC_Touch (void)
+{
+       float isprimary;
+
+       PROJECTILE_TOUCH;
+
+       self.event_damage = func_null;
+       
+       isprimary = !(self.projectiledeathtype & HITTYPE_SECONDARY);
+       
+       RadiusDamage(self, self.realowner, WEP_CVAR_BOTH(hlac, isprimary, damage), WEP_CVAR_BOTH(hlac, isprimary, edgedamage), WEP_CVAR_BOTH(hlac, isprimary, radius), world, world, WEP_CVAR_BOTH(hlac, isprimary, force), self.projectiledeathtype, other);
+
+       remove (self);
+}
+
+void W_HLAC_Attack (void)
+{
+       entity missile;
+    float spread;
+
+       W_DecreaseAmmo(ammo_cells, WEP_CVAR_PRI(hlac, ammo), autocvar_g_balance_hlac_reload_ammo);
+
+    spread = WEP_CVAR_PRI(hlac, spread_min) + (WEP_CVAR_PRI(hlac, spread_add) * self.misc_bulletcounter);
+    spread = min(spread,WEP_CVAR_PRI(hlac, spread_max));
+    if(self.crouch)
+        spread = spread * WEP_CVAR_PRI(hlac, spread_crouchmod);
+
+       W_SetupShot (self, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_A, WEP_CVAR_PRI(hlac, damage));
+       pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+       if (!autocvar_g_norecoil)
+       {
+               self.punchangle_x = random () - 0.5;
+               self.punchangle_y = random () - 0.5;
+       }
+
+       missile = spawn ();
+       missile.owner = missile.realowner = self;
+       missile.classname = "hlacbolt";
+       missile.bot_dodge = TRUE;
+
+    missile.bot_dodgerating = WEP_CVAR_PRI(hlac, damage);
+
+       missile.movetype = MOVETYPE_FLY;
+       PROJECTILE_MAKETRIGGER(missile);
+
+       setorigin (missile, w_shotorg);
+       setsize(missile, '0 0 0', '0 0 0');
+
+       W_SetupProjectileVelocity(missile, WEP_CVAR_PRI(hlac, speed), spread);
+       //missile.angles = vectoangles (missile.velocity); // csqc
+
+       missile.touch = W_HLAC_Touch;
+       missile.think = SUB_Remove;
+
+    missile.nextthink = time + WEP_CVAR_PRI(hlac, lifetime);
+
+       missile.flags = FL_PROJECTILE;
+       missile.projectiledeathtype = WEP_HLAC;
+
+       CSQCProjectile(missile, TRUE, PROJECTILE_HLAC, TRUE);
+
+       other = missile; MUTATOR_CALLHOOK(EditProjectile);
+}
+
+void W_HLAC_Attack2f (void)
+{
+       entity missile;
+    float spread;
+
+    spread = WEP_CVAR_SEC(hlac, spread);
+
+
+    if(self.crouch)
+        spread = spread * WEP_CVAR_SEC(hlac, spread_crouchmod);
+
+       W_SetupShot (self, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(hlac, damage));
+       pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+       missile = spawn ();
+       missile.owner = missile.realowner = self;
+       missile.classname = "hlacbolt";
+       missile.bot_dodge = TRUE;
+
+    missile.bot_dodgerating = WEP_CVAR_SEC(hlac, damage);
+
+       missile.movetype = MOVETYPE_FLY;
+       PROJECTILE_MAKETRIGGER(missile);
+
+       setorigin (missile, w_shotorg);
+       setsize(missile, '0 0 0', '0 0 0');
+
+       W_SetupProjectileVelocity(missile, WEP_CVAR_SEC(hlac, speed), spread);
+       //missile.angles = vectoangles (missile.velocity); // csqc
+
+       missile.touch = W_HLAC_Touch;
+       missile.think = SUB_Remove;
+
+    missile.nextthink = time + WEP_CVAR_SEC(hlac, lifetime);
+
+       missile.flags = FL_PROJECTILE;
+       missile.missile_flags = MIF_SPLASH; 
+       missile.projectiledeathtype = WEP_HLAC | HITTYPE_SECONDARY;
+
+       CSQCProjectile(missile, TRUE, PROJECTILE_HLAC, TRUE);
+
+       other = missile; MUTATOR_CALLHOOK(EditProjectile);
+}
+
+void W_HLAC_Attack2 (void)
+{
+    float i;
+
+       W_DecreaseAmmo(ammo_cells, WEP_CVAR_SEC(hlac, ammo), autocvar_g_balance_hlac_reload_ammo);
+
+    for(i=WEP_CVAR_SEC(hlac, shots);i>0;--i)
+        W_HLAC_Attack2f();
+
+       if (!autocvar_g_norecoil)
+       {
+               self.punchangle_x = random () - 0.5;
+               self.punchangle_y = random () - 0.5;
+       }
+}
+
+// weapon frames
+void HLAC_fire1_02()
+{
+       if(self.weapon != self.switchweapon) // abort immediately if switching
+       {
+               w_ready();
+               return;
+       }
+
+       if (self.BUTTON_ATCK)
+       {
+               if (!WEP_ACTION(self.weapon, WR_CHECKAMMO1))
+               if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
+               {
+                       W_SwitchWeapon_Force(self, w_getbestweapon(self));
+                       w_ready();
+                       return;
+               }
+
+               ATTACK_FINISHED(self) = time + WEP_CVAR_PRI(hlac, refire) * W_WeaponRateFactor();
+               W_HLAC_Attack();
+               self.misc_bulletcounter = self.misc_bulletcounter + 1;
+        weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), HLAC_fire1_02);
+       }
+       else
+       {
+               weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hlac, animtime), w_ready);
+       }
+}
+
+float w_hlac(float req)
+{
+       float ammo_amount;
+       switch(req)
+       {
+               case WR_AIM:
+               {
+                       self.BUTTON_ATCK = bot_aim(WEP_CVAR_PRI(hlac, speed), 0, WEP_CVAR_PRI(hlac, lifetime), FALSE);
+                       return TRUE;
+               }
+               case WR_THINK:
+               {
+                       if(autocvar_g_balance_hlac_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(hlac, ammo), WEP_CVAR_SEC(hlac, ammo))) // forced reload
+                               WEP_ACTION(self.weapon, WR_RELOAD);
+                       else if (self.BUTTON_ATCK)
+                       {
+                               if (weapon_prepareattack(0, WEP_CVAR_PRI(hlac, refire)))
+                               {
+                                       self.misc_bulletcounter = 0;
+                                       W_HLAC_Attack();
+                                       weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), HLAC_fire1_02);
+                               }
+                       }
+
+                       else if (self.BUTTON_ATCK2 && WEP_CVAR(hlac, secondary))
+                       {
+                               if (weapon_prepareattack(1, WEP_CVAR_SEC(hlac, refire)))
+                               {
+                                       W_HLAC_Attack2();
+                                       weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(hlac, animtime), w_ready);
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_model ("models/weapons/g_hlac.md3");
+                       precache_model ("models/weapons/v_hlac.md3");
+                       precache_model ("models/weapons/h_hlac.iqm");
+                       precache_sound ("weapons/lasergun_fire.wav");
+                       WEP_SET_PROPS(HLAC_SETTINGS(hlac), WEP_HLAC)
+                       return TRUE;
+               }
+               case WR_SETUP:
+               {
+                       self.current_ammo = ammo_cells;
+                       return TRUE;
+               }
+               case WR_CHECKAMMO1:
+               {
+                       ammo_amount = self.ammo_cells >= WEP_CVAR_PRI(hlac, ammo);
+                       ammo_amount += self.(weapon_load[WEP_HLAC]) >= WEP_CVAR_PRI(hlac, ammo);
+                       return ammo_amount;
+               }
+               case WR_CHECKAMMO2:
+               {
+                       ammo_amount = self.ammo_cells >= WEP_CVAR_SEC(hlac, ammo);
+                       ammo_amount += self.(weapon_load[WEP_HLAC]) >= WEP_CVAR_SEC(hlac, ammo);
+                       return ammo_amount;
+               }
+               case WR_CONFIG:
+               {
+                       WEP_CONFIG_SETTINGS(HLAC_SETTINGS(hlac))
+                       return TRUE;
+               }
+               case WR_RELOAD:
+               {
+                       W_Reload(min(WEP_CVAR_PRI(hlac, ammo), WEP_CVAR_SEC(hlac, ammo)), "weapons/reload.wav");
+                       return TRUE;
+               }
+               case WR_SUICIDEMESSAGE:
+               {
+                       return WEAPON_HLAC_SUICIDE;
+               }
+               case WR_KILLMESSAGE:
+               {
+                       return WEAPON_HLAC_MURDER;
+               }
+       }
+       return TRUE;
+}
+#endif
+#ifdef CSQC
+float w_hlac(float req)
+{
+       switch(req)
+       {
+               case WR_IMPACTEFFECT:
+               {
+                       vector org2;
+                       org2 = w_org + w_backoff * 6;
+                       pointparticles(particleeffectnum("laser_impact"), org2, w_backoff * 1000, 1);
+                       if(!w_issilent)
+                               sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTN_NORM);
+                               
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_sound("weapons/laserimpact.wav");
+                       return TRUE;
+               }
+       }
+       return TRUE;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/w_hook.qc b/qcsrc/common/weapons/w_hook.qc
new file mode 100644 (file)
index 0000000..c803587
--- /dev/null
@@ -0,0 +1,355 @@
+#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_PULLING;
+                               if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
+                                       self.hook_state &= ~HOOK_RELEASING;
+                               else
+                                       self.hook_state |= HOOK_RELEASING;
+                       }
+                       else
+                       {
+                               self.hook_state |= HOOK_PULLING;
+                               self.hook_state &= ~HOOK_RELEASING;
+
+                               if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
+                               {
+                                       // already fired
+                                       if(self.hook)
+                                               self.hook_state |= HOOK_WAITING_FOR_RELEASE;
+                               }
+                               else
+                               {
+                                       self.hook_state |= HOOK_REMOVING;
+                                       self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_model ("models/weapons/g_hookgun.md3");
+                       precache_model ("models/weapons/v_hookgun.md3");
+                       precache_model ("models/weapons/h_hookgun.iqm");
+                       precache_sound ("weapons/hook_impact.wav"); // done by g_hook.qc
+                       precache_sound ("weapons/hook_fire.wav");
+                       precache_sound ("weapons/hookbomb_fire.wav");
+                       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
diff --git a/qcsrc/common/weapons/w_machinegun.qc b/qcsrc/common/weapons/w_machinegun.qc
new file mode 100644 (file)
index 0000000..2d8ea14
--- /dev/null
@@ -0,0 +1,403 @@
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id */ UZI,
+/* function */ w_uzi,
+/* ammotype */ IT_NAILS,
+/* impulse  */ 3,
+/* flags    */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN,
+/* rating   */ BOT_PICKUP_RATING_MID,
+/* model    */ "uzi",
+/* netname  */ "uzi",
+/* fullname */ _("Machine Gun")
+);
+
+#define UZI_SETTINGS(weapon) \
+       WEP_ADD_CVAR(weapon, MO_NONE, speed) \
+       WEP_ADD_CVAR(weapon, MO_NONE, spread_min) \
+       WEP_ADD_CVAR(weapon, MO_NONE, spread_max) \
+       WEP_ADD_CVAR(weapon, MO_NONE, spread_add) \
+       WEP_ADD_CVAR(weapon, MO_NONE, mode) \
+       WEP_ADD_CVAR(weapon, MO_NONE, bulletconstant) \
+       WEP_ADD_CVAR(weapon, MO_NONE, first) \
+       WEP_ADD_CVAR(weapon, MO_NONE, first_damage) \
+       WEP_ADD_CVAR(weapon, MO_NONE, first_force) \
+       WEP_ADD_CVAR(weapon, MO_NONE, first_refire) \
+       WEP_ADD_CVAR(weapon, MO_NONE, first_spread) \
+       WEP_ADD_CVAR(weapon, MO_NONE, first_ammo) \
+       WEP_ADD_CVAR(weapon, MO_NONE, sustained_damage) \
+       WEP_ADD_CVAR(weapon, MO_NONE, sustained_force) \
+       WEP_ADD_CVAR(weapon, MO_NONE, sustained_refire) \
+       WEP_ADD_CVAR(weapon, MO_NONE, sustained_spread) \
+       WEP_ADD_CVAR(weapon, MO_NONE, sustained_ammo) \
+       WEP_ADD_CVAR(weapon, MO_NONE, burst) \
+       WEP_ADD_CVAR(weapon, MO_NONE, burst_refire) \
+       WEP_ADD_CVAR(weapon, MO_NONE, burst_refire2) \
+       WEP_ADD_CVAR(weapon, MO_NONE, burst_animtime) \
+       WEP_ADD_CVAR(weapon, MO_NONE, burst_speed) \
+       WEP_ADD_CVAR(weapon, MO_NONE, burst_ammo) \
+       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
+UZI_SETTINGS(uzi)
+#endif
+#else
+#ifdef SVQC
+
+void spawnfunc_weapon_uzi()
+{
+       if(autocvar_sv_q3acompat_machineshotgunswap)
+       if(self.classname != "droppedweapon")
+       {
+               weapon_defaultspawnfunc(WEP_SHOTGUN);
+               return;
+       }
+       weapon_defaultspawnfunc(WEP_UZI);
+}
+
+// leilei's fancy muzzleflash stuff
+void UZI_Flash_Go()
+{
+       self.frame = self.frame + 2;
+       self.scale = self.scale * 0.5;
+       self.alpha = self.alpha - 0.25;
+       self.nextthink = time + 0.05;
+
+       if (self.alpha <= 0)
+       {
+               self.think = SUB_Remove;
+               self.nextthink = time;
+               self.realowner.muzzle_flash = world;
+               return;
+       }
+
+}
+
+void UziFlash()
+{
+       if (self.muzzle_flash == world)
+               self.muzzle_flash = spawn();
+
+       // muzzle flash for 1st person view
+       setmodel(self.muzzle_flash, "models/uziflash.md3"); // precision set below
+
+       self.muzzle_flash.scale = 0.75;
+       self.muzzle_flash.think = UZI_Flash_Go;
+       self.muzzle_flash.nextthink = time + 0.02;
+       self.muzzle_flash.frame = 2;
+       self.muzzle_flash.alpha = 0.75;
+       self.muzzle_flash.angles_z = random() * 180;
+       self.muzzle_flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
+       self.muzzle_flash.owner = self.muzzle_flash.realowner = self;
+}
+
+void W_UZI_Attack (float deathtype)
+{
+       W_SetupShot (self, autocvar_g_antilag_bullets && WEP_CVAR(uzi, speed) >= autocvar_g_antilag_bullets, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, ((self.misc_bulletcounter == 1) ? WEP_CVAR(uzi, first_damage) : WEP_CVAR(uzi, sustained_damage)));
+       if (!autocvar_g_norecoil)
+       {
+               self.punchangle_x = random () - 0.5;
+               self.punchangle_y = random () - 0.5;
+       }
+
+       // this attack_finished just enforces a cooldown at the end of a burst
+       ATTACK_FINISHED(self) = time + WEP_CVAR(uzi, first_refire) * W_WeaponRateFactor();
+
+       if (self.misc_bulletcounter == 1)
+               fireBallisticBullet(w_shotorg, w_shotdir, WEP_CVAR(uzi, first_spread), WEP_CVAR(uzi, speed), 5, WEP_CVAR(uzi, first_damage), WEP_CVAR(uzi, first_force), deathtype, 0, 1, WEP_CVAR(uzi, bulletconstant));
+       else
+               fireBallisticBullet(w_shotorg, w_shotdir, WEP_CVAR(uzi, sustained_spread), WEP_CVAR(uzi, speed), 5, WEP_CVAR(uzi, sustained_damage), WEP_CVAR(uzi, sustained_force), deathtype, 0, 1, WEP_CVAR(uzi, bulletconstant));
+       endFireBallisticBullet();
+
+       pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+       UziFlash();
+       W_AttachToShotorg(self.muzzle_flash, '5 0 0');
+
+       // casing code
+       if (autocvar_g_casings >= 2)
+               SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, self);
+
+       if (self.misc_bulletcounter == 1)
+               W_DecreaseAmmo(ammo_nails, WEP_CVAR(uzi, first_ammo), autocvar_g_balance_uzi_reload_ammo);
+       else
+               W_DecreaseAmmo(ammo_nails, WEP_CVAR(uzi, sustained_ammo), autocvar_g_balance_uzi_reload_ammo);
+}
+
+// weapon frames
+void uzi_fire1_02()
+{
+       if(self.weapon != self.switchweapon) // abort immediately if switching
+       {
+               w_ready();
+               return;
+       }
+       if (self.BUTTON_ATCK)
+       {
+               if (!WEP_ACTION(self.weapon, WR_CHECKAMMO2))
+               if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
+               {
+                       W_SwitchWeapon_Force(self, w_getbestweapon(self));
+                       w_ready();
+                       return;
+               }
+               self.misc_bulletcounter = self.misc_bulletcounter + 1;
+               W_UZI_Attack(WEP_UZI);
+               weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(uzi, sustained_refire), uzi_fire1_02);
+       }
+       else
+               weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(uzi, sustained_refire), w_ready);
+}
+
+
+void uzi_mode1_fire_auto()
+{
+       float uzi_spread;
+
+       if (!self.BUTTON_ATCK)
+       {
+               w_ready();
+               return;
+       }
+
+       if (!WEP_ACTION(self.weapon, WR_CHECKAMMO1))
+       if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
+       {
+               W_SwitchWeapon_Force(self, w_getbestweapon(self));
+               w_ready();
+               return;
+       }
+
+       W_DecreaseAmmo(ammo_nails, WEP_CVAR(uzi, sustained_ammo), autocvar_g_balance_uzi_reload_ammo);
+
+       W_SetupShot (self, autocvar_g_antilag_bullets && WEP_CVAR(uzi, speed) >= autocvar_g_antilag_bullets, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, WEP_CVAR(uzi, sustained_damage));
+       if (!autocvar_g_norecoil)
+       {
+               self.punchangle_x = random () - 0.5;
+               self.punchangle_y = random () - 0.5;
+       }
+
+       uzi_spread = bound(WEP_CVAR(uzi, spread_min), WEP_CVAR(uzi, spread_min) + (WEP_CVAR(uzi, spread_add) * self.misc_bulletcounter), WEP_CVAR(uzi, spread_max));
+       fireBallisticBullet(w_shotorg, w_shotdir, uzi_spread, WEP_CVAR(uzi, speed), 5, WEP_CVAR(uzi, sustained_damage), WEP_CVAR(uzi, sustained_force), WEP_UZI, 0, 1, WEP_CVAR(uzi, bulletconstant));
+       endFireBallisticBullet();
+
+       self.misc_bulletcounter = self.misc_bulletcounter + 1;
+
+       pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+       UziFlash();
+       W_AttachToShotorg(self.muzzle_flash, '5 0 0');
+
+       if (autocvar_g_casings >= 2) // casing code
+               SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, self);
+
+       ATTACK_FINISHED(self) = time + WEP_CVAR(uzi, first_refire) * W_WeaponRateFactor();
+       weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(uzi, sustained_refire), uzi_mode1_fire_auto);
+}
+
+void uzi_mode1_fire_burst()
+{
+       W_SetupShot (self, autocvar_g_antilag_bullets && WEP_CVAR(uzi, speed) >= autocvar_g_antilag_bullets, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, WEP_CVAR(uzi, sustained_damage));
+       if (!autocvar_g_norecoil)
+       {
+               self.punchangle_x = random () - 0.5;
+               self.punchangle_y = random () - 0.5;
+       }
+
+       fireBallisticBullet(w_shotorg, w_shotdir, WEP_CVAR(uzi, burst_speed), WEP_CVAR(uzi, speed), 5, WEP_CVAR(uzi, sustained_damage), WEP_CVAR(uzi, sustained_force), WEP_UZI, 0, 1, WEP_CVAR(uzi, bulletconstant));
+       endFireBallisticBullet();
+
+
+       pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+       UziFlash();
+       W_AttachToShotorg(self.muzzle_flash, '5 0 0');
+
+       if (autocvar_g_casings >= 2) // casing code
+               SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, self);
+
+       self.misc_bulletcounter = self.misc_bulletcounter + 1;
+       if (self.misc_bulletcounter == 0)
+       {
+               ATTACK_FINISHED(self) = time + WEP_CVAR(uzi, burst_refire2) * W_WeaponRateFactor();
+               weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(uzi, burst_animtime), w_ready);
+       }
+       else
+       {
+               weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(uzi, burst_refire), uzi_mode1_fire_burst);
+       }
+
+}
+
+float w_uzi(float req)
+{
+       float ammo_amount;
+       switch(req)
+       {
+               case WR_AIM:
+               {
+                       if(vlen(self.origin-self.enemy.origin) < 3000 - bound(0, skill, 10) * 200)
+                               self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE);
+                       else
+                               self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE);
+                       
+                       return TRUE;
+               }
+               case WR_THINK:
+               {
+                       if(autocvar_g_balance_uzi_reload_ammo && self.clip_load < min(max(WEP_CVAR(uzi, sustained_ammo), WEP_CVAR(uzi, first_ammo)), WEP_CVAR(uzi, burst_ammo))) // forced reload
+                               WEP_ACTION(self.weapon, WR_RELOAD);
+                       else if(WEP_CVAR(uzi, mode) == 1)
+                       {
+                               if (self.BUTTON_ATCK)
+                               if (weapon_prepareattack(0, 0))
+                               {
+                                       self.misc_bulletcounter = 0;
+                                       uzi_mode1_fire_auto();
+                               }
+
+                               if(self.BUTTON_ATCK2)
+                               if(weapon_prepareattack(1, 0))
+                               {
+                                       if (!WEP_ACTION(self.weapon, WR_CHECKAMMO2))
+                                       if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
+                                       {
+                                               W_SwitchWeapon_Force(self, w_getbestweapon(self));
+                                               w_ready();
+                                               return FALSE;
+                                       }
+
+                                       W_DecreaseAmmo(ammo_nails, WEP_CVAR(uzi, burst_ammo), autocvar_g_balance_uzi_reload_ammo);
+
+                                       self.misc_bulletcounter = WEP_CVAR(uzi, burst) * -1;
+                                       uzi_mode1_fire_burst();
+                               }
+                       }
+                       else
+                       {
+
+                               if (self.BUTTON_ATCK)
+                               if (weapon_prepareattack(0, 0))
+                               {
+                                       self.misc_bulletcounter = 1;
+                                       W_UZI_Attack(WEP_UZI); // sets attack_finished
+                                       weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(uzi, sustained_refire), uzi_fire1_02);
+                               }
+
+                               if (self.BUTTON_ATCK2 && WEP_CVAR(uzi, first))
+                               if (weapon_prepareattack(1, 0))
+                               {
+                                       self.misc_bulletcounter = 1;
+                                       W_UZI_Attack(WEP_UZI | HITTYPE_SECONDARY); // sets attack_finished
+                                       weapon_thinkf(WFRAME_FIRE2, WEP_CVAR(uzi, first_refire), w_ready);
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_model ("models/uziflash.md3");
+                       precache_model ("models/weapons/g_uzi.md3");
+                       precache_model ("models/weapons/v_uzi.md3");
+                       precache_model ("models/weapons/h_uzi.iqm");
+                       precache_sound ("weapons/uzi_fire.wav");
+                       WEP_SET_PROPS(UZI_SETTINGS(uzi), WEP_UZI)
+                       return TRUE;
+               }
+               case WR_SETUP:
+               {
+                       self.current_ammo = ammo_nails;
+                       return TRUE;
+               }
+               case WR_CHECKAMMO1:
+               {
+                       if(WEP_CVAR(uzi, mode) == 1)
+                               ammo_amount = self.ammo_nails >= WEP_CVAR(uzi, sustained_ammo);
+                       else
+                               ammo_amount = self.ammo_nails >= WEP_CVAR(uzi, first_ammo);
+
+                       if(autocvar_g_balance_uzi_reload_ammo)
+                       {
+                               if(WEP_CVAR(uzi, mode) == 1)
+                                       ammo_amount += self.(weapon_load[WEP_UZI]) >= WEP_CVAR(uzi, sustained_ammo);
+                               else
+                                       ammo_amount += self.(weapon_load[WEP_UZI]) >= WEP_CVAR(uzi, first_ammo);
+                       }
+                       return ammo_amount;
+               }
+               case WR_CHECKAMMO2:
+               {
+                       if(WEP_CVAR(uzi, mode) == 1)
+                               ammo_amount = self.ammo_nails >= WEP_CVAR(uzi, burst_ammo);
+                       else
+                               ammo_amount = self.ammo_nails >= WEP_CVAR(uzi, first_ammo);
+
+                       if(autocvar_g_balance_uzi_reload_ammo)
+                       {
+                               if(WEP_CVAR(uzi, mode) == 1)
+                                       ammo_amount += self.(weapon_load[WEP_UZI]) >= WEP_CVAR(uzi, burst_ammo);
+                               else
+                                       ammo_amount += self.(weapon_load[WEP_UZI]) >= WEP_CVAR(uzi, first_ammo);
+                       }
+                       return ammo_amount;
+               }
+               case WR_CONFIG:
+               {
+                       WEP_CONFIG_SETTINGS(UZI_SETTINGS(uzi))
+                       return TRUE;
+               }
+               case WR_RELOAD:
+               {
+                       W_Reload(min(max(WEP_CVAR(uzi, sustained_ammo), WEP_CVAR(uzi, first_ammo)), WEP_CVAR(uzi, burst_ammo)), "weapons/reload.wav");
+                       return TRUE;
+               }
+               case WR_SUICIDEMESSAGE:
+               {
+                       return WEAPON_THINKING_WITH_PORTALS;
+               }
+               case WR_KILLMESSAGE:
+               {
+                       if(w_deathtype & HITTYPE_SECONDARY)
+                               return WEAPON_UZI_MURDER_SNIPE;
+                       else
+                               return WEAPON_UZI_MURDER_SPRAY;
+               }
+       }
+       return TRUE;
+}
+#endif
+#ifdef CSQC
+float w_uzi(float req)
+{
+       switch(req)
+       {
+               case WR_IMPACTEFFECT:
+               {
+                       vector org2;
+                       org2 = w_org + w_backoff * 2;
+                       pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
+                       if(!w_issilent)
+                               if(w_random < 0.05)
+                                       sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
+                               else if(w_random < 0.1)
+                                       sound(self, CH_SHOTS, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
+                               else if(w_random < 0.2)
+                                       sound(self, CH_SHOTS, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
+                                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_sound("weapons/ric1.wav");
+                       precache_sound("weapons/ric2.wav");
+                       precache_sound("weapons/ric3.wav");
+                       return TRUE;
+               }
+       }
+       return TRUE;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/w_minelayer.qc b/qcsrc/common/weapons/w_minelayer.qc
new file mode 100644 (file)
index 0000000..8322abe
--- /dev/null
@@ -0,0 +1,611 @@
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id */ MINE_LAYER,
+/* function */ w_minelayer,
+/* ammotype */ IT_ROCKETS,
+/* impulse  */ 4,
+/* flags    */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH,
+/* rating   */ BOT_PICKUP_RATING_HIGH,
+/* model    */ "minelayer",
+/* netname  */ "minelayer",
+/* fullname */ _("Mine Layer")
+);
+
+#define MINELAYER_SETTINGS(weapon) \
+       WEP_ADD_CVAR(weapon, MO_NONE, ammo) \
+       WEP_ADD_CVAR(weapon, MO_NONE, animtime) \
+       WEP_ADD_CVAR(weapon, MO_NONE, damage) \
+       WEP_ADD_CVAR(weapon, MO_NONE, damageforcescale) \
+       WEP_ADD_CVAR(weapon, MO_NONE, detonatedelay) \
+       WEP_ADD_CVAR(weapon, MO_NONE, edgedamage) \
+       WEP_ADD_CVAR(weapon, MO_NONE, force) \
+       WEP_ADD_CVAR(weapon, MO_NONE, health) \
+       WEP_ADD_CVAR(weapon, MO_NONE, lifetime) \
+       WEP_ADD_CVAR(weapon, MO_NONE, lifetime_countdown) \
+       WEP_ADD_CVAR(weapon, MO_NONE, limit) \
+       WEP_ADD_CVAR(weapon, MO_NONE, protection) \
+       WEP_ADD_CVAR(weapon, MO_NONE, proximityradius) \
+       WEP_ADD_CVAR(weapon, MO_NONE, radius) \
+       WEP_ADD_CVAR(weapon, MO_NONE, refire) \
+       WEP_ADD_CVAR(weapon, MO_NONE, remote_damage) \
+       WEP_ADD_CVAR(weapon, MO_NONE, remote_edgedamage) \
+       WEP_ADD_CVAR(weapon, MO_NONE, remote_force) \
+       WEP_ADD_CVAR(weapon, MO_NONE, remote_radius) \
+       WEP_ADD_CVAR(weapon, MO_NONE, speed) \
+       WEP_ADD_CVAR(weapon, MO_NONE, time) \
+       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
+MINELAYER_SETTINGS(minelayer)
+void W_Mine_Think (void);
+.float minelayer_detonate, mine_explodeanyway;
+.float mine_time;
+.vector mine_orientation;
+#endif
+#else
+#ifdef SVQC
+void spawnfunc_weapon_minelayer() { weapon_defaultspawnfunc(WEP_MINE_LAYER); }
+
+void W_Mine_Stick (entity to)
+{
+       spamsound (self, CH_SHOTS, "weapons/mine_stick.wav", VOL_BASE, ATTN_NORM);
+
+       // in order for mines to face properly when sticking to the ground, they must be a server side entity rather than a csqc projectile
+
+       entity newmine;
+       newmine = spawn();
+       newmine.classname = self.classname;
+
+       newmine.bot_dodge = self.bot_dodge;
+       newmine.bot_dodgerating = self.bot_dodgerating;
+
+       newmine.owner = self.owner;
+       newmine.realowner = self.realowner;
+       setsize(newmine, '-4 -4 -4', '4 4 4');
+       setorigin(newmine, self.origin);
+       setmodel(newmine, "models/mine.md3");
+       newmine.angles = vectoangles(-trace_plane_normal); // face against the surface
+
+       newmine.mine_orientation = -trace_plane_normal;
+
+       newmine.takedamage = self.takedamage;
+       newmine.damageforcescale = self.damageforcescale;
+       newmine.health = self.health;
+       newmine.event_damage = self.event_damage;
+       newmine.spawnshieldtime = self.spawnshieldtime;
+       newmine.damagedbycontents = TRUE;
+
+       newmine.movetype = MOVETYPE_NONE; // lock the mine in place
+       newmine.projectiledeathtype = self.projectiledeathtype;
+
+       newmine.mine_time = self.mine_time;
+
+       newmine.touch = func_null;
+       newmine.think = W_Mine_Think;
+       newmine.nextthink = time;
+       newmine.cnt = self.cnt;
+       newmine.flags = self.flags;
+
+       remove(self);
+       self = newmine;
+
+       if(to)
+               SetMovetypeFollow(self, to);
+}
+
+void W_Mine_Explode ()
+{
+       if(other.takedamage == DAMAGE_AIM)
+               if(IS_PLAYER(other))
+                       if(DIFF_TEAM(self.realowner, other))
+                               if(other.deadflag == DEAD_NO)
+                                       if(IsFlying(other))
+                                               Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT);
+
+       self.event_damage = func_null;
+       self.takedamage = DAMAGE_NO;
+
+       RadiusDamage (self, self.realowner, WEP_CVAR(minelayer, damage), WEP_CVAR(minelayer, edgedamage), WEP_CVAR(minelayer, radius), world, world, WEP_CVAR(minelayer, force), self.projectiledeathtype, other);
+
+       if (self.realowner.weapon == WEP_MINE_LAYER)
+       {
+               entity oldself;
+               oldself = self;
+               self = self.realowner;
+               if (!WEP_ACTION(WEP_MINE_LAYER, WR_CHECKAMMO1))
+               {
+                       self.cnt = WEP_MINE_LAYER;
+                       ATTACK_FINISHED(self) = time;
+                       self.switchweapon = w_getbestweapon(self);
+               }
+               self = oldself;
+       }
+       self.realowner.minelayer_mines -= 1;
+       remove (self);
+}
+
+void W_Mine_DoRemoteExplode ()
+{
+       self.event_damage = func_null;
+       self.takedamage = DAMAGE_NO;
+
+       if(self.movetype == MOVETYPE_NONE || self.movetype == MOVETYPE_FOLLOW)
+               self.velocity = self.mine_orientation; // particle fx and decals need .velocity
+
+       RadiusDamage (self, self.realowner, WEP_CVAR(minelayer, remote_damage), WEP_CVAR(minelayer, remote_edgedamage), WEP_CVAR(minelayer, remote_radius), world, world, WEP_CVAR(minelayer, remote_force), self.projectiledeathtype | HITTYPE_BOUNCE, world);
+
+       if (self.realowner.weapon == WEP_MINE_LAYER)
+       {
+               entity oldself;
+               oldself = self;
+               self = self.realowner;
+               if (!WEP_ACTION(WEP_MINE_LAYER, WR_CHECKAMMO1))
+               {
+                       self.cnt = WEP_MINE_LAYER;
+                       ATTACK_FINISHED(self) = time;
+                       self.switchweapon = w_getbestweapon(self);
+               }
+               self = oldself;
+       }
+       self.realowner.minelayer_mines -= 1;
+       remove (self);
+}
+
+void W_Mine_RemoteExplode ()
+{
+       if(self.realowner.deadflag == DEAD_NO)
+               if((self.spawnshieldtime >= 0)
+                       ? (time >= self.spawnshieldtime) // timer
+                       : (vlen(NearestPointOnBox(self.realowner, self.origin) - self.origin) > WEP_CVAR(minelayer, remote_radius)) // safety device
+               )
+               {
+                       W_Mine_DoRemoteExplode();
+               }
+}
+
+void W_Mine_ProximityExplode ()
+{
+       // make sure no friend is in the mine's radius. If there is any, explosion is delayed until he's at a safe distance
+       if(WEP_CVAR(minelayer, protection) && self.mine_explodeanyway == 0)
+       {
+               entity head;
+               head = findradius(self.origin, WEP_CVAR(minelayer, radius));
+               while(head)
+               {
+                       if(head == self.realowner || SAME_TEAM(head, self.realowner))
+                               return;
+                       head = head.chain;
+               }
+       }
+
+       self.mine_time = 0;
+       W_Mine_Explode();
+}
+
+float W_Mine_Count(entity e)
+{
+       float minecount = 0;
+       entity mine;
+       for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.realowner == e)
+               minecount += 1;
+
+       return minecount;
+}
+
+void W_Mine_Think (void)
+{
+       entity head;
+
+       self.nextthink = time;
+
+       if(self.movetype == MOVETYPE_FOLLOW)
+       {
+               if(LostMovetypeFollow(self))
+               {
+                       UnsetMovetypeFollow(self);
+                       self.movetype = MOVETYPE_NONE;
+               }
+       }
+       
+       // our lifetime has expired, it's time to die - mine_time just allows us to play a sound for this
+       // TODO: replace this mine_trigger.wav sound with a real countdown
+       if ((time > self.cnt) && (!self.mine_time))
+       {
+               if(WEP_CVAR(minelayer, lifetime_countdown) > 0)
+                       spamsound (self, CH_SHOTS, "weapons/mine_trigger.wav", VOL_BASE, ATTN_NORM);
+               self.mine_time = time + WEP_CVAR(minelayer, lifetime_countdown);
+               self.mine_explodeanyway = 1; // make the mine super aggressive -- Samual: Rather, make it not care if a team mate is near.
+       }
+
+       // a player's mines shall explode if he disconnects or dies
+       // TODO: Do this on team change too -- Samual: But isn't a player killed when they switch teams?
+       if(!IS_PLAYER(self.realowner) || self.realowner.deadflag != DEAD_NO)
+       {
+               other = world;
+               self.projectiledeathtype |= HITTYPE_BOUNCE;
+               W_Mine_Explode();
+               return;
+       }
+
+       // set the mine for detonation when a foe gets close enough
+       head = findradius(self.origin, WEP_CVAR(minelayer, proximityradius));
+       while(head)
+       {
+               if(IS_PLAYER(head) && head.deadflag == DEAD_NO)
+               if(head != self.realowner && DIFF_TEAM(head, self.realowner)) // don't trigger for team mates
+               if(!self.mine_time)
+               {
+                       spamsound (self, CH_SHOTS, "weapons/mine_trigger.wav", VOL_BASE, ATTN_NORM);
+                       self.mine_time = time + WEP_CVAR(minelayer, time);
+               }
+               head = head.chain;
+       }
+
+       // explode if it's time to
+       if(self.mine_time && time >= self.mine_time)
+       {
+               W_Mine_ProximityExplode();
+               return;
+       }
+
+       // remote detonation
+       if (self.realowner.weapon == WEP_MINE_LAYER)
+       if (self.realowner.deadflag == DEAD_NO)
+       if (self.minelayer_detonate)
+               W_Mine_RemoteExplode();
+}
+
+void W_Mine_Touch (void)
+{
+       if(self.movetype == MOVETYPE_NONE || self.movetype == MOVETYPE_FOLLOW)
+               return; // we're already a stuck mine, why do we get called? TODO does this even happen?
+
+       if(WarpZone_Projectile_Touch())
+       {
+               if(wasfreed(self))
+                       self.realowner.minelayer_mines -= 1;
+               return;
+       }
+
+       if(other && IS_PLAYER(other) && other.deadflag == DEAD_NO)
+       {
+               // hit a player
+               // don't stick
+       }
+       else
+       {
+               W_Mine_Stick(other);
+       }
+}
+
+void W_Mine_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+       if (self.health <= 0)
+               return;
+               
+       float is_from_enemy = (inflictor.realowner != self.realowner);
+               
+       if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, (is_from_enemy ? 1 : -1)))
+               return; // g_projectiles_damage says to halt
+               
+       self.health = self.health - damage;
+       self.angles = vectoangles(self.velocity);
+       
+       if (self.health <= 0)
+               W_PrepareExplosionByDamage(attacker, W_Mine_Explode);
+}
+
+void W_Mine_Attack (void)
+{
+       entity mine;
+       entity flash;
+
+       // scan how many mines we placed, and return if we reached our limit
+       if(WEP_CVAR(minelayer, limit))
+       {
+               if(self.minelayer_mines >= WEP_CVAR(minelayer, limit))
+               {
+                       // the refire delay keeps this message from being spammed
+                       sprint(self, strcat("minelayer: You cannot place more than ^2", ftos(WEP_CVAR(minelayer, limit)), " ^7mines at a time\n") );
+                       play2(self, "weapons/unavailable.wav");
+                       return;
+               }
+       }
+
+       W_DecreaseAmmo(ammo_rockets, WEP_CVAR(minelayer, ammo), autocvar_g_balance_minelayer_reload_ammo);
+
+       W_SetupShot_ProjectileSize (self, '-4 -4 -4', '4 4 4', FALSE, 5, "weapons/mine_fire.wav", CH_WEAPON_A, WEP_CVAR(minelayer, damage));
+       pointparticles(particleeffectnum("rocketlauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+       mine = WarpZone_RefSys_SpawnSameRefSys(self);
+       mine.owner = mine.realowner = self;
+       if(WEP_CVAR(minelayer, detonatedelay) >= 0)
+               mine.spawnshieldtime = time + WEP_CVAR(minelayer, detonatedelay);
+       else
+               mine.spawnshieldtime = -1;
+       mine.classname = "mine";
+       mine.bot_dodge = TRUE;
+       mine.bot_dodgerating = WEP_CVAR(minelayer, damage) * 2; // * 2 because it can detonate inflight which makes it even more dangerous
+
+       mine.takedamage = DAMAGE_YES;
+       mine.damageforcescale = WEP_CVAR(minelayer, damageforcescale);
+       mine.health = WEP_CVAR(minelayer, health);
+       mine.event_damage = W_Mine_Damage;
+       mine.damagedbycontents = TRUE;
+
+       mine.movetype = MOVETYPE_TOSS;
+       PROJECTILE_MAKETRIGGER(mine);
+       mine.projectiledeathtype = WEP_MINE_LAYER;
+       setsize (mine, '-4 -4 -4', '4 4 4'); // give it some size so it can be shot
+
+       setorigin (mine, w_shotorg - v_forward * 4); // move it back so it hits the wall at the right point
+       W_SetupProjectileVelocity(mine, WEP_CVAR(minelayer, speed), 0);
+       mine.angles = vectoangles (mine.velocity);
+
+       mine.touch = W_Mine_Touch;
+       mine.think = W_Mine_Think;
+       mine.nextthink = time;
+       mine.cnt = time + (WEP_CVAR(minelayer, lifetime) - WEP_CVAR(minelayer, lifetime_countdown));
+       mine.flags = FL_PROJECTILE;
+       mine.missile_flags = MIF_SPLASH | MIF_ARC | MIF_PROXY;
+
+       CSQCProjectile(mine, TRUE, PROJECTILE_MINE, TRUE);
+
+       // muzzle flash for 1st person view
+       flash = spawn ();
+       setmodel (flash, "models/flash.md3"); // precision set below
+       SUB_SetFade (flash, time, 0.1);
+       flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
+       W_AttachToShotorg(flash, '5 0 0');
+
+       // common properties
+
+       other = mine; MUTATOR_CALLHOOK(EditProjectile);
+       
+       self.minelayer_mines = W_Mine_Count(self);
+}
+
+float W_PlacedMines(float detonate)
+{
+       entity mine;
+       float minfound = 0;
+
+       for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.realowner == self)
+       {
+               if(detonate)
+               {
+                       if(!mine.minelayer_detonate)
+                       {
+                               mine.minelayer_detonate = TRUE;
+                               minfound = 1;
+                       }
+               }
+               else
+                       minfound = 1;
+       }
+       return minfound;
+}
+
+float w_minelayer(float req)
+{
+       entity mine;
+       float ammo_amount;
+       switch(req)
+       {
+               case WR_AIM:
+               {
+                       // aim and decide to fire if appropriate
+                       if(self.minelayer_mines >= WEP_CVAR(minelayer, limit))
+                               self.BUTTON_ATCK = FALSE;
+                       else
+                               self.BUTTON_ATCK = bot_aim(WEP_CVAR(minelayer, speed), 0, WEP_CVAR(minelayer, lifetime), FALSE);
+                       if(skill >= 2) // skill 0 and 1 bots won't detonate mines!
+                       {
+                               // decide whether to detonate mines
+                               entity targetlist, targ;
+                               float edgedamage, coredamage, edgeradius, recipricoledgeradius, d;
+                               float selfdamage, teamdamage, enemydamage;
+                               edgedamage = WEP_CVAR(minelayer, edgedamage);
+                               coredamage = WEP_CVAR(minelayer, damage);
+                               edgeradius = WEP_CVAR(minelayer, radius);
+                               recipricoledgeradius = 1 / edgeradius;
+                               selfdamage = 0;
+                               teamdamage = 0;
+                               enemydamage = 0;
+                               targetlist = findchainfloat(bot_attack, TRUE);
+                               mine = find(world, classname, "mine");
+                               while (mine)
+                               {
+                                       if (mine.realowner != self)
+                                       {
+                                               mine = find(mine, classname, "mine");
+                                               continue;
+                                       }
+                                       targ = targetlist;
+                                       while (targ)
+                                       {
+                                               d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - mine.origin);
+                                               d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000);
+                                               // count potential damage according to type of target
+                                               if (targ == self)
+                                                       selfdamage = selfdamage + d;
+                                               else if (targ.team == self.team && teamplay)
+                                                       teamdamage = teamdamage + d;
+                                               else if (bot_shouldattack(targ))
+                                                       enemydamage = enemydamage + d;
+                                               targ = targ.chain;
+                                       }
+                                       mine = find(mine, classname, "mine");
+                               }
+                               float desirabledamage;
+                               desirabledamage = enemydamage;
+                               if (time > self.invincible_finished && time > self.spawnshieldtime)
+                                       desirabledamage = desirabledamage - selfdamage * autocvar_g_balance_selfdamagepercent;
+                               if (teamplay && self.team)
+                                       desirabledamage = desirabledamage - teamdamage;
+
+                               mine = find(world, classname, "mine");
+                               while (mine)
+                               {
+                                       if (mine.realowner != self)
+                                       {
+                                               mine = find(mine, classname, "mine");
+                                               continue;
+                                       }
+                                       makevectors(mine.v_angle);
+                                       targ = targetlist;
+                                       if (skill > 9) // normal players only do this for the target they are tracking
+                                       {
+                                               targ = targetlist;
+                                               while (targ)
+                                               {
+                                                       if (
+                                                               (v_forward * normalize(mine.origin - targ.origin)< 0.1)
+                                                               && desirabledamage > 0.1*coredamage
+                                                       )self.BUTTON_ATCK2 = TRUE;
+                                                       targ = targ.chain;
+                                               }
+                                       }else{
+                                               float distance; distance= bound(300,vlen(self.origin-self.enemy.origin),30000);
+                                               //As the distance gets larger, a correct detonation gets near imposible
+                                               //Bots are assumed to use the mine spawnfunc_light to see if the mine gets near a player
+                                               if(v_forward * normalize(mine.origin - self.enemy.origin)< 0.1)
+                                                       if(IS_PLAYER(self.enemy))
+                                                               if(desirabledamage >= 0.1*coredamage)
+                                                                       if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1))
+                                                                               self.BUTTON_ATCK2 = TRUE;
+                                       //      dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n");
+                                       }
+
+                                       mine = find(mine, classname, "mine");
+                               }
+                               // if we would be doing at X percent of the core damage, detonate it
+                               // but don't fire a new shot at the same time!
+                               if (desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events
+                                       self.BUTTON_ATCK2 = TRUE;
+                               if ((skill > 6.5) && (selfdamage > self.health))
+                                       self.BUTTON_ATCK2 = FALSE;
+                               //if(self.BUTTON_ATCK2 == TRUE)
+                               //      dprint(ftos(desirabledamage),"\n");
+                               if (self.BUTTON_ATCK2 == TRUE) self.BUTTON_ATCK = FALSE;
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_THINK:
+               {
+                       if(autocvar_g_balance_minelayer_reload_ammo && self.clip_load < WEP_CVAR(minelayer, ammo)) // forced reload
+                       {
+                               // not if we're holding the minelayer without enough ammo, but can detonate existing mines
+                               if not (W_PlacedMines(FALSE) && self.ammo_rockets < WEP_CVAR(minelayer, ammo))
+                                       WEP_ACTION(self.weapon, WR_RELOAD);
+                       }
+                       else if (self.BUTTON_ATCK)
+                       {
+                               if(weapon_prepareattack(0, WEP_CVAR(minelayer, refire)))
+                               {
+                                       W_Mine_Attack();
+                                       weapon_thinkf(WFRAME_FIRE1, WEP_CVAR(minelayer, animtime), w_ready);
+                               }
+                       }
+
+                       if (self.BUTTON_ATCK2)
+                       {
+                               if(W_PlacedMines(TRUE))
+                                       sound (self, CH_WEAPON_B, "weapons/mine_det.wav", VOL_BASE, ATTN_NORM);
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_model ("models/flash.md3");
+                       precache_model ("models/mine.md3");
+                       precache_model ("models/weapons/g_minelayer.md3");
+                       precache_model ("models/weapons/v_minelayer.md3");
+                       precache_model ("models/weapons/h_minelayer.iqm");
+                       precache_sound ("weapons/mine_det.wav");
+                       precache_sound ("weapons/mine_fire.wav");
+                       precache_sound ("weapons/mine_stick.wav");
+                       precache_sound ("weapons/mine_trigger.wav");
+                       WEP_SET_PROPS(MINELAYER_SETTINGS(minelayer), WEP_MINE_LAYER)
+                       return TRUE;
+               }
+               case WR_SETUP:
+               {
+                       self.current_ammo = ammo_rockets;
+                       return TRUE;
+               }
+               case WR_CHECKAMMO1:
+               {
+                       // don't switch while placing a mine
+                       if (ATTACK_FINISHED(self) <= time || self.weapon != WEP_MINE_LAYER)
+                       {
+                               ammo_amount = self.ammo_rockets >= WEP_CVAR(minelayer, ammo);
+                               ammo_amount += self.(weapon_load[WEP_MINE_LAYER]) >= WEP_CVAR(minelayer, ammo);
+                               return ammo_amount;
+                       }
+                       return TRUE;
+               }
+               case WR_CHECKAMMO2:
+               {
+                       if (W_PlacedMines(FALSE))
+                               return TRUE;
+                       else
+                               return FALSE;
+               }
+               case WR_CONFIG:
+               {
+                       WEP_CONFIG_SETTINGS(MINELAYER_SETTINGS(minelayer))
+                       return TRUE;
+               }
+               case WR_RESETPLAYER:
+               {
+                       self.minelayer_mines = 0;
+                       return TRUE;
+               }
+               case WR_RELOAD:
+               {
+                       W_Reload(WEP_CVAR(minelayer, ammo), "weapons/reload.wav");
+                       return TRUE;
+               }
+               case WR_SUICIDEMESSAGE:
+               {
+                       return WEAPON_MINELAYER_SUICIDE;
+               }
+               case WR_KILLMESSAGE:
+               {
+                       return WEAPON_MINELAYER_MURDER;
+               }
+       }
+       return TRUE;
+}
+#endif
+#ifdef CSQC
+float w_minelayer(float req)
+{
+       switch(req)
+       {
+               case WR_IMPACTEFFECT:
+               {
+                       vector org2;
+                       org2 = w_org + w_backoff * 12;
+                       pointparticles(particleeffectnum("rocket_explode"), org2, '0 0 0', 1);
+                       if(!w_issilent)
+                               sound(self, CH_SHOTS, "weapons/mine_exp.wav", VOL_BASE, ATTN_NORM);
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_sound("weapons/mine_exp.wav");
+                       return TRUE;
+               }
+       }
+       return TRUE;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/w_minstanex.qc b/qcsrc/common/weapons/w_minstanex.qc
new file mode 100644 (file)
index 0000000..b7adc52
--- /dev/null
@@ -0,0 +1,230 @@
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id */ MINSTANEX,
+/* function */ w_minstanex,
+/* ammotype */ IT_CELLS,
+/* impulse  */ 7,
+/* flags    */ WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_FLAG_SUPERWEAPON | WEP_TYPE_HITSCAN,
+/* rating   */ BOT_PICKUP_RATING_HIGH,
+/* model    */ "minstanex",
+/* netname  */ "minstanex",
+/* fullname */ _("MinstaNex")
+);
+
+#ifdef SVQC
+.float minstanex_lasthit;
+.float jump_interval;
+#endif
+#else
+#ifdef SVQC
+void spawnfunc_weapon_minstanex (void) { weapon_defaultspawnfunc(WEP_MINSTANEX); }
+
+void W_MinstaNex_Attack (void)
+{
+       float flying;
+       flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last
+
+       W_SetupShot (self, TRUE, 0, "weapons/minstanexfire.wav", CH_WEAPON_A, 10000);
+
+       yoda = 0;
+       damage_goodhits = 0;
+       FireRailgunBullet (w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, 10000, 800, 0, 0, 0, 0, WEP_MINSTANEX);
+
+       if(yoda && flying)
+               Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA);
+       if(damage_goodhits && self.minstanex_lasthit)
+       {
+               Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_IMPRESSIVE);
+               damage_goodhits = 0; // only every second time
+       }
+
+       self.minstanex_lasthit = damage_goodhits;
+
+       pointparticles(particleeffectnum("nex_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+       // teamcolor / hit beam effect
+       vector v;
+       v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
+       switch(self.team)
+       {
+               case NUM_TEAM_1:   // Red
+                       if(damage_goodhits)
+                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED_HIT"), w_shotorg, v);
+                       else
+                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED"), w_shotorg, v);
+                       break;
+               case NUM_TEAM_2:   // Blue
+                       if(damage_goodhits)
+                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE_HIT"), w_shotorg, v);
+                       else
+                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE"), w_shotorg, v);
+                       break;
+               case NUM_TEAM_3:   // Yellow
+                       if(damage_goodhits)
+                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW_HIT"), w_shotorg, v);
+                       else
+                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW"), w_shotorg, v);
+                       break;
+               case NUM_TEAM_4:   // Pink
+                       if(damage_goodhits)
+                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK_HIT"), w_shotorg, v);
+                       else
+                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK"), w_shotorg, v);
+                       break;
+               default:
+                       if(damage_goodhits)
+                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3_HIT"), w_shotorg, v);
+                       else
+                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3"), w_shotorg, v);
+                       break;
+       }
+       
+       W_DecreaseAmmo(ammo_cells, ((g_minstagib) ? 1 : autocvar_g_balance_minstanex_ammo), autocvar_g_balance_minstanex_reload_ammo);
+}
+
+float w_minstanex(float req)
+{
+       float ammo_amount;
+       float minstanex_ammo;
+
+       // now multiple WR_s use this
+       minstanex_ammo = ((g_minstagib) ? 1 : autocvar_g_balance_minstanex_ammo);
+
+       switch(req)
+       {
+               case WR_AIM:
+               {
+                       if(self.ammo_cells > 0)
+                               self.BUTTON_ATCK = bot_aim(1000000, 0, 1, FALSE);
+                       else
+                               self.BUTTON_ATCK2 = bot_aim(autocvar_g_balance_laser_primary_speed, 0, autocvar_g_balance_laser_primary_lifetime, FALSE);
+                               
+                       return TRUE;
+               }
+               case WR_THINK:
+               {
+                       // if the laser uses load, we also consider its ammo for reloading
+                       if(autocvar_g_balance_minstanex_reload_ammo && autocvar_g_balance_minstanex_laser_ammo && self.clip_load < min(minstanex_ammo, autocvar_g_balance_minstanex_laser_ammo)) // forced reload
+                               WEP_ACTION(self.weapon, WR_RELOAD);
+                       else if(autocvar_g_balance_minstanex_reload_ammo && self.clip_load < minstanex_ammo) // forced reload
+                               WEP_ACTION(self.weapon, WR_RELOAD);
+                       else if (self.BUTTON_ATCK)
+                       {
+                               if (weapon_prepareattack(0, autocvar_g_balance_minstanex_refire))
+                               {
+                                       W_MinstaNex_Attack();
+                                       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_minstanex_animtime, w_ready);
+                               }
+                       }
+                       else if (self.BUTTON_ATCK2)
+                       {
+                               if (self.jump_interval <= time)
+                               if (weapon_prepareattack(1, -1))
+                               {
+                                       // handle refire manually, so that primary and secondary can be fired without conflictions (important for minstagib)
+                                       self.jump_interval = time + autocvar_g_balance_minstanex_laser_refire * W_WeaponRateFactor();
+                                       
+                                       // decrease ammo for the laser?
+                                       if(autocvar_g_balance_minstanex_laser_ammo)
+                                               W_DecreaseAmmo(ammo_cells, autocvar_g_balance_minstanex_laser_ammo, autocvar_g_balance_minstanex_reload_ammo);
+
+                                       // ugly minstagib hack to reuse the fire mode of the laser
+                                       float w;
+                                       w = self.weapon;
+                                       self.weapon = WEP_LASER;
+                                       W_Laser_Shockwave();
+                                       self.weapon = w;
+                                       
+                                       // now do normal refire
+                                       weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_minstanex_laser_animtime, w_ready);
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_model ("models/nexflash.md3");
+                       precache_model ("models/weapons/g_minstanex.md3");
+                       precache_model ("models/weapons/v_minstanex.md3");
+                       precache_model ("models/weapons/h_minstanex.iqm");
+                       precache_sound ("weapons/minstanexfire.wav");
+                       precache_sound ("weapons/nexwhoosh1.wav");
+                       precache_sound ("weapons/nexwhoosh2.wav");
+                       precache_sound ("weapons/nexwhoosh3.wav");
+                       W_Laser(WR_INIT);
+                       return TRUE;
+               }
+               case WR_SETUP:
+               {
+                       self.current_ammo = ammo_cells;
+                       self.minstanex_lasthit = 0;
+                       return TRUE;
+               }
+               case WR_CHECKAMMO1:
+               {
+                       ammo_amount = self.ammo_cells >= minstanex_ammo;
+                       ammo_amount += self.(weapon_load[WEP_MINSTANEX]) >= minstanex_ammo;
+                       return ammo_amount;
+               }
+               case WR_CHECKAMMO2:
+               {
+                       if(!autocvar_g_balance_minstanex_laser_ammo)
+                               return TRUE;
+                       ammo_amount = self.ammo_cells >= autocvar_g_balance_minstanex_laser_ammo;
+                       ammo_amount += self.(weapon_load[WEP_MINSTANEX]) >= autocvar_g_balance_minstanex_laser_ammo;
+                       return ammo_amount;
+               }
+               case WR_RESETPLAYER:
+               {
+                       self.minstanex_lasthit = 0;
+                       return TRUE;
+               }
+               case WR_RELOAD:
+               {
+                       float used_ammo;
+                       if(autocvar_g_balance_minstanex_laser_ammo)
+                               used_ammo = min(minstanex_ammo, autocvar_g_balance_minstanex_laser_ammo);
+                       else
+                               used_ammo = minstanex_ammo;
+
+                       W_Reload(used_ammo, "weapons/reload.wav");
+                       return TRUE;
+               }
+               case WR_SUICIDEMESSAGE:
+               {
+                       return WEAPON_THINKING_WITH_PORTALS;
+               }
+               case WR_KILLMESSAGE:
+               {
+                       return WEAPON_MINSTANEX_MURDER;
+               }
+       }
+       return TRUE;
+}
+#endif
+#ifdef CSQC
+float w_minstanex(float req)
+{
+       switch(req)
+       {
+               case WR_IMPACTEFFECT:
+               {
+                       vector org2;
+                       org2 = w_org + w_backoff * 6;
+                       pointparticles(particleeffectnum("nex_impact"), org2, '0 0 0', 1);
+                       if(!w_issilent)
+                               sound(self, CH_SHOTS, "weapons/neximpact.wav", VOL_BASE, ATTN_NORM);
+                               
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_sound("weapons/neximpact.wav");
+                       return TRUE;
+               }
+       }
+       return TRUE;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/w_mortar.qc b/qcsrc/common/weapons/w_mortar.qc
new file mode 100644 (file)
index 0000000..b6f156d
--- /dev/null
@@ -0,0 +1,477 @@
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id */ GRENADE_LAUNCHER,
+/* function */ w_glauncher,
+/* ammotype */ IT_ROCKETS,
+/* impulse  */ 4,
+/* flags    */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
+/* rating   */ BOT_PICKUP_RATING_MID,
+/* model    */ "gl",
+/* netname  */ "grenadelauncher",
+/* fullname */ _("Mortar")
+);
+
+#define MORTAR_SETTINGS(weapon) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, ammo) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, animtime) \
+       WEP_ADD_CVAR(weapon, MO_NONE, bouncefactor) \
+       WEP_ADD_CVAR(weapon, MO_NONE, bouncestop) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, damage) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, damageforcescale) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, edgedamage) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, force) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, health) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, lifetime) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  lifetime_bounce) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, lifetime_stick) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, radius) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, refire) \
+       WEP_ADD_CVAR(weapon, MO_PRI,  remote_minbouncecnt) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, speed) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, speed_up) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, type) \
+       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
+MORTAR_SETTINGS(mortar)
+.float gl_detonate_later;
+.float gl_bouncecnt;
+#endif
+#else
+#ifdef SVQC
+
+void W_Grenade_Explode (void)
+{
+       if(other.takedamage == DAMAGE_AIM)
+               if(IS_PLAYER(other))
+                       if(DIFF_TEAM(self.realowner, other))
+                               if(other.deadflag == DEAD_NO)
+                                       if(IsFlying(other))
+                                               Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT);
+
+       self.event_damage = func_null;
+       self.takedamage = DAMAGE_NO;
+
+       if(self.movetype == MOVETYPE_NONE)
+               self.velocity = self.oldvelocity;
+
+       RadiusDamage (self, self.realowner, WEP_CVAR_PRI(mortar, damage), WEP_CVAR_PRI(mortar, edgedamage), WEP_CVAR_PRI(mortar, radius), world, world, WEP_CVAR_PRI(mortar, force), self.projectiledeathtype, other);
+
+       remove (self);
+}
+
+void W_Grenade_Explode2 (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_AIRSHOT);
+
+       self.event_damage = func_null;
+       self.takedamage = DAMAGE_NO;
+
+       if(self.movetype == MOVETYPE_NONE)
+               self.velocity = self.oldvelocity;
+
+       RadiusDamage (self, self.realowner, WEP_CVAR_SEC(mortar, damage), WEP_CVAR_SEC(mortar, edgedamage), WEP_CVAR_SEC(mortar, radius), world, world, WEP_CVAR_SEC(mortar, force), self.projectiledeathtype, other);
+
+       remove (self);
+}
+
+void W_Grenade_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+       if (self.health <= 0)
+               return;
+               
+       if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
+               return; // g_projectiles_damage says to halt
+               
+       self.health = self.health - damage;
+       
+       if (self.health <= 0)
+               W_PrepareExplosionByDamage(attacker, self.use);
+}
+
+void W_Grenade_Think1 (void)
+{
+       self.nextthink = time;
+       if (time > self.cnt)
+       {
+               other = world;
+               self.projectiledeathtype |= HITTYPE_BOUNCE;
+               W_Grenade_Explode ();
+               return;
+       }
+       if(self.gl_detonate_later && self.gl_bouncecnt >= WEP_CVAR_PRI(mortar, remote_minbouncecnt))
+               W_Grenade_Explode();
+}
+
+void W_Grenade_Touch1 (void)
+{
+       PROJECTILE_TOUCH;
+       if (other.takedamage == DAMAGE_AIM || WEP_CVAR_PRI(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile
+       {
+               self.use ();
+       }
+       else if (WEP_CVAR_PRI(mortar, type) == 1) // bounce
+       {
+               float r;
+               r = random() * 6;
+               if(r < 1)
+                       spamsound (self, CH_SHOTS, "weapons/grenade_bounce1.wav", VOL_BASE, ATTN_NORM);
+               else if(r < 2)
+                       spamsound (self, CH_SHOTS, "weapons/grenade_bounce2.wav", VOL_BASE, ATTN_NORM);
+               else if(r < 3)
+                       spamsound (self, CH_SHOTS, "weapons/grenade_bounce3.wav", VOL_BASE, ATTN_NORM);
+               else if(r < 4)
+                       spamsound (self, CH_SHOTS, "weapons/grenade_bounce4.wav", VOL_BASE, ATTN_NORM);
+               else if(r < 5)
+                       spamsound (self, CH_SHOTS, "weapons/grenade_bounce5.wav", VOL_BASE, ATTN_NORM);
+               else
+                       spamsound (self, CH_SHOTS, "weapons/grenade_bounce6.wav", VOL_BASE, ATTN_NORM);
+               self.projectiledeathtype |= HITTYPE_BOUNCE;
+               self.gl_bouncecnt += 1;
+       }
+       else if(WEP_CVAR_PRI(mortar, type) == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick
+       {
+               spamsound (self, CH_SHOTS, "weapons/grenade_stick.wav", VOL_BASE, ATTN_NORM);
+
+               // let it stick whereever it is
+               self.oldvelocity = self.velocity;
+               self.velocity = '0 0 0';
+               self.movetype = MOVETYPE_NONE; // also disables gravity
+               self.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO
+               UpdateCSQCProjectile(self);
+
+               // do not respond to any more touches
+               self.solid = SOLID_NOT;
+
+               self.nextthink = min(self.nextthink, time + WEP_CVAR_PRI(mortar, lifetime_stick));
+       }
+}
+
+void W_Grenade_Touch2 (void)
+{
+       PROJECTILE_TOUCH;
+       if (other.takedamage == DAMAGE_AIM || WEP_CVAR_SEC(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile
+       {
+               self.use ();
+       }
+       else if (WEP_CVAR_SEC(mortar, type) == 1) // bounce
+       {
+               float r;
+               r = random() * 6;
+               if(r < 1)
+                       spamsound (self, CH_SHOTS, "weapons/grenade_bounce1.wav", VOL_BASE, ATTN_NORM);
+               else if(r < 2)
+                       spamsound (self, CH_SHOTS, "weapons/grenade_bounce2.wav", VOL_BASE, ATTN_NORM);
+               else if(r < 3)
+                       spamsound (self, CH_SHOTS, "weapons/grenade_bounce3.wav", VOL_BASE, ATTN_NORM);
+               else if(r < 4)
+                       spamsound (self, CH_SHOTS, "weapons/grenade_bounce4.wav", VOL_BASE, ATTN_NORM);
+               else if(r < 5)
+                       spamsound (self, CH_SHOTS, "weapons/grenade_bounce5.wav", VOL_BASE, ATTN_NORM);
+               else
+                       spamsound (self, CH_SHOTS, "weapons/grenade_bounce6.wav", VOL_BASE, ATTN_NORM);
+               self.projectiledeathtype |= HITTYPE_BOUNCE;
+               self.gl_bouncecnt += 1;
+               
+               if (WEP_CVAR_SEC(mortar, lifetime_bounce) && self.gl_bouncecnt == 1)
+                       self.nextthink = time + WEP_CVAR_SEC(mortar, lifetime_bounce);
+                       
+       }
+       else if(WEP_CVAR_SEC(mortar, type) == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick
+       {
+               spamsound (self, CH_SHOTS, "weapons/grenade_stick.wav", VOL_BASE, ATTN_NORM);
+
+               // let it stick whereever it is
+               self.oldvelocity = self.velocity;
+               self.velocity = '0 0 0';
+               self.movetype = MOVETYPE_NONE; // also disables gravity
+               self.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO
+               UpdateCSQCProjectile(self);
+
+               // do not respond to any more touches
+               self.solid = SOLID_NOT;
+
+               self.nextthink = min(self.nextthink, time + WEP_CVAR_SEC(mortar, lifetime_stick));
+       }
+}
+
+void W_Grenade_Attack (void)
+{
+       entity gren;
+
+       W_DecreaseAmmo(ammo_rockets, WEP_CVAR_PRI(mortar, ammo), autocvar_g_balance_mortar_reload_ammo); // WEAPONTODO
+
+       W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', FALSE, 4, "weapons/grenade_fire.wav", CH_WEAPON_A, WEP_CVAR_PRI(mortar, damage));
+       w_shotdir = v_forward; // no TrueAim for grenades please
+
+       pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+       gren = spawn ();
+       gren.owner = gren.realowner = self;
+       gren.classname = "grenade";
+       gren.bot_dodge = TRUE;
+       gren.bot_dodgerating = WEP_CVAR_PRI(mortar, damage);
+       gren.movetype = MOVETYPE_BOUNCE;
+       gren.bouncefactor = WEP_CVAR(mortar, bouncefactor);
+       gren.bouncestop = WEP_CVAR(mortar, bouncestop);
+       PROJECTILE_MAKETRIGGER(gren);
+       gren.projectiledeathtype = WEP_GRENADE_LAUNCHER;
+       setorigin(gren, w_shotorg);
+       setsize(gren, '-3 -3 -3', '3 3 3');
+
+       gren.cnt = time + WEP_CVAR_PRI(mortar, lifetime);
+       gren.nextthink = time;
+       gren.think = W_Grenade_Think1;
+       gren.use = W_Grenade_Explode;
+       gren.touch = W_Grenade_Touch1;
+
+       gren.takedamage = DAMAGE_YES;
+       gren.health = WEP_CVAR_PRI(mortar, health);
+       gren.damageforcescale = WEP_CVAR_PRI(mortar, damageforcescale);
+       gren.event_damage = W_Grenade_Damage;
+       gren.damagedbycontents = TRUE;
+       gren.missile_flags = MIF_SPLASH | MIF_ARC;
+       W_SETUPPROJECTILEVELOCITY_UP(gren, g_balance_mortar_primary); // WEAPONTODO
+
+       gren.angles = vectoangles (gren.velocity);
+       gren.flags = FL_PROJECTILE;
+
+       if(WEP_CVAR_PRI(mortar, type) == 0 || WEP_CVAR_PRI(mortar, type) == 2)
+               CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE, TRUE);
+       else
+               CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE_BOUNCING, TRUE);
+
+       other = gren; MUTATOR_CALLHOOK(EditProjectile);
+}
+
+void W_Grenade_Attack2 (void)
+{
+       entity gren;
+
+       W_DecreaseAmmo(ammo_rockets, WEP_CVAR_SEC(mortar, ammo), autocvar_g_balance_mortar_reload_ammo);
+
+       W_SetupShot_ProjectileSize (self, '-3 -3 -3', '3 3 3', FALSE, 4, "weapons/grenade_fire.wav", CH_WEAPON_A, WEP_CVAR_SEC(mortar, damage));
+       w_shotdir = v_forward; // no TrueAim for grenades please
+
+       pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+       gren = spawn ();
+       gren.owner = gren.realowner = self;
+       gren.classname = "grenade";
+       gren.bot_dodge = TRUE;
+       gren.bot_dodgerating = WEP_CVAR_SEC(mortar, damage);
+       gren.movetype = MOVETYPE_BOUNCE;
+       gren.bouncefactor = WEP_CVAR(mortar, bouncefactor);
+       gren.bouncestop = WEP_CVAR(mortar, bouncestop);
+       PROJECTILE_MAKETRIGGER(gren);
+       gren.projectiledeathtype = WEP_GRENADE_LAUNCHER | HITTYPE_SECONDARY;
+       setorigin(gren, w_shotorg);
+       setsize(gren, '-3 -3 -3', '3 3 3');
+
+       gren.nextthink = time + WEP_CVAR_SEC(mortar, lifetime);
+       gren.think = adaptor_think2use_hittype_splash;
+       gren.use = W_Grenade_Explode2;
+       gren.touch = W_Grenade_Touch2;
+
+       gren.takedamage = DAMAGE_YES;
+       gren.health = WEP_CVAR_SEC(mortar, health);
+       gren.damageforcescale = WEP_CVAR_SEC(mortar, damageforcescale);
+       gren.event_damage = W_Grenade_Damage;
+       gren.damagedbycontents = TRUE;
+       gren.missile_flags = MIF_SPLASH | MIF_ARC;
+       W_SETUPPROJECTILEVELOCITY_UP(gren, g_balance_mortar_secondary); // WEAPONTODO
+
+       gren.angles = vectoangles (gren.velocity);
+       gren.flags = FL_PROJECTILE;
+
+       if(WEP_CVAR_SEC(mortar, type) == 0 || WEP_CVAR_SEC(mortar, type) == 2)
+               CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE, TRUE);
+       else
+               CSQCProjectile(gren, TRUE, PROJECTILE_GRENADE_BOUNCING, TRUE);
+
+       other = gren; MUTATOR_CALLHOOK(EditProjectile);
+}
+
+void spawnfunc_weapon_grenadelauncher (void)
+{
+       weapon_defaultspawnfunc(WEP_GRENADE_LAUNCHER);
+}
+
+.float bot_secondary_grenademooth;
+float w_glauncher(float req)
+{
+       entity nade;
+       float nadefound;
+       float ammo_amount;
+       switch(req)
+       {
+               case WR_AIM:
+               {
+                       self.BUTTON_ATCK = FALSE;
+                       self.BUTTON_ATCK2 = FALSE;
+                       if (self.bot_secondary_grenademooth == 0) // WEAPONTODO: merge this into using WEP_CVAR_BOTH
+                       {
+                               if(bot_aim(WEP_CVAR_PRI(mortar, speed), WEP_CVAR_PRI(mortar, speed_up), WEP_CVAR_PRI(mortar, lifetime), TRUE))
+                               {
+                                       self.BUTTON_ATCK = TRUE;
+                                       if(random() < 0.01) self.bot_secondary_grenademooth = 1;
+                               }
+                       }
+                       else
+                       {
+                               if(bot_aim(WEP_CVAR_SEC(mortar, speed), WEP_CVAR_SEC(mortar, speed_up), WEP_CVAR_SEC(mortar, lifetime), TRUE))
+                               {
+                                       self.BUTTON_ATCK2 = TRUE;
+                                       if(random() < 0.02) self.bot_secondary_grenademooth = 0;
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               /*case WR_CALCINFO:
+               {
+                       wepinfo_pri_refire = max3(sys_frametime, WEP_CVAR_PRI(mortar, refire), WEP_CVAR_PRI(mortar, animtime));
+                       wepinfo_pri_dps = (WEP_CVAR_PRI(mortar, damage) * (1 / wepinfo_pri_refire));
+                       wepinfo_pri_speed = (1 / max(1, (10000 / max(1, WEP_CVAR_PRI(mortar, speed)))));
+
+                       // for the range calculation, closer to 1 is better
+                       wepinfo_pri_range_max = 2000 * wepinfo_pri_speed;
+                       wepinfo_pri_range = wepinfo_pri_speed * WEP_CVAR_PRI(mortar, 
+                       
+                       wepinfo_sec_refire = max3(sys_frametime, WEP_CVAR_SEC(mortar, refire), WEP_CVAR_SEC(mortar, animtime));
+                       wepinfo_sec_dps = (WEP_CVAR_SEC(mortar, damage) * (1 / wepinfo_sec_refire));
+                       
+                       wepinfo_sec_dps = (WEP_CVAR_SEC(mortar, damage) * (1 / max3(sys_frametime, WEP_CVAR_SEC(mortar, refire), WEP_CVAR_SEC(mortar, animtime))));
+                       wepinfo_ter_dps = 0;
+                       */
+               case WR_THINK:
+               {
+                       if(autocvar_g_balance_mortar_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo))) // forced reload
+                               WEP_ACTION(self.weapon, WR_RELOAD);
+                       else if (self.BUTTON_ATCK)
+                       {
+                               if (weapon_prepareattack(0, WEP_CVAR_PRI(mortar, refire)))
+                               {
+                                       W_Grenade_Attack();
+                                       weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(mortar, animtime), w_ready);
+                               }
+                       }
+                       else if (self.BUTTON_ATCK2)
+                       {
+                               if (cvar("g_balance_mortar_secondary_remote_detonateprimary"))
+                               {
+                                       nadefound = 0;
+                                       for(nade = world; (nade = find(nade, classname, "grenade")); ) if(nade.realowner == self)
+                                       {
+                                               if(!nade.gl_detonate_later)
+                                               {
+                                                       nade.gl_detonate_later = TRUE;
+                                                       nadefound = 1;
+                                               }
+                                       }
+                                       if(nadefound)
+                                               sound (self, CH_WEAPON_B, "weapons/rocket_det.wav", VOL_BASE, ATTN_NORM);
+                               }
+                               else if (weapon_prepareattack(1, WEP_CVAR_SEC(mortar, refire)))
+                               {
+                                       W_Grenade_Attack2();
+                                       weapon_thinkf(WFRAME_FIRE2, WEP_CVAR_SEC(mortar, animtime), w_ready);
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_model ("models/weapons/g_gl.md3");
+                       precache_model ("models/weapons/v_gl.md3");
+                       precache_model ("models/weapons/h_gl.iqm");
+                       precache_sound ("weapons/grenade_bounce1.wav");
+                       precache_sound ("weapons/grenade_bounce2.wav");
+                       precache_sound ("weapons/grenade_bounce3.wav");
+                       precache_sound ("weapons/grenade_bounce4.wav");
+                       precache_sound ("weapons/grenade_bounce5.wav");
+                       precache_sound ("weapons/grenade_bounce6.wav");
+                       precache_sound ("weapons/grenade_stick.wav");
+                       precache_sound ("weapons/grenade_fire.wav");
+                       WEP_SET_PROPS(MORTAR_SETTINGS(mortar), WEP_GRENADE_LAUNCHER)
+                       return TRUE;
+               }
+               case WR_SETUP:
+               {
+                       self.current_ammo = ammo_rockets;
+                       return TRUE;
+               }
+               case WR_CHECKAMMO1:
+               {
+                       ammo_amount = self.ammo_rockets >= WEP_CVAR_PRI(mortar, ammo);
+                       ammo_amount += self.(weapon_load[WEP_GRENADE_LAUNCHER]) >= WEP_CVAR_PRI(mortar, ammo);
+                       return ammo_amount;
+               }
+               case WR_CHECKAMMO2:
+               {
+                       ammo_amount = self.ammo_rockets >= WEP_CVAR_SEC(mortar, ammo);
+                       ammo_amount += self.(weapon_load[WEP_GRENADE_LAUNCHER]) >= WEP_CVAR_SEC(mortar, ammo);
+                       return ammo_amount;
+               }
+               case WR_CONFIG:
+               {
+                       WEP_CONFIG_SETTINGS(MORTAR_SETTINGS(mortar))
+                       return TRUE;
+               }
+               case WR_RELOAD:
+               {
+                       W_Reload(min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo)), "weapons/reload.wav"); // WEAPONTODO
+                       return TRUE;
+               }
+               case WR_SUICIDEMESSAGE:
+               {
+                       if(w_deathtype & HITTYPE_SECONDARY)
+                               return WEAPON_MORTAR_SUICIDE_BOUNCE;
+                       else
+                               return WEAPON_MORTAR_SUICIDE_EXPLODE;
+               }
+               case WR_KILLMESSAGE:
+               {
+                       if(w_deathtype & HITTYPE_SECONDARY)
+                               return WEAPON_MORTAR_MURDER_BOUNCE;
+                       else
+                               return WEAPON_MORTAR_MURDER_EXPLODE;
+               }
+       }
+       return TRUE;
+}
+#endif
+#ifdef CSQC
+float w_glauncher(float req)
+{
+       switch(req)
+       {
+               case WR_IMPACTEFFECT:
+               {
+                       vector org2;
+                       org2 = w_org + w_backoff * 12;
+                       pointparticles(particleeffectnum("grenade_explode"), org2, '0 0 0', 1);
+                       if(!w_issilent)
+                               sound(self, CH_SHOTS, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
+                               
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_sound("weapons/grenade_impact.wav");
+                       return TRUE;
+               }
+       }
+       return TRUE;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/w_nex.qc b/qcsrc/common/weapons/w_nex.qc
new file mode 100644 (file)
index 0000000..6b5f4a2
--- /dev/null
@@ -0,0 +1,318 @@
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id */ NEX,
+/* function */ w_nex,
+/* ammotype */ IT_CELLS,
+/* impulse  */ 7,
+/* flags    */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN,
+/* rating   */ BOT_PICKUP_RATING_HIGH,
+/* model    */ "nex",
+/* netname  */ "nex",
+/* fullname */ _("Nex")
+);
+
+#define NEX_SETTINGS(weapon) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, ammo) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, animtime) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, damage) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, force) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, damagefalloff_mindist) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, damagefalloff_maxdist) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, damagefalloff_halflife) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, damagefalloff_forcehalflife) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, refire) \
+       WEP_ADD_CVAR(weapon, MO_NONE, charge) \
+       WEP_ADD_CVAR(weapon, MO_NONE, charge_mindmg) \
+       WEP_ADD_CVAR(weapon, MO_NONE, charge_shot_multiplier) \
+       WEP_ADD_CVAR(weapon, MO_NONE, charge_animlimit) \
+       WEP_ADD_CVAR(weapon, MO_NONE, charge_limit) \
+       WEP_ADD_CVAR(weapon, MO_NONE, charge_rate) \
+       WEP_ADD_CVAR(weapon, MO_NONE, charge_rot_rate) \
+       WEP_ADD_CVAR(weapon, MO_NONE, charge_rot_pause) \
+       WEP_ADD_CVAR(weapon, MO_NONE, charge_start) \
+       WEP_ADD_CVAR(weapon, MO_NONE, charge_minspeed) \
+       WEP_ADD_CVAR(weapon, MO_NONE, charge_maxspeed) \
+       WEP_ADD_CVAR(weapon, MO_NONE, charge_velocity_rate) \
+       WEP_ADD_CVAR(weapon, MO_NONE, secondary) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  chargepool) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  chargepool_regen) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  chargepool_pause_regen) \
+       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
+NEX_SETTINGS(nex)
+#endif
+#else
+#ifdef SVQC
+void spawnfunc_weapon_nex (void) { weapon_defaultspawnfunc(WEP_NEX); }
+
+void SendCSQCNexBeamParticle(float charge) {
+       vector v;
+       v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
+       WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
+       WriteByte(MSG_BROADCAST, TE_CSQC_NEXGUNBEAMPARTICLE);
+       WriteCoord(MSG_BROADCAST, w_shotorg_x);
+       WriteCoord(MSG_BROADCAST, w_shotorg_y);
+       WriteCoord(MSG_BROADCAST, w_shotorg_z);
+       WriteCoord(MSG_BROADCAST, v_x);
+       WriteCoord(MSG_BROADCAST, v_y);
+       WriteCoord(MSG_BROADCAST, v_z);
+       WriteByte(MSG_BROADCAST, bound(0, 255 * charge, 255));
+}
+
+void W_Nex_Attack (float issecondary)
+{
+       float mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, myammo, charge;
+       
+       mydmg = WEP_CVAR_BOTH(nex, !issecondary, damage);
+       myforce = WEP_CVAR_BOTH(nex, !issecondary, force);
+       mymindist = WEP_CVAR_BOTH(nex, !issecondary, damagefalloff_mindist);
+       mymaxdist = WEP_CVAR_BOTH(nex, !issecondary, damagefalloff_maxdist);
+       myhalflife = WEP_CVAR_BOTH(nex, !issecondary, damagefalloff_halflife);
+       myforcehalflife = WEP_CVAR_BOTH(nex, !issecondary, damagefalloff_forcehalflife);
+       myammo = WEP_CVAR_BOTH(nex, !issecondary, ammo);
+
+       float flying;
+       flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last
+
+       if(WEP_CVAR(nex, charge))
+       {
+               charge = WEP_CVAR(nex, charge_mindmg) / mydmg + (1 - WEP_CVAR(nex, charge_mindmg) / mydmg) * self.nex_charge;
+               self.nex_charge *= WEP_CVAR(nex, charge_shot_multiplier); // do this AFTER setting mydmg/myforce
+               // O RLY? -- divVerent
+               // YA RLY -- FruitieX
+       }
+       else
+               charge = 1;
+       mydmg *= charge;
+       myforce *= charge;
+
+       W_SetupShot (self, TRUE, 5, "weapons/nexfire.wav", CH_WEAPON_A, mydmg);
+       if(charge > WEP_CVAR(nex, charge_animlimit) && WEP_CVAR(nex, charge_animlimit)) // if the Nex is overcharged, we play an extra sound
+       {
+               sound (self, CH_WEAPON_B, "weapons/nexcharge.wav", VOL_BASE * (charge - 0.5 * WEP_CVAR(nex, charge_animlimit)) / (1 - 0.5 * WEP_CVAR(nex, charge_animlimit)), ATTN_NORM);
+       }
+
+       yoda = 0;
+       FireRailgunBullet (w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, WEP_NEX);
+
+       if(yoda && flying)
+               Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); 
+
+       //beam and muzzle flash done on client
+       SendCSQCNexBeamParticle(charge);
+
+       W_DecreaseAmmo(ammo_cells, myammo, autocvar_g_balance_nex_reload_ammo);
+}
+
+void spawnfunc_weapon_nex (void); // defined in t_items.qc
+
+.float nex_chargepool_pauseregen_finished;
+float w_nex(float req)
+{
+       float dt;
+       float ammo_amount;
+       switch(req)
+       {
+               case WR_AIM:
+               {
+                       if(bot_aim(1000000, 0, 1, FALSE))
+                               self.BUTTON_ATCK = TRUE;
+                       else
+                       {
+                               if(WEP_CVAR(nex, charge))
+                                       self.BUTTON_ATCK2 = TRUE;
+                       }
+                       return TRUE;
+               }
+               case WR_THINK:
+               {
+                       if(WEP_CVAR(nex, charge) && self.nex_charge < WEP_CVAR(nex, charge_limit))
+                               self.nex_charge = min(1, self.nex_charge + WEP_CVAR(nex, charge_rate) * frametime / W_TICSPERFRAME);
+                               
+                       if(WEP_CVAR_SEC(nex, chargepool))
+                               if(self.nex_chargepool_ammo < 1)
+                               {
+                                       if(self.nex_chargepool_pauseregen_finished < time)
+                                               self.nex_chargepool_ammo = min(1, self.nex_chargepool_ammo + WEP_CVAR_SEC(nex, chargepool_regen) * frametime / W_TICSPERFRAME);
+                                       self.pauseregen_finished = max(self.pauseregen_finished, time + WEP_CVAR_SEC(nex, chargepool_pause_regen));
+                               }
+
+                       if(autocvar_g_balance_nex_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(nex, ammo), WEP_CVAR_SEC(nex, ammo))) // forced reload
+                               WEP_ACTION(self.weapon, WR_RELOAD);
+                       else
+                       {
+                               if (self.BUTTON_ATCK)
+                               {
+                                       if (weapon_prepareattack(0, WEP_CVAR_PRI(nex, refire)))
+                                       {
+                                               W_Nex_Attack(0);
+                                               weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_PRI(nex, animtime), w_ready);
+                                       }
+                               }
+                               if ((WEP_CVAR(nex, charge) && !WEP_CVAR(nex, secondary)) ? (self.BUTTON_ZOOM | self.BUTTON_ZOOMSCRIPT) : self.BUTTON_ATCK2)
+                               {
+                                       if(WEP_CVAR(nex, charge))
+                                       {
+                                               self.nex_charge_rottime = time + WEP_CVAR(nex, charge_rot_pause);
+                                               dt = frametime / W_TICSPERFRAME;
+
+                                               if(self.nex_charge < 1)
+                                               {
+                                                       if(WEP_CVAR_SEC(nex, chargepool))
+                                                       {
+                                                               if(WEP_CVAR_SEC(nex, ammo))
+                                                               {
+                                                                       // always deplete if secondary is held
+                                                                       self.nex_chargepool_ammo = max(0, self.nex_chargepool_ammo - WEP_CVAR_SEC(nex, ammo) * dt);
+
+                                                                       dt = min(dt, (1 - self.nex_charge) / WEP_CVAR(nex, charge_rate));
+                                                                       self.nex_chargepool_pauseregen_finished = time + WEP_CVAR_SEC(nex, chargepool_pause_regen);
+                                                                       dt = min(dt, self.nex_chargepool_ammo);
+                                                                       dt = max(0, dt);
+
+                                                                       self.nex_charge += dt * WEP_CVAR(nex, charge_rate);
+                                                               }
+                                                       }
+
+                                                       else if(WEP_CVAR_SEC(nex, ammo))
+                                                       {
+                                                               if(self.BUTTON_ATCK2) // only eat ammo when the button is pressed
+                                                               {
+                                                                       dt = min(dt, (1 - self.nex_charge) / WEP_CVAR(nex, charge_rate));
+                                                                       if not(self.items & IT_UNLIMITED_WEAPON_AMMO)
+                                                                       {
+                                                                               // if this weapon is reloadable, decrease its load. Else decrease the player's ammo
+                                                                               if(autocvar_g_balance_nex_reload_ammo)
+                                                                               {
+                                                                                       dt = min(dt, (self.clip_load - WEP_CVAR_PRI(nex, ammo)) / WEP_CVAR_SEC(nex, ammo));
+                                                                                       dt = max(0, dt);
+                                                                                       if(dt > 0)
+                                                                                       {
+                                                                                               self.clip_load = max(WEP_CVAR_SEC(nex, ammo), self.clip_load - WEP_CVAR_SEC(nex, ammo) * dt);
+                                                                                       }
+                                                                                       self.(weapon_load[WEP_NEX]) = self.clip_load;
+                                                                               }
+                                                                               else
+                                                                               {
+                                                                                       dt = min(dt, (self.ammo_cells - WEP_CVAR_PRI(nex, ammo)) / WEP_CVAR_SEC(nex, ammo));
+                                                                                       dt = max(0, dt);
+                                                                                       if(dt > 0)
+                                                                                       {
+                                                                                               self.ammo_cells = max(WEP_CVAR_SEC(nex, ammo), self.ammo_cells - WEP_CVAR_SEC(nex, ammo) * dt);
+                                                                                       }
+                                                                               }
+                                                                       }
+                                                                       self.nex_charge += dt * WEP_CVAR(nex, charge_rate);
+                                                               }
+                                                       }
+
+                                                       else
+                                                       {
+                                                               dt = min(dt, (1 - self.nex_charge) / WEP_CVAR(nex, charge_rate));
+                                                               self.nex_charge += dt * WEP_CVAR(nex, charge_rate);
+                                                       }
+                                               }
+                                       }
+                                       else if(WEP_CVAR(nex, secondary))
+                                       {
+                                               if (weapon_prepareattack(0, WEP_CVAR_SEC(nex, refire)))
+                                               {
+                                                       W_Nex_Attack(1);
+                                                       weapon_thinkf(WFRAME_FIRE1, WEP_CVAR_SEC(nex, animtime), w_ready);
+                                               }
+                                       }
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_model ("models/nexflash.md3");
+                       precache_model ("models/weapons/g_nex.md3");
+                       precache_model ("models/weapons/v_nex.md3");
+                       precache_model ("models/weapons/h_nex.iqm");
+                       precache_sound ("weapons/nexfire.wav");
+                       precache_sound ("weapons/nexcharge.wav");
+                       precache_sound ("weapons/nexwhoosh1.wav");
+                       precache_sound ("weapons/nexwhoosh2.wav");
+                       precache_sound ("weapons/nexwhoosh3.wav");
+                       WEP_SET_PROPS(NEX_SETTINGS(nex), WEP_NEX)
+                       return TRUE;
+               }
+               case WR_SETUP:
+               {
+                       self.current_ammo = ammo_cells;
+                       return TRUE;
+               }
+               case WR_CHECKAMMO1:
+               {
+                       ammo_amount = self.ammo_cells >= WEP_CVAR_PRI(nex, ammo);
+                       ammo_amount += (autocvar_g_balance_nex_reload_ammo && self.(weapon_load[WEP_NEX]) >= WEP_CVAR_PRI(nex, ammo));
+                       return ammo_amount;
+               }
+               case WR_CHECKAMMO2:
+               {
+                       if(WEP_CVAR(nex, secondary))
+                       {
+                               // don't allow charging if we don't have enough ammo
+                               ammo_amount = self.ammo_cells >= WEP_CVAR_SEC(nex, ammo);
+                               ammo_amount += self.(weapon_load[WEP_NEX]) >= WEP_CVAR_SEC(nex, ammo);  
+                               return ammo_amount;
+                       }
+                       else
+                       {
+                               return FALSE; // zoom is not a fire mode
+                       }
+               }
+               case WR_CONFIG:
+               {
+                       WEP_CONFIG_SETTINGS(NEX_SETTINGS(nex))
+                       return TRUE;
+               }
+               case WR_RELOAD:
+               {
+                       W_Reload(min(WEP_CVAR_PRI(nex, ammo), WEP_CVAR_SEC(nex, ammo)), "weapons/reload.wav");
+                       return TRUE;
+               }
+               case WR_SUICIDEMESSAGE:
+               {
+                       return WEAPON_THINKING_WITH_PORTALS;
+               }
+               case WR_KILLMESSAGE:
+               {
+                       return WEAPON_NEX_MURDER;
+               }
+       }
+       return TRUE;
+}
+#endif
+#ifdef CSQC
+float w_nex(float req)
+{
+       switch(req)
+       {
+               case WR_IMPACTEFFECT:
+               {
+                       vector org2;
+                       org2 = w_org + w_backoff * 6;
+                       pointparticles(particleeffectnum("nex_impact"), org2, '0 0 0', 1);
+                       if(!w_issilent)
+                               sound(self, CH_SHOTS, "weapons/neximpact.wav", VOL_BASE, ATTN_NORM);
+                               
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_sound("weapons/neximpact.wav");
+                       return TRUE;
+               }
+       }
+       return TRUE;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/w_porto.qc b/qcsrc/common/weapons/w_porto.qc
new file mode 100644 (file)
index 0000000..53d28c0
--- /dev/null
@@ -0,0 +1,410 @@
+#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;
+
+       if(self.cnt < 0 && !failhard && self.realowner.playerid == self.playerid && self.realowner.deadflag == DEAD_NO && !(self.realowner.weapons & WEPSET_PORTO))
+       {
+               setsize (self, '-16 -16 0', '16 16 32');
+               setorigin(self, self.origin + trace_plane_normal);
+               if(move_out_of_solid(self))
+               {
+                       self.flags = FL_ITEM;
+                       self.velocity = trigger_push_calculatevelocity(self.origin, self.realowner, 128);
+                       tracetoss(self, self);
+                       if(vlen(trace_endpos - self.realowner.origin) < 128)
+                       {
+                               W_ThrowNewWeapon(self.realowner, WEP_PORTO, 0, self.origin, self.velocity);
+                               centerprint(self.realowner, "^1Portal deployment failed.\n\n^2Catch it to try again!");
+                       }
+               }
+       }
+       remove(self);
+}
+
+void W_Porto_Remove (entity p)
+{
+       if(p.porto_current.realowner == p && p.porto_current.classname == "porto")
+       {
+               entity oldself;
+               oldself = self;
+               self = p.porto_current;
+               W_Porto_Fail(1);
+               self = oldself;
+       }
+}
+
+void W_Porto_Think (void)
+{
+       trace_plane_normal = '0 0 0';
+       if(self.realowner.playerid != self.playerid)
+               remove(self);
+       else
+               W_Porto_Fail(0);
+}
+
+void W_Porto_Touch (void)
+{
+       vector norm;
+
+       // do not use PROJECTILE_TOUCH here
+       // FIXME but DO handle warpzones!
+
+       if(other.classname == "portal")
+               return; // handled by the portal
+
+       norm = trace_plane_normal;
+       if(trace_ent.iscreature)
+       {
+               traceline(trace_ent.origin, trace_ent.origin + '0 0 2' * PL_MIN_z, MOVE_WORLDONLY, self);
+               if(trace_fraction >= 1)
+                       return;
+               if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
+                       return;
+               if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
+                       return;
+       }
+
+       if(self.realowner.playerid != self.playerid)
+       {
+               sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
+               remove(self);
+       }
+       else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
+       {
+               spamsound(self, CH_SHOTS, "porto/bounce.wav", VOL_BASE, ATTEN_NORM);
+               // just reflect
+               self.right_vector = self.right_vector - 2 * trace_plane_normal * (self.right_vector * trace_plane_normal);
+               self.angles = vectoangles(self.velocity - 2 * trace_plane_normal * (self.velocity * trace_plane_normal));
+       }
+       else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
+       {
+               sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
+               W_Porto_Fail(0);
+               if(self.cnt < 0)
+                       Portal_ClearAll_PortalsOnly(self.realowner);
+       }
+       else if(self.cnt == 0)
+       {
+               // in-portal only
+               if(Portal_SpawnInPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
+               {
+                       sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
+                       trace_plane_normal = norm;
+                       centerprint(self.realowner, "^1In^7-portal created.");
+                       W_Porto_Success();
+               }
+               else
+               {
+                       sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
+                       trace_plane_normal = norm;
+                       W_Porto_Fail(0);
+               }
+       }
+       else if(self.cnt == 1)
+       {
+               // out-portal only
+               if(Portal_SpawnOutPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
+               {
+                       sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
+                       trace_plane_normal = norm;
+                       centerprint(self.realowner, "^4Out^7-portal created.");
+                       W_Porto_Success();
+               }
+               else
+               {
+                       sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
+                       trace_plane_normal = norm;
+                       W_Porto_Fail(0);
+               }
+       }
+       else if(self.effects & EF_RED)
+       {
+               self.effects += EF_BLUE - EF_RED;
+               if(Portal_SpawnInPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
+               {
+                       sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
+                       trace_plane_normal = norm;
+                       centerprint(self.realowner, "^1In^7-portal created.");
+                       self.right_vector = self.right_vector - 2 * trace_plane_normal * (self.right_vector * norm);
+                       self.angles = vectoangles(self.velocity - 2 * trace_plane_normal * (self.velocity * norm));
+                       CSQCProjectile(self, TRUE, PROJECTILE_PORTO_BLUE, TRUE); // change type
+               }
+               else
+               {
+                       sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
+                       trace_plane_normal = norm;
+                       Portal_ClearAll_PortalsOnly(self.realowner);
+                       W_Porto_Fail(0);
+               }
+       }
+       else
+       {
+               if(self.realowner.portal_in.portal_id == self.portal_id)
+               {
+                       if(Portal_SpawnOutPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
+                       {
+                               sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
+                               trace_plane_normal = norm;
+                               centerprint(self.realowner, "^4Out^7-portal created.");
+                               W_Porto_Success();
+                       }
+                       else
+                       {
+                               sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
+                               Portal_ClearAll_PortalsOnly(self.realowner);
+                               W_Porto_Fail(0);
+                       }
+               }
+               else
+               {
+                       sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
+                       Portal_ClearAll_PortalsOnly(self.realowner);
+                       W_Porto_Fail(0);
+               }
+       }
+}
+
+void W_Porto_Attack (float type)
+{
+       entity gren;
+
+       W_SetupShot (self, FALSE, 4, "porto/fire.wav", CH_WEAPON_A, 0);
+       // always shoot from the eye
+       w_shotdir = v_forward;
+       w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward;
+
+       //pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
+
+       gren = spawn ();
+       gren.cnt = type;
+       gren.owner = gren.realowner = self;
+       gren.playerid = self.playerid;
+       gren.classname = "porto";
+       gren.bot_dodge = TRUE;
+       gren.bot_dodgerating = 200;
+       gren.movetype = MOVETYPE_BOUNCEMISSILE;
+       PROJECTILE_MAKETRIGGER(gren);
+       gren.effects = EF_RED;
+       gren.scale = 4;
+       setorigin(gren, w_shotorg);
+       setsize(gren, '0 0 0', '0 0 0');
+       
+       gren.nextthink = time + WEP_CVAR_BOTH(porto, (type <= 0), lifetime);
+       gren.think = W_Porto_Think;
+       gren.touch = W_Porto_Touch;
+       
+       if(self.items & IT_STRENGTH)
+               W_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
diff --git a/qcsrc/common/weapons/w_rifle.qc b/qcsrc/common/weapons/w_rifle.qc
new file mode 100644 (file)
index 0000000..76cf939
--- /dev/null
@@ -0,0 +1,299 @@
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id */ RIFLE,
+/* function */ w_rifle,
+/* ammotype */ IT_NAILS,
+/* impulse  */ 7,
+/* flags    */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN,
+/* rating   */ BOT_PICKUP_RATING_MID,
+/* model    */ "campingrifle",
+/* netname  */ "rifle",
+/* fullname */ _("Rifle")
+);
+
+#define RIFLE_SETTINGS(weapon) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, ammo) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, animtime) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, bulletconstant) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, bullethail) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, burstcost) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, damage) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, force) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, lifetime) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, refire) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, shots) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, speed) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, spread) \
+       WEP_ADD_CVAR(weapon, MO_BOTH, tracer) \
+       WEP_ADD_CVAR(weapon, MO_NONE, bursttime) \
+       WEP_ADD_CVAR(weapon, MO_NONE, secondary) \
+       WEP_ADD_CVAR(weapon, MO_SEC,  reload) \
+       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
+RIFLE_SETTINGS(rifle)
+.float rifle_accumulator;
+#endif
+#else
+#ifdef SVQC
+void spawnfunc_weapon_rifle (void) { weapon_defaultspawnfunc(WEP_RIFLE); }
+void spawnfunc_weapon_campingrifle (void) { spawnfunc_weapon_rifle(); }
+void spawnfunc_weapon_sniperrifle (void) { spawnfunc_weapon_rifle(); }
+
+void W_Rifle_FireBullet(float pSpread, float pDamage, float pForce, float pSpeed, float pLifetime, float pAmmo, float deathtype, float pBulletConstant, float pTracer, float pShots, string pSound)
+{
+       float i;
+
+       W_DecreaseAmmo(ammo_nails, pAmmo, autocvar_g_balance_rifle_reload_ammo);
+
+       W_SetupShot (self, autocvar_g_antilag_bullets && pSpeed >= autocvar_g_antilag_bullets, 2, pSound, CH_WEAPON_A, pDamage * pShots);
+
+       pointparticles(particleeffectnum("rifle_muzzleflash"), w_shotorg, w_shotdir * 2000, 1);
+
+       if(self.BUTTON_ZOOM | self.BUTTON_ZOOMSCRIPT) // if zoomed, shoot from the eye
+       {
+               w_shotdir = v_forward;
+               w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward;
+       }
+
+       for(i = 0; i < pShots; ++i)
+               fireBallisticBullet(w_shotorg, w_shotdir, pSpread, pSpeed, pLifetime, pDamage, pForce, deathtype, (pTracer ? EF_RED : EF_BLUE), 1, pBulletConstant);
+       endFireBallisticBullet();
+
+       if (autocvar_g_casings >= 2)
+               SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, self);
+}
+
+void W_Rifle_Attack()
+{
+       W_Rifle_FireBullet(WEP_CVAR_PRI(rifle, spread), WEP_CVAR_PRI(rifle, damage), WEP_CVAR_PRI(rifle, force), WEP_CVAR_PRI(rifle, speed), WEP_CVAR_PRI(rifle, lifetime), WEP_CVAR_PRI(rifle, ammo), WEP_RIFLE, WEP_CVAR_PRI(rifle, bulletconstant), WEP_CVAR_PRI(rifle, tracer), WEP_CVAR_PRI(rifle, shots), "weapons/campingrifle_fire.wav");
+}
+
+void W_Rifle_Attack2()
+{
+       W_Rifle_FireBullet(WEP_CVAR_SEC(rifle, spread), WEP_CVAR_SEC(rifle, damage), WEP_CVAR_SEC(rifle, force), WEP_CVAR_SEC(rifle, speed), WEP_CVAR_SEC(rifle, lifetime), WEP_CVAR_SEC(rifle, ammo), WEP_RIFLE | HITTYPE_SECONDARY, WEP_CVAR_SEC(rifle, bulletconstant), WEP_CVAR_SEC(rifle, tracer), WEP_CVAR_SEC(rifle, shots), "weapons/campingrifle_fire2.wav");
+}
+
+.void(void) rifle_bullethail_attackfunc;
+.float rifle_bullethail_frame;
+.float rifle_bullethail_animtime;
+.float rifle_bullethail_refire;
+void W_Rifle_BulletHail_Continue()
+{
+       float r, sw, af;
+
+       sw = self.switchweapon; // make it not detect weapon changes as reason to abort firing
+       af = ATTACK_FINISHED(self);
+       self.switchweapon = self.weapon;
+       ATTACK_FINISHED(self) = time;
+       print(ftos(self.ammo_nails), "\n");
+       r = weapon_prepareattack(self.rifle_bullethail_frame == WFRAME_FIRE2, self.rifle_bullethail_refire);
+       if(self.switchweapon == self.weapon)
+               self.switchweapon = sw;
+       if(r)
+       {
+               self.rifle_bullethail_attackfunc();
+               weapon_thinkf(self.rifle_bullethail_frame, self.rifle_bullethail_animtime, W_Rifle_BulletHail_Continue);
+               print("thinkf set\n");
+       }
+       else
+       {
+               ATTACK_FINISHED(self) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
+               print("out of ammo... ", ftos(self.weaponentity.state), "\n");
+       }
+}
+
+void W_Rifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire)
+{
+       // if we get here, we have at least one bullet to fire
+       AttackFunc();
+       if(mode)
+       {
+               // continue hail
+               self.rifle_bullethail_attackfunc = AttackFunc;
+               self.rifle_bullethail_frame = fr;
+               self.rifle_bullethail_animtime = animtime;
+               self.rifle_bullethail_refire = refire;
+               weapon_thinkf(fr, animtime, W_Rifle_BulletHail_Continue);
+       }
+       else
+       {
+               // just one shot
+               weapon_thinkf(fr, animtime, w_ready);
+       }
+}
+
+.float bot_secondary_riflemooth;
+float w_rifle(float req)
+{
+       float ammo_amount;
+       
+       switch(req)
+       {
+               case WR_AIM:
+               {
+                       self.BUTTON_ATCK=FALSE;
+                       self.BUTTON_ATCK2=FALSE;
+                       if(vlen(self.origin-self.enemy.origin) > 1000)
+                               self.bot_secondary_riflemooth = 0;
+                       if(self.bot_secondary_riflemooth == 0)
+                       {
+                               if(bot_aim(WEP_CVAR_PRI(rifle, speed), 0, WEP_CVAR_PRI(rifle, lifetime), FALSE))
+                               {
+                                       self.BUTTON_ATCK = TRUE;
+                                       if(random() < 0.01) self.bot_secondary_riflemooth = 1;
+                               }
+                       }
+                       else
+                       {
+                               if(bot_aim(WEP_CVAR_SEC(rifle, speed), 0, WEP_CVAR_SEC(rifle, lifetime), FALSE))
+                               {
+                                       self.BUTTON_ATCK2 = TRUE;
+                                       if(random() < 0.03) self.bot_secondary_riflemooth = 0;
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_THINK:
+               {
+                       if(autocvar_g_balance_rifle_reload_ammo && self.clip_load < min(WEP_CVAR_PRI(rifle, ammo), WEP_CVAR_SEC(rifle, ammo))) // forced reload
+                               WEP_ACTION(self.weapon, WR_RELOAD);
+                       else
+                       {
+                               self.rifle_accumulator = bound(time - WEP_CVAR(rifle, bursttime), self.rifle_accumulator, time);
+                               if (self.BUTTON_ATCK)
+                               if (weapon_prepareattack_check(0, WEP_CVAR_PRI(rifle, refire)))
+                               if (time >= self.rifle_accumulator + WEP_CVAR_PRI(rifle, burstcost))
+                               {
+                                       weapon_prepareattack_do(0, WEP_CVAR_PRI(rifle, refire));
+                                       W_Rifle_BulletHail(WEP_CVAR_PRI(rifle, bullethail), W_Rifle_Attack, WFRAME_FIRE1, WEP_CVAR_PRI(rifle, animtime), WEP_CVAR_PRI(rifle, refire));
+                                       self.rifle_accumulator += WEP_CVAR_PRI(rifle, burstcost);
+                               }
+                               if (self.BUTTON_ATCK2)
+                               {
+                                       if (WEP_CVAR(rifle, secondary))
+                                       {
+                                               if(WEP_CVAR_SEC(rifle, reload))
+                                                       WEP_ACTION(self.weapon, WR_RELOAD);
+                                               else
+                                               {
+                                                       if (weapon_prepareattack_check(1, WEP_CVAR_SEC(rifle, refire)))
+                                                       if (time >= self.rifle_accumulator + WEP_CVAR_SEC(rifle, burstcost))
+                                                       {
+                                                               weapon_prepareattack_do(1, WEP_CVAR_SEC(rifle, refire));
+                                                               W_Rifle_BulletHail(WEP_CVAR_SEC(rifle, bullethail), W_Rifle_Attack2, WFRAME_FIRE2, WEP_CVAR_SEC(rifle, animtime), WEP_CVAR_PRI(rifle, refire));
+                                                               self.rifle_accumulator += WEP_CVAR_SEC(rifle, burstcost);
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_model ("models/weapons/g_campingrifle.md3");
+                       precache_model ("models/weapons/v_campingrifle.md3");
+                       precache_model ("models/weapons/h_campingrifle.iqm");
+                       precache_sound ("weapons/campingrifle_fire.wav");
+                       precache_sound ("weapons/campingrifle_fire2.wav");
+                       WEP_SET_PROPS(RIFLE_SETTINGS(rifle), WEP_RIFLE)
+                       return TRUE;
+               }
+               case WR_SETUP:
+               {
+                       self.current_ammo = ammo_nails;
+                       return TRUE;
+               }
+               case WR_CHECKAMMO1:
+               {
+                       ammo_amount = self.ammo_nails >= WEP_CVAR_PRI(rifle, ammo);
+                       ammo_amount += self.(weapon_load[WEP_RIFLE]) >= WEP_CVAR_PRI(rifle, ammo);
+                       return ammo_amount;
+               }
+               case WR_CHECKAMMO2:
+               {
+                       ammo_amount = self.ammo_nails >= WEP_CVAR_SEC(rifle, ammo);
+                       ammo_amount += self.(weapon_load[WEP_RIFLE]) >= WEP_CVAR_SEC(rifle, ammo);
+                       return ammo_amount;
+               }
+               case WR_CONFIG:
+               {
+                       WEP_CONFIG_SETTINGS(RIFLE_SETTINGS(rifle))
+                       return TRUE;
+               }
+               case WR_RESETPLAYER:
+               {
+                       self.rifle_accumulator = time - WEP_CVAR(rifle, bursttime);
+                       return TRUE;
+               }
+               case WR_RELOAD:
+               {
+                       W_Reload(min(WEP_CVAR_PRI(rifle, ammo), WEP_CVAR_SEC(rifle, ammo)), "weapons/reload.wav");
+                       return TRUE;
+               }
+               case WR_SUICIDEMESSAGE:
+               {
+                       return WEAPON_THINKING_WITH_PORTALS;
+               }
+               case WR_KILLMESSAGE:
+               {
+                       if(w_deathtype & HITTYPE_SECONDARY)
+                       {
+                               if(w_deathtype & HITTYPE_BOUNCE)
+                                       return WEAPON_RIFLE_MURDER_HAIL_PIERCING;
+                               else
+                                       return WEAPON_RIFLE_MURDER_HAIL;
+                       }
+                       else
+                       {
+                               if(w_deathtype & HITTYPE_BOUNCE)
+                                       return WEAPON_RIFLE_MURDER_PIERCING;
+                               else
+                                       return WEAPON_RIFLE_MURDER;
+                       }
+               }
+       }
+       return TRUE;
+}
+#endif
+#ifdef CSQC
+float w_rifle(float req)
+{
+       switch(req)
+       {
+               case WR_IMPACTEFFECT:
+               {
+                       vector org2;
+                       org2 = w_org + w_backoff * 2;
+                       pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
+                       if(!w_issilent)
+                       {
+                               if(w_random < 0.2)
+                                       sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTN_NORM);
+                               else if(w_random < 0.4)
+                                       sound(self, CH_SHOTS, "weapons/ric2.wav", VOL_BASE, ATTN_NORM);
+                               else if(w_random < 0.5)
+                                       sound(self, CH_SHOTS, "weapons/ric3.wav", VOL_BASE, ATTN_NORM);
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_sound("weapons/ric1.wav");
+                       precache_sound("weapons/ric2.wav");
+                       precache_sound("weapons/ric3.wav");
+                       return TRUE;
+               }
+       }
+
+       return TRUE;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/w_seeker.qc b/qcsrc/common/weapons/w_seeker.qc
new file mode 100644 (file)
index 0000000..5a8f8d5
--- /dev/null
@@ -0,0 +1,775 @@
+#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/tag_impact.wav", 1, ATTEN_NORM);
+                               }
+                               else
+                               {
+                                       pointparticles(particleeffectnum("hagar_explode"), org2, '0 0 0', 1);
+                                       if(!w_issilent)
+                                       {
+                                               if (w_random<0.15)
+                                                       sound(self, CH_SHOTS, "weapons/tagexp1.wav", 1, ATTEN_NORM);
+                                               else if (w_random<0.7)
+                                                       sound(self, CH_SHOTS, "weapons/tagexp2.wav", 1, ATTEN_NORM);
+                                               else
+                                                       sound(self, CH_SHOTS, "weapons/tagexp3.wav", 1, ATTEN_NORM);
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               pointparticles(particleeffectnum("hagar_explode"), org2, '0 0 0', 1);
+                               if(!w_issilent)
+                               {
+                                       if (w_random<0.15)
+                                               sound(self, CH_SHOTS, "weapons/seekerexp1.wav", 1, ATTEN_NORM);
+                                       else if (w_random<0.7)
+                                               sound(self, CH_SHOTS, "weapons/seekerexp2.wav", 1, ATTEN_NORM);
+                                       else
+                                               sound(self, CH_SHOTS, "weapons/seekerexp3.wav", 1, ATTEN_NORM);
+                               }
+                       }
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_sound("weapons/seekerexp1.wav");
+                       precache_sound("weapons/seekerexp2.wav");
+                       precache_sound("weapons/seekerexp3.wav");
+                       precache_sound("weapons/tagexp1.wav");
+                       precache_sound("weapons/tagexp2.wav");
+                       precache_sound("weapons/tagexp3.wav");
+                       precache_sound("weapons/tag_impact.wav");
+                       return TRUE;
+               }
+       }
+
+       return TRUE;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/w_shockwave.qc b/qcsrc/common/weapons/w_shockwave.qc
new file mode 100644 (file)
index 0000000..7ac25f2
--- /dev/null
@@ -0,0 +1,538 @@
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id */ SHOCKWAVE,
+/* function */ W_Shockwave,
+/* ammotype */ IT_SHELLS,
+/* impulse  */ 2,
+/* flags    */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN,
+/* rating   */ BOT_PICKUP_RATING_LOW,
+/* model    */ "shotgun",
+/* netname  */ "shockwave",
+/* fullname */ _("Shockwave")
+);
+#else
+#ifdef SVQC
+void spawnfunc_weapon_shockwave()
+{
+       if(autocvar_sv_q3acompat_machineshockwaveswap)
+       if(self.classname != "droppedweapon")
+       {
+               weapon_defaultspawnfunc(WEP_UZI);
+               return;
+       }
+       weapon_defaultspawnfunc(WEP_SHOCKWAVE);
+}
+.float swing_prev;
+.entity swing_alreadyhit;
+.float shockwave_pelletstime;
+entity shockwave_hit[32];
+float shockwave_hit_damage[32];
+vector shockwave_hit_force[32];
+
+// LEGACY ATTACK MODE: Scattered bullets
+void W_Shockwave_Pellets(void)
+{
+       float sc;
+       float ammoamount;
+       float bullets;
+       float d;
+       float f;
+       float spread;
+       float bulletspeed;
+       float bulletconstant;
+       entity flash;
+
+       ammoamount = autocvar_g_balance_shockwave_pellets_ammo;
+       bullets = autocvar_g_balance_shockwave_pellets_bullets;
+       d = autocvar_g_balance_shockwave_pellets_damage;
+       f = autocvar_g_balance_shockwave_pellets_force;
+       spread = autocvar_g_balance_shockwave_pellets_spread;
+       bulletspeed = autocvar_g_balance_shockwave_pellets_speed;
+       bulletconstant = autocvar_g_balance_shockwave_pellets_bulletconstant;
+
+       W_DecreaseAmmo(ammo_shells, ammoamount, autocvar_g_balance_shockwave_reload_ammo);
+
+       W_SetupShot (self, autocvar_g_antilag_bullets && bulletspeed >= autocvar_g_antilag_bullets, 5, "weapons/shockwave_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_SHOCKWAVE, 0, 1, bulletconstant);
+       endFireBallisticBullet();
+
+       pointparticles(particleeffectnum("shockwave_muzzleflash"), w_shotorg, w_shotdir * 1000, autocvar_g_balance_shockwave_pellets_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');
+}
+
+// MELEE ATTACK MODE
+void W_Shockwave_Melee_Think()
+{
+       // declarations
+       float i, f, swing, swing_factor, swing_damage, meleetime, is_player;
+       entity target_victim;
+       vector targpos;
+
+       if(!self.cnt) // set start time of melee
+       {
+               self.cnt = time; 
+               W_PlayStrengthSound(self.realowner);
+       }
+
+       makevectors(self.realowner.v_angle); // update values for v_* vectors
+       
+       // calculate swing percentage based on time
+       meleetime = autocvar_g_balance_laser_melee_time * W_WeaponRateFactor();
+       swing = bound(0, (self.cnt + meleetime - time) / meleetime, 10);
+       f = ((1 - swing) * autocvar_g_balance_laser_melee_traces);
+       
+       // check to see if we can still continue, otherwise give up now
+       if((self.realowner.deadflag != DEAD_NO) && autocvar_g_balance_laser_melee_no_doubleslap)
+       {
+               remove(self);
+               return;
+       }
+       
+       // if okay, perform the traces needed for this frame 
+       for(i=self.swing_prev; i < f; ++i)
+       {
+               swing_factor = ((1 - (i / autocvar_g_balance_laser_melee_traces)) * 2 - 1);
+               
+               targpos = (self.realowner.origin + self.realowner.view_ofs 
+                       + (v_forward * autocvar_g_balance_laser_melee_range)
+                       + (v_up * swing_factor * autocvar_g_balance_laser_melee_swing_up)
+                       + (v_right * swing_factor * autocvar_g_balance_laser_melee_swing_side));
+
+               WarpZone_traceline_antilag(self.realowner, self.realowner.origin + self.realowner.view_ofs, targpos, FALSE, self.realowner, ANTILAG_LATENCY(self.realowner));
+               
+               // draw lightning beams for debugging
+               te_lightning2(world, targpos, self.realowner.origin + self.realowner.view_ofs + v_forward * 5 - v_up * 5); 
+               te_customflash(targpos, 40,  2, '1 1 1');
+               
+               is_player = (trace_ent.classname == "player" || trace_ent.classname == "body");
+
+               if((trace_fraction < 1) // if trace is good, apply the damage and remove self
+                       && (trace_ent.takedamage == DAMAGE_AIM)  
+                       && (trace_ent != self.swing_alreadyhit)
+                       && (is_player || autocvar_g_balance_laser_melee_nonplayerdamage))
+               {
+                       target_victim = trace_ent; // so it persists through other calls
+                       
+                       if(is_player) // this allows us to be able to nerf the non-player damage done in e.g. assault or onslaught.
+                               swing_damage = (autocvar_g_balance_laser_melee_damage * min(1, swing_factor + 1));
+                       else
+                               swing_damage = (autocvar_g_balance_laser_melee_nonplayerdamage * min(1, swing_factor + 1));
+                       
+                       //print(strcat(self.realowner.netname, " hitting ", target_victim.netname, " with ", strcat(ftos(swing_damage), " damage (factor: ", ftos(swing_factor), ") at "), ftos(time), " seconds.\n"));
+                       
+                       Damage(target_victim, self.realowner, self.realowner, 
+                               swing_damage, WEP_SHOCKWAVE | HITTYPE_SECONDARY, 
+                               self.realowner.origin + self.realowner.view_ofs, 
+                               v_forward * autocvar_g_balance_laser_melee_force);
+                               
+                       if(accuracy_isgooddamage(self.realowner, target_victim)) { accuracy_add(self.realowner, WEP_SHOCKWAVE, 0, swing_damage); }
+                       
+                       if(autocvar_g_balance_laser_melee_multihit) // allow multiple hits with one swing, but not against the same player twice.
+                       {
+                               self.swing_alreadyhit = target_victim;
+                               continue; // move along to next trace
+                       }
+                       else
+                       {
+                               remove(self);
+                               return;
+                       }
+               }
+       }
+       
+       if(time >= self.cnt + meleetime)
+       {
+               // melee is finished
+               remove(self);
+               return;
+       }
+       else
+       {
+               // set up next frame 
+               self.swing_prev = i;
+               self.nextthink = time;
+       }
+}
+
+void W_Shockwave_Melee()
+{
+       sound(self, CH_WEAPON_A, "weapons/shotgun_melee.wav", VOL_BASE, ATTN_NORM);
+       weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_laser_melee_animtime, w_ready);
+
+       entity meleetemp;
+       meleetemp = spawn();
+       meleetemp.owner = meleetemp.realowner = self;
+       meleetemp.think = W_Shockwave_Melee_Think;
+       meleetemp.nextthink = time + autocvar_g_balance_shockwave_melee_delay * W_WeaponRateFactor();
+       W_SetupShot_Range(self, TRUE, 0, "", 0, autocvar_g_balance_shockwave_melee_damage, autocvar_g_balance_shockwave_melee_range);
+}
+
+// SHOCKWAVE ATTACK MODE
+float W_Shockwave_Attack_CheckSpread(
+       vector targetorg,
+       vector nearest_on_line,
+       vector sw_shotorg,
+       vector attack_endpos)
+{
+       float spreadlimit;
+       float distance_of_attack = vlen(sw_shotorg - attack_endpos);
+       float distance_from_line = vlen(targetorg - nearest_on_line);
+       
+       spreadlimit = (distance_of_attack ? min(1, (vlen(sw_shotorg - nearest_on_line) / distance_of_attack)) : 1);
+       spreadlimit = (autocvar_g_balance_laser_shockwave_spread_min * (1 - spreadlimit) + autocvar_g_balance_laser_shockwave_spread_max * spreadlimit);
+       
+       if(
+               (spreadlimit && (distance_from_line <= spreadlimit))
+               &&
+               ((vlen(normalize(targetorg - sw_shotorg) - normalize(attack_endpos - sw_shotorg)) * RAD2DEG) <= 90)
+       )
+               { return bound(0, (distance_from_line / spreadlimit), 1); }
+       else
+               { return FALSE; }
+}
+
+float W_Shockwave_Attack_IsVisible(
+       entity head,
+       vector nearest_on_line,
+       vector sw_shotorg,
+       vector attack_endpos)
+{
+       vector nearest_to_attacker = head.WarpZone_findradius_nearest;
+       vector center = (head.origin + (head.mins + head.maxs) * 0.5);
+       vector corner;
+       float i;
+
+       // STEP ONE: Check if the nearest point is clear
+       if(W_Shockwave_Attack_CheckSpread(nearest_to_attacker, nearest_on_line, sw_shotorg, attack_endpos))
+       {
+               WarpZone_TraceLine(sw_shotorg, nearest_to_attacker, MOVE_NOMONSTERS, self);
+               if(trace_fraction == 1) { return TRUE; } // yes, the nearest point is clear and we can allow the damage
+       }
+
+       // STEP TWO: Check if shotorg to center point is clear
+       if(W_Shockwave_Attack_CheckSpread(center, nearest_on_line, sw_shotorg, attack_endpos))
+       {
+               WarpZone_TraceLine(sw_shotorg, center, MOVE_NOMONSTERS, self);
+               if(trace_fraction == 1) { return TRUE; } // yes, the center point is clear and we can allow the damage
+       }
+
+       // STEP THREE: Check each corner to see if they are clear
+       for(i=1; i<=8; ++i)
+       {
+               corner = get_corner_position(head, i);
+               if(W_Shockwave_Attack_CheckSpread(corner, nearest_on_line, sw_shotorg, attack_endpos))
+               {
+                       WarpZone_TraceLine(sw_shotorg, corner, MOVE_NOMONSTERS, self);
+                       if(trace_fraction == 1) { return TRUE; } // yes, this corner is clear and we can allow the damage
+               }
+       }
+
+       return FALSE;
+}
+
+float W_Shockwave_Attack_CheckHit(
+       float queue,
+       entity head,
+       vector final_force,
+       float final_damage)
+{
+       if not(head) { return FALSE; }
+       float i;
+
+       ++queue;
+       
+       for(i = 1; i <= queue; ++i)
+       {
+               if(shockwave_hit[i] == head)
+               {
+                       if(vlen(final_force) > vlen(shockwave_hit_force[i])) { shockwave_hit_force[i] = final_force; }
+                       if(final_damage > shockwave_hit_damage[i]) { shockwave_hit_damage[i] = final_damage; }
+                       return FALSE;
+               }
+       }
+
+       shockwave_hit[queue] = head;
+       shockwave_hit_force[queue] = final_force;
+       shockwave_hit_damage[queue] = final_damage;
+       return TRUE;
+}
+
+void W_Shockwave_Attack()
+{
+       // declarations
+       float multiplier, multiplier_from_accuracy, multiplier_from_distance;
+       float final_damage; //, final_spread;
+       vector final_force, center, vel;
+       entity head, next;
+
+       float i, queue = 0;
+       
+       // set up the shot direction
+       W_SetupShot(self, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_shockwave_damage);
+       vector attack_endpos = (w_shotorg + (w_shotdir * autocvar_g_balance_laser_shockwave_distance));
+       WarpZone_TraceLine(w_shotorg, attack_endpos, MOVE_NOMONSTERS, self);
+       vector attack_hitpos = trace_endpos;
+       float distance_to_end = vlen(w_shotorg - attack_endpos);
+       float distance_to_hit = vlen(w_shotorg - attack_hitpos);
+       //entity transform = WarpZone_trace_transform;
+
+       // do the firing effect now
+       SendCSQCShockwaveParticle(attack_endpos);
+       Damage_DamageInfo(attack_hitpos, autocvar_g_balance_laser_shockwave_splash_damage, autocvar_g_balance_laser_shockwave_splash_edgedamage, autocvar_g_balance_laser_shockwave_splash_radius, w_shotdir * autocvar_g_balance_laser_shockwave_splash_force, WEP_SHOCKWAVE, 0, self);
+
+       // splash damage/jumping trace
+       head = WarpZone_FindRadius(attack_hitpos, max(autocvar_g_balance_laser_shockwave_splash_radius, autocvar_g_balance_laser_shockwave_jump_radius), FALSE);
+       while(head)
+       {
+               next = head.chain;
+
+               if(head.takedamage)
+               {
+                       center = PLAYER_CENTER(head);
+
+                       float distance_to_head = vlen(attack_hitpos - head.WarpZone_findradius_nearest);
+                       
+                       if((head == self) && (distance_to_head <= autocvar_g_balance_laser_shockwave_jump_radius))
+                       {
+                               multiplier_from_accuracy = (1 - (distance_to_head ? min(1, (distance_to_head / autocvar_g_balance_laser_shockwave_jump_radius)) : 0));
+                               multiplier_from_distance = (1 - (distance_to_hit ? min(1, (distance_to_hit / distance_to_end)) : 0));
+                               multiplier = max(autocvar_g_balance_laser_shockwave_jump_multiplier_min, ((multiplier_from_accuracy * autocvar_g_balance_laser_shockwave_jump_multiplier_accuracy) + (multiplier_from_distance * autocvar_g_balance_laser_shockwave_jump_multiplier_distance)));
+
+                               final_force = ((normalize(center - attack_hitpos) * autocvar_g_balance_laser_shockwave_jump_force) * multiplier);
+                               vel = head.velocity; vel_z = 0;
+                               vel = normalize(vel) * bound(0, vlen(vel) / autocvar_sv_maxspeed, 1) * autocvar_g_balance_laser_shockwave_jump_force_velocitybias;
+                               final_force = (vlen(final_force) * normalize(normalize(final_force) + vel));
+                               final_force_z *= autocvar_g_balance_laser_shockwave_jump_force_zscale;
+                               final_damage = (autocvar_g_balance_laser_shockwave_jump_damage * multiplier + autocvar_g_balance_laser_shockwave_jump_edgedamage * (1 - multiplier));
+
+                               Damage(head, self, self, final_damage, WEP_SHOCKWAVE, head.origin, final_force);
+                               //print("SELF HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n");
+                       }
+                       else if (distance_to_head <= autocvar_g_balance_laser_shockwave_splash_radius)
+                       {       
+                               multiplier_from_accuracy = (1 - (distance_to_head ? min(1, (distance_to_head / autocvar_g_balance_laser_shockwave_splash_radius)) : 0));
+                               multiplier_from_distance = (1 - (distance_to_hit ? min(1, (distance_to_hit / distance_to_end)) : 0));
+                               multiplier = max(autocvar_g_balance_laser_shockwave_splash_multiplier_min, ((multiplier_from_accuracy * autocvar_g_balance_laser_shockwave_splash_multiplier_accuracy) + (multiplier_from_distance * autocvar_g_balance_laser_shockwave_splash_multiplier_distance)));
+
+                               final_force = normalize(center - (attack_hitpos - (w_shotdir * autocvar_g_balance_laser_shockwave_splash_force_forwardbias)));
+                               //te_lightning2(world, attack_hitpos, (attack_hitpos + (final_force * 200)));
+                               final_force = ((final_force * autocvar_g_balance_laser_shockwave_splash_force) * multiplier);
+                               final_force_z *= autocvar_g_balance_laser_shockwave_force_zscale;
+                               final_damage = (autocvar_g_balance_laser_shockwave_splash_damage * multiplier + autocvar_g_balance_laser_shockwave_splash_edgedamage * (1 - multiplier));
+
+                               if(W_Shockwave_Attack_CheckHit(queue, head, final_force, final_damage)) { ++queue; }
+                               //print("SPLASH HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n");
+                       }
+               }
+               head = next;
+       }
+
+       // cone damage trace
+       head = WarpZone_FindRadius(w_shotorg, autocvar_g_balance_laser_shockwave_distance, FALSE);
+       while(head)
+       {
+               next = head.chain;
+               
+               if((head != self) && head.takedamage)
+               {
+                       // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) 
+                       center = CENTER_OR_VIEWOFS(head);
+
+                       // find the closest point on the enemy to the center of the attack
+                       float ang; // angle between shotdir and h
+                       float h; // hypotenuse, which is the distance between attacker to head
+                       float a; // adjacent side, which is the distance between attacker and the point on w_shotdir that is closest to head.origin
+                       
+                       h = vlen(center - self.origin);
+                       ang = acos(dotproduct(normalize(center - self.origin), w_shotdir));
+                       a = h * cos(ang);
+
+                       vector nearest_on_line = (w_shotorg + a * w_shotdir);
+                       vector nearest_to_attacker = WarpZoneLib_NearestPointOnBox(center + head.mins, center + head.maxs, nearest_on_line);
+                       float distance_to_target = vlen(w_shotorg - nearest_to_attacker); // todo: use the findradius function for this
+
+                       if((distance_to_target <= autocvar_g_balance_laser_shockwave_distance) 
+                               && (W_Shockwave_Attack_IsVisible(head, nearest_on_line, w_shotorg, attack_endpos)))
+                       {
+                               multiplier_from_accuracy = (1 - W_Shockwave_Attack_CheckSpread(nearest_to_attacker, nearest_on_line, w_shotorg, attack_endpos));
+                               multiplier_from_distance = (1 - (distance_to_hit ? min(1, (distance_to_target / distance_to_end)) : 0));
+                               multiplier = max(autocvar_g_balance_laser_shockwave_multiplier_min, ((multiplier_from_accuracy * autocvar_g_balance_laser_shockwave_multiplier_accuracy) + (multiplier_from_distance * autocvar_g_balance_laser_shockwave_multiplier_distance)));
+
+                               final_force = normalize(center - (nearest_on_line - (w_shotdir * autocvar_g_balance_laser_shockwave_force_forwardbias)));
+                               //te_lightning2(world, nearest_on_line, (attack_hitpos + (final_force * 200)));
+                               final_force = ((final_force * autocvar_g_balance_laser_shockwave_force) * multiplier);
+                               final_force_z *= autocvar_g_balance_laser_shockwave_force_zscale;
+                               final_damage = (autocvar_g_balance_laser_shockwave_damage * multiplier + autocvar_g_balance_laser_shockwave_edgedamage * (1 - multiplier));
+
+                               if(W_Shockwave_Attack_CheckHit(queue, head, final_force, final_damage)) { ++queue; }
+                               //print("CONE HIT: multiplier = ", ftos(multiplier), strcat(", damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force))),"... multiplier_from_accuracy = ", ftos(multiplier_from_accuracy), ", multiplier_from_distance = ", ftos(multiplier_from_distance), ".\n");
+                       }
+               }
+               head = next;
+       }
+
+       for(i = 1; i <= queue; ++i)
+       {
+               head = shockwave_hit[i];
+               final_force = shockwave_hit_force[i];
+               final_damage = shockwave_hit_damage[i];
+               
+               Damage(head, self, self, final_damage, WEP_SHOCKWAVE, head.origin, final_force);
+               print("SHOCKWAVE by ", self.netname, ": damage = ", ftos(final_damage), ", force = ", ftos(vlen(final_force)), ".\n");
+               
+               shockwave_hit[i] = world;
+               shockwave_hit_force[i] = '0 0 0';
+               shockwave_hit_damage[i] = 0;
+       }
+       //print("queue was ", ftos(queue), ".\n\n");
+}
+
+float W_Shockwave(float req)
+{
+       float ammo_amount;
+       
+       switch(req)
+       {
+               case WR_AIM:
+               {
+                       if(vlen(self.origin-self.enemy.origin) <= autocvar_g_balance_shockwave_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_shockwave_pellets_speed, 0, 0.001, FALSE);
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_THINK:
+               {
+                       if(autocvar_g_balance_shockwave_reload_ammo && self.clip_load < autocvar_g_balance_shockwave_pellets_ammo) // forced reload
+                       {
+                               // don't force reload an empty shockwave if its melee attack is active
+                               if not(autocvar_g_balance_shockwave_secondary && self.ammo_shells < autocvar_g_balance_shockwave_pellets_ammo)
+                                       WEP_ACTION(self.weapon, WR_RELOAD);
+                       }
+                       else
+                       {
+                               if (self.BUTTON_ATCK)
+                               {
+                                       if (time >= self.shockwave_pelletstime) // handle refire separately so the secondary can be fired straight after a primary
+                                       {
+                                               if(weapon_prepareattack(0, autocvar_g_balance_shockwave_pellets_animtime))
+                                               {
+                                                       W_Shockwave_Attack();
+                                                       self.shockwave_pelletstime = time + autocvar_g_balance_shockwave_pellets_refire * W_WeaponRateFactor();
+                                                       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_shockwave_pellets_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_shockwave_secondary)
+                       if (weapon_prepareattack(1, autocvar_g_balance_shockwave_secondary_refire))
+                       {
+                               // attempt forcing playback of the anim by switching to another anim (that we never play) here...
+                               weapon_thinkf(WFRAME_FIRE1, 0, W_Shockwave_Melee);
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_model ("models/uziflash.md3");
+                       precache_model ("models/weapons/g_shockwave.md3");
+                       precache_model ("models/weapons/v_shockwave.md3");
+                       precache_model ("models/weapons/h_shockwave.iqm");
+                       precache_sound ("misc/itempickup.wav");
+                       precache_sound ("weapons/shockwave_fire.wav");
+                       precache_sound ("weapons/shockwave_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_shockwave_pellets_ammo;
+                       ammo_amount += self.(weapon_load[WEP_SHOCKWAVE]) >= autocvar_g_balance_shockwave_pellets_ammo;
+                       return ammo_amount;
+               }
+               case WR_CHECKAMMO2:
+               {
+                       // melee attack is always available
+                       return TRUE;
+               }
+               case WR_RELOAD:
+               {
+                       W_Reload(autocvar_g_balance_shockwave_pellets_ammo, "weapons/reload.wav");
+                       return TRUE;
+               }
+               case WR_SUICIDEMESSAGE:
+               {
+                       return WEAPON_THINKING_WITH_PORTALS;
+               }
+               case WR_KILLMESSAGE:
+               {
+                       if(w_deathtype & HITTYPE_SECONDARY)
+                               return WEAPON_SHOCKWAVE_MURDER_SLAP;
+                       else
+                               return WEAPON_SHOCKWAVE_MURDER;
+               }
+       }
+       return TRUE;
+}
+#endif
+#ifdef CSQC
+.float prevric;
+float W_Shockwave(float req)
+{
+       switch(req)
+       {
+               case WR_IMPACTEFFECT:
+               {
+                       vector org2;
+                       org2 = w_org + w_backoff * 2;
+                       pointparticles(particleeffectnum("shockwave_impact"), org2, w_backoff * 1000, 1);
+                       if(!w_issilent && time - self.prevric > 0.25)
+                       {
+                               if(w_random < 0.0165)
+                                       sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTEN_NORM);
+                               else if(w_random < 0.033)
+                                       sound(self, CH_SHOTS, "weapons/ric2.wav", VOL_BASE, ATTEN_NORM);
+                               else if(w_random < 0.05)
+                                       sound(self, CH_SHOTS, "weapons/ric3.wav", VOL_BASE, ATTEN_NORM);
+                               self.prevric = time;
+                       }
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_sound("weapons/ric1.wav");
+                       precache_sound("weapons/ric2.wav");
+                       precache_sound("weapons/ric3.wav");
+                       return TRUE;
+               }
+       }
+       return TRUE;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/w_tuba.qc b/qcsrc/common/weapons/w_tuba.qc
new file mode 100644 (file)
index 0000000..cf194da
--- /dev/null
@@ -0,0 +1,491 @@
+#ifdef REGISTER_WEAPON
+REGISTER_WEAPON(
+/* WEP_##id */ TUBA,
+/* function */ w_tuba,
+/* ammotype */ 0,
+/* impulse  */ 1,
+/* flags    */ WEP_FLAG_HIDDEN | WEP_TYPE_SPLASH,
+/* rating   */ BOT_PICKUP_RATING_MID,
+/* model    */ "tuba",
+/* netname  */ "tuba",
+/* xgettext:no-c-format */
+/* fullname  */ _("@!#%'n Tuba")
+);
+
+#define TUBA_SETTINGS(weapon) \
+       WEP_ADD_CVAR(weapon, MO_NONE, animtime) \
+       WEP_ADD_CVAR(weapon, MO_NONE, attenuation) \
+       WEP_ADD_CVAR(weapon, MO_NONE, damage) \
+       WEP_ADD_CVAR(weapon, MO_NONE, edgedamage) \
+       WEP_ADD_CVAR(weapon, MO_NONE, force) \
+       WEP_ADD_CVAR(weapon, MO_NONE, radius) \
+       WEP_ADD_CVAR(weapon, MO_NONE, refire) \
+       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
+TUBA_SETTINGS(tuba)
+.entity tuba_note;
+.float tuba_smoketime;
+.float tuba_instrument;
+
+#define MAX_TUBANOTES 32
+.float tuba_lastnotes_last;
+.float tuba_lastnotes_cnt; // over
+.vector tuba_lastnotes[MAX_TUBANOTES];
+#endif
+#else
+#ifdef SVQC
+void spawnfunc_weapon_tuba (void) { weapon_defaultspawnfunc(WEP_TUBA); }
+
+float W_Tuba_HasPlayed(entity pl, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo)
+{
+       float i, j, mmin, mmax, nolength;
+       float n = tokenize_console(melody);
+       if(n > pl.tuba_lastnotes_cnt)
+               return FALSE;
+       float pitchshift = 0;
+
+       if(instrument >= 0)
+               if(pl.tuba_instrument != instrument)
+                       return FALSE;
+
+       // verify notes...
+       nolength = FALSE;
+       for(i = 0; i < n; ++i)
+       {
+               vector v = pl.(tuba_lastnotes[mod(pl.tuba_lastnotes_last - i + MAX_TUBANOTES, MAX_TUBANOTES)]);
+               float ai = stof(argv(n - i - 1));
+               float np = floor(ai);
+               if(ai == np)
+                       nolength = TRUE;
+               // n counts the last played notes BACKWARDS
+               // _x is start
+               // _y is end
+               // _z is note pitch
+               if(ignorepitch && i == 0)
+               {
+                       pitchshift = np - v_z;
+               }
+               else
+               {
+                       if(v_z + pitchshift != np)
+                               return FALSE;
+               }
+       }
+
+       // now we know the right NOTES were played
+       if(!nolength)
+       {
+               // verify rhythm...
+               float ti = 0;
+               if(maxtempo > 0)
+                       mmin = 240 / maxtempo; // 60 = "0.25 means 1 sec", at 120 0.5 means 1 sec, at 240 1 means 1 sec
+               else
+                       mmin = 0;
+               if(mintempo > 0)
+                       mmax = 240 / mintempo; // 60 = "0.25 means 1 sec", at 120 0.5 means 1 sec, at 240 1 means 1 sec
+               else
+                       mmax = 240; // you won't try THAT hard... (tempo 1)
+               //print(sprintf("initial tempo rules: %f %f\n", mmin, mmax));
+
+               for(i = 0; i < n; ++i)
+               {
+                       vector vi = pl.(tuba_lastnotes[mod(pl.tuba_lastnotes_last - i + MAX_TUBANOTES, MAX_TUBANOTES)]);
+                       float ai = stof(argv(n - i - 1));
+                       ti -= 1 / (ai - floor(ai));
+                       float tj = ti;
+                       for(j = i+1; j < n; ++j)
+                       {
+                               vector vj = pl.(tuba_lastnotes[mod(pl.tuba_lastnotes_last - j + MAX_TUBANOTES, MAX_TUBANOTES)]);
+                               float aj = stof(argv(n - j - 1));
+                               tj -= (aj - floor(aj));
+
+                               // note i should be at m*ti+b
+                               // note j should be at m*tj+b
+                               // so:
+                               // we have a LINE l, so that
+                               // vi_x <= l(ti) <= vi_y
+                               // vj_x <= l(tj) <= vj_y
+                               // what is m?
+
+                               // vi_x <= vi_y <= vj_x <= vj_y
+                               // ti <= tj
+                               //print(sprintf("first note: %f to %f, should be %f\n", vi_x, vi_y, ti));
+                               //print(sprintf("second note: %f to %f, should be %f\n", vj_x, vj_y, tj));
+                               //print(sprintf("m1 = %f\n", (vi_x - vj_y) / (ti - tj)));
+                               //print(sprintf("m2 = %f\n", (vi_y - vj_x) / (ti - tj)));
+                               mmin = max(mmin, (vi_x - vj_y) / (ti - tj)); // lower bound
+                               mmax = min(mmax, (vi_y - vj_x) / (ti - tj)); // upper bound
+                       }
+               }
+
+               if(mmin > mmax) // rhythm fail
+                       return FALSE;
+       }
+
+       pl.tuba_lastnotes_cnt = 0;
+
+       return TRUE;
+}
+
+void W_Tuba_NoteOff()
+{
+       // we have a note:
+       //   on: self.spawnshieldtime
+       //   off: time
+       //   note: self.cnt
+       if(self.owner.tuba_note == self)
+       {
+               self.owner.tuba_lastnotes_last = mod(self.owner.tuba_lastnotes_last + 1, MAX_TUBANOTES);
+               self.owner.(tuba_lastnotes[self.owner.tuba_lastnotes_last]) = eX * self.spawnshieldtime + eY * time + eZ * self.cnt;
+               self.owner.tuba_note = world;
+               self.owner.tuba_lastnotes_cnt = bound(0, self.owner.tuba_lastnotes_cnt + 1, MAX_TUBANOTES);
+
+               string s;
+               s = trigger_magicear_processmessage_forallears(self.owner, 0, world, string_null);
+               if(s != "")
+               {
+                       // simulate a server message
+                       switch(self.tuba_instrument)
+                       {
+                               default:
+                               case 0: // Tuba
+                                       bprint(strcat("\{1}\{13}* ^3", self.owner.netname, "^3 played on the @!#%'n Tuba: ^7", s, "\n"));
+                                       break;
+                               case 1:
+                                       bprint(strcat("\{1}\{13}* ^3", self.owner.netname, "^3 played on the @!#%'n Accordeon: ^7", s, "\n"));
+                                       break;
+                               case 2:
+                                       bprint(strcat("\{1}\{13}* ^3", self.owner.netname, "^3 played on the @!#%'n Klein Bottle: ^7", s, "\n"));
+                                       break;
+                       }
+               }
+       }
+       remove(self);
+}
+
+float Tuba_GetNote(entity pl, float hittype)
+{
+       float note;
+       float movestate;
+       movestate = 5;
+       if(pl.movement_x < 0) movestate -= 3;
+       if(pl.movement_x > 0) movestate += 3;
+       if(pl.movement_y < 0) movestate -= 1;
+       if(pl.movement_y > 0) movestate += 1;
+#ifdef GMQCC
+       note = 0;
+#endif
+       switch(movestate)
+       {
+       // layout: originally I wanted
+       //   eb e  e#=f
+       //   B  c  d
+       //   Gb G  G#
+       // but then you only use forward and right key. So to make things more
+       // interesting, I swapped B with e#. Har har har...
+       //   eb e  B
+       // f=e# c  d
+       //   Gb G  G#
+               case 1: note = -6; break; // Gb
+               case 2: note = -5; break; // G
+               case 3: note = -4; break; // G#
+               case 4: note = +5; break; // e#
+               default:
+               case 5: note =  0; break; // c
+               case 6: note = +2; break; // d
+               case 7: note = +3; break; // eb
+               case 8: note = +4; break; // e
+               case 9: note = -1; break; // B
+       }
+       if(pl.BUTTON_CROUCH)
+               note -= 12;
+       if(pl.BUTTON_JUMP)
+               note += 12;
+       if(hittype & HITTYPE_SECONDARY)
+               note += 7;
+
+       // we support two kinds of tubas, those tuned in Eb and those tuned in C
+       // kind of tuba currently is player slot number, or team number if in
+       // teamplay
+       // that way, holes in the range of notes are "plugged"
+       if(teamplay)
+       {
+               if(pl.team == NUM_TEAM_2 || pl.team == NUM_TEAM_4)
+                       note += 3;
+       }
+       else
+       {
+               if(pl.clientcolors & 1)
+                       note += 3;
+       }
+
+       // total range of notes:
+       //                       0
+       //                 ***  ** ****
+       //                        ***  ** ****
+       //     ***  ** ****
+       //            ***  ** ****
+       //     ***  ********************* ****
+       //     -18.........................+12
+       //        ***  ********************* ****
+       //     -18............................+15
+       //     with jump: ... +24
+       //     ... +27
+       return note;
+}
+
+float W_Tuba_NoteSendEntity(entity to, float sf)
+{
+       float f;
+
+       msg_entity = to;
+       if(!sound_allowed(MSG_ONE, self.realowner))
+               return FALSE;
+
+       WriteByte(MSG_ENTITY, ENT_CLIENT_TUBANOTE);
+       WriteByte(MSG_ENTITY, sf);
+       if(sf & 1)
+       {
+               WriteChar(MSG_ENTITY, self.cnt);
+               f = 0;
+               if(self.realowner != to)
+                       f |= 1;
+               f |= 2 * self.tuba_instrument;
+               WriteByte(MSG_ENTITY, f);
+       }
+       if(sf & 2)
+       {
+               WriteCoord(MSG_ENTITY, self.origin_x);
+               WriteCoord(MSG_ENTITY, self.origin_y);
+               WriteCoord(MSG_ENTITY, self.origin_z);
+       }
+       return TRUE;
+}
+
+void W_Tuba_NoteThink()
+{
+       float dist_mult;
+       float vol0, vol1;
+       vector dir0, dir1;
+       vector v;
+       entity e;
+       if(time > self.teleport_time)
+       {
+               W_Tuba_NoteOff();
+               return;
+       }
+       self.nextthink = time;
+       dist_mult = WEP_CVAR(tuba, attenuation) / autocvar_snd_soundradius;
+       FOR_EACH_REALCLIENT(e)
+       if(e != self.realowner)
+       {
+               v = self.origin - (e.origin + e.view_ofs);
+               vol0 = max(0, 1 - vlen(v) * dist_mult);
+               dir0 = normalize(v);
+               v = self.realowner.origin - (e.origin + e.view_ofs);
+               vol1 = max(0, 1 - vlen(v) * dist_mult);
+               dir1 = normalize(v);
+               if(fabs(vol0 - vol1) > 0.005) // 0.5 percent change in volume
+               {
+                       setorigin(self, self.realowner.origin);
+                       self.SendFlags |= 2;
+                       break;
+               }
+               if(dir0 * dir1 < 0.9994) // 2 degrees change in angle
+               {
+                       setorigin(self, self.realowner.origin);
+                       self.SendFlags |= 2;
+                       break;
+               }
+       }
+}
+
+void W_Tuba_NoteOn(float hittype)
+{
+       vector o;
+       float n;
+
+       W_SetupShot(self, FALSE, 2, "", 0, WEP_CVAR(tuba, damage));
+
+       n = Tuba_GetNote(self, hittype);
+
+       hittype = 0;
+       if(self.tuba_instrument & 1)
+               hittype |= HITTYPE_SECONDARY;
+       if(self.tuba_instrument & 2)
+               hittype |= HITTYPE_BOUNCE;
+
+       if(self.tuba_note)
+       {
+               if(self.tuba_note.cnt != n || self.tuba_note.tuba_instrument != self.tuba_instrument)
+               {
+                       entity oldself = self;
+                       self = self.tuba_note;
+                       W_Tuba_NoteOff();
+                       self = oldself;
+               }
+       }
+
+       if (!self.tuba_note)
+       {
+               self.tuba_note = spawn();
+               self.tuba_note.owner = self.tuba_note.realowner = self;
+               self.tuba_note.cnt = n;
+               self.tuba_note.tuba_instrument = self.tuba_instrument;
+               self.tuba_note.think = W_Tuba_NoteThink;
+               self.tuba_note.nextthink = time;
+               self.tuba_note.spawnshieldtime = time;
+               Net_LinkEntity(self.tuba_note, FALSE, 0, W_Tuba_NoteSendEntity);
+       }
+
+       self.tuba_note.teleport_time = time + WEP_CVAR(tuba, refire) * 2 * W_WeaponRateFactor(); // so it can get prolonged safely
+
+       //sound(self, c, TUBA_NOTE(n), bound(0, VOL_BASE * cvar("g_balance_tuba_volume"), 1), autocvar_g_balance_tuba_attenuation);
+       RadiusDamage(self, self, WEP_CVAR(tuba, damage), WEP_CVAR(tuba, edgedamage), WEP_CVAR(tuba, radius), world, world, WEP_CVAR(tuba, force), hittype | WEP_TUBA, world);
+
+       o = gettaginfo(self.exteriorweaponentity, 0);
+       if(time > self.tuba_smoketime)
+       {
+               pointparticles(particleeffectnum("smoke_ring"), o + v_up * 45 + v_right * -6 + v_forward * 8, v_up * 100, 1);
+               self.tuba_smoketime = time + 0.25;
+       }
+}
+
+float w_tuba(float req)
+{
+       switch(req)
+       {
+               case WR_AIM:
+               {
+                       // bots cannot play the Tuba well yet
+                       // I think they should start with the recorder first
+                       if(vlen(self.origin - self.enemy.origin) < WEP_CVAR(tuba, radius))
+                       {
+                               if(random() > 0.5)
+                                       self.BUTTON_ATCK = 1;
+                               else
+                                       self.BUTTON_ATCK2 = 1;
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_THINK:
+               {
+                       if (self.BUTTON_ATCK)
+                       if (weapon_prepareattack(0, WEP_CVAR(tuba, refire)))
+                       {
+                               W_Tuba_NoteOn(0);
+                               //weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_tuba_animtime, w_ready);
+                               weapon_thinkf(WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready);
+                       }
+                       if (self.BUTTON_ATCK2)
+                       if (weapon_prepareattack(1, WEP_CVAR(tuba, refire)))
+                       {
+                               W_Tuba_NoteOn(HITTYPE_SECONDARY);
+                               //weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_tuba_animtime, w_ready);
+                               weapon_thinkf(WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready);
+                       }
+                       if(self.tuba_note)
+                       {
+                               if(!self.BUTTON_ATCK && !self.BUTTON_ATCK2)
+                               {
+                                       entity oldself = self;
+                                       self = self.tuba_note;
+                                       W_Tuba_NoteOff();
+                                       self = oldself;
+                               }
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_INIT:
+               {
+                       precache_model ("models/weapons/g_tuba.md3");
+                       precache_model ("models/weapons/v_tuba.md3");
+                       precache_model ("models/weapons/h_tuba.iqm");
+                       precache_model ("models/weapons/v_akordeon.md3");
+                       precache_model ("models/weapons/h_akordeon.iqm");
+                       precache_model ("models/weapons/v_kleinbottle.md3");
+                       precache_model ("models/weapons/h_kleinbottle.iqm");
+                       WEP_SET_PROPS(TUBA_SETTINGS(tuba), WEP_TUBA)
+                       return TRUE;
+               }
+               case WR_SETUP:
+               {
+                       self.current_ammo = ammo_none;
+                       self.tuba_instrument = 0;
+                       return TRUE;
+               }
+               case WR_RELOAD:
+               {
+                       // switch to alternate instruments :)
+                       if(self.weaponentity.state == WS_READY)
+                       {
+                               switch(self.tuba_instrument)
+                               {
+                                       case 0:
+                                               self.tuba_instrument = 1;
+                                               self.weaponname = "akordeon";
+                                               break;
+                                       case 1:
+                                               self.tuba_instrument = 2;
+                                               self.weaponname = "kleinbottle";
+                                               break;
+                                       case 2:
+                                               self.tuba_instrument = 0;
+                                               self.weaponname = "tuba";
+                                               break;
+                               }
+                               W_SetupShot(self, FALSE, 0, "", 0, 0);
+                               pointparticles(particleeffectnum("teleport"), w_shotorg, '0 0 0', 1);
+                               self.weaponentity.state = WS_INUSE;
+                               weapon_thinkf(WFRAME_RELOAD, 0.5, w_ready);
+                       }
+                       
+                       return TRUE;
+               }
+               case WR_CHECKAMMO1:
+               case WR_CHECKAMMO2:
+               {
+                       return TRUE; // tuba has infinite ammo
+               }
+               case WR_CONFIG:
+               {
+                       WEP_CONFIG_SETTINGS(TUBA_SETTINGS(tuba))
+                       return TRUE;
+               }
+               case WR_SUICIDEMESSAGE:
+               {
+                       if(w_deathtype & HITTYPE_BOUNCE)
+                               return WEAPON_KLEINBOTTLE_SUICIDE;
+                       else if(w_deathtype & HITTYPE_SECONDARY)
+                               return WEAPON_ACCORDEON_SUICIDE;
+                       else
+                               return WEAPON_TUBA_SUICIDE;
+               }
+               case WR_KILLMESSAGE:
+               {
+                       if(w_deathtype & HITTYPE_BOUNCE)
+                               return WEAPON_KLEINBOTTLE_MURDER;
+                       else if(w_deathtype & HITTYPE_SECONDARY)
+                               return WEAPON_ACCORDEON_MURDER;
+                       else
+                               return WEAPON_TUBA_MURDER;
+               }
+       }
+       return TRUE;
+}
+#endif
+#ifdef CSQC
+float w_tuba(float req)
+{
+       // nothing to do here; particles of tuba are handled differently
+
+       return TRUE;
+}
+#endif
+#endif
diff --git a/qcsrc/common/weapons/weapons.qc b/qcsrc/common/weapons/weapons.qc
new file mode 100644 (file)
index 0000000..2f9dd2c
--- /dev/null
@@ -0,0 +1,260 @@
+#ifndef MENUQC
+#include "calculations.qc"
+#endif
+#include "all.qh"
+
+// WEAPON PLUGIN SYSTEM
+entity weapon_info[WEP_MAXCOUNT];
+entity dummy_weapon_info;
+
+#if WEP_MAXCOUNT > 72
+# error Kein Weltraum links auf dem Gerät
+#endif
+
+WepSet WepSet_FromWeapon(float a) {
+       a -= WEP_FIRST;
+#if WEP_MAXCOUNT > 24
+       if(a >= 24) {
+               a -= 24;
+#if WEP_MAXCOUNT > 48
+               if(a >= 24) {
+                       a -= 24;
+                       return '0 0 1' * power2of(a);
+               }
+#endif
+               return '0 1 0' * power2of(a);
+       }
+#endif
+       return '1 0 0' * power2of(a);
+}
+#ifdef SVQC
+void WepSet_AddStat()
+{
+       addstat(STAT_WEAPONS, AS_INT, weapons_x);
+#if WEP_MAXCOUNT > 24
+       addstat(STAT_WEAPONS2, AS_INT, weapons_y);
+#if WEP_MAXCOUNT > 48
+       addstat(STAT_WEAPONS3, AS_INT, weapons_z);
+#endif
+#endif
+}
+void WriteWepSet(float dst, WepSet w)
+{
+#if WEP_MAXCOUNT > 48
+       WriteInt72_t(dst, w);
+#elif WEP_MAXCOUNT > 24
+       WriteInt48_t(dst, w);
+#else
+       WriteInt24_t(dst, w_x);
+#endif
+}
+#endif
+#ifdef CSQC
+WepSet WepSet_GetFromStat()
+{
+       WepSet w = '0 0 0';
+       w_x = getstati(STAT_WEAPONS);
+#if WEP_MAXCOUNT > 24
+       w_y = getstati(STAT_WEAPONS2);
+#if WEP_MAXCOUNT > 48
+       w_z = getstati(STAT_WEAPONS3);
+#endif
+#endif
+       return w;
+}
+WepSet ReadWepSet()
+{
+#if WEP_MAXCOUNT > 48
+       return ReadInt72_t();
+#elif WEP_MAXCOUNT > 24
+       return ReadInt48_t();
+#else
+       return ReadInt24_t() * '1 0 0';
+#endif
+}
+#endif
+
+void register_weapon(float id, WepSet bit, float(float) func, float ammotype, float i, float weapontype, float pickupbasevalue, string modelname, string shortname, string wname)
+{
+       entity e;
+       weapon_info[id - 1] = e = spawn();
+       e.classname = "weapon_info";
+       e.weapon = id;
+       e.weapons = bit;
+       e.netname = shortname;
+       e.message = wname;
+       e.items = ammotype;
+       e.weapon_func = func;
+       e.mdl = modelname;
+       e.model = strzone(strcat("models/weapons/g_", modelname, ".md3"));
+       e.spawnflags = weapontype;
+       e.model2 = strzone(strcat("wpn-", e.mdl));
+       e.impulse = i;
+       e.bot_pickupbasevalue = pickupbasevalue;
+       if(ammotype & IT_SHELLS)
+               e.ammo_field = ammo_shells;
+       else if(ammotype & IT_NAILS)
+               e.ammo_field = ammo_nails;
+       else if(ammotype & IT_ROCKETS)
+               e.ammo_field = ammo_rockets;
+       else if(ammotype & IT_CELLS)
+               e.ammo_field = ammo_cells;
+       else if(ammotype & IT_FUEL)
+               e.ammo_field = ammo_fuel;
+       else
+               e.ammo_field = ammo_batteries;
+
+       #ifndef MENUQC
+       func(WR_INIT);
+       #endif
+}
+float w_null(float dummy)
+{
+       return 0;
+}
+void register_weapons_done()
+{
+       dummy_weapon_info = spawn();
+       dummy_weapon_info.classname = "weapon_info";
+       dummy_weapon_info.weapon = 0; // you can recognize dummies by this
+       dummy_weapon_info.weapons = '0 0 0';
+       dummy_weapon_info.netname = "";
+       dummy_weapon_info.message = "AOL CD Thrower";
+       dummy_weapon_info.items = 0;
+       dummy_weapon_info.weapon_func = w_null;
+       dummy_weapon_info.mdl = "";
+       dummy_weapon_info.model = "";
+       dummy_weapon_info.spawnflags = 0;
+       dummy_weapon_info.model2 = "";
+       dummy_weapon_info.impulse = -1;
+       dummy_weapon_info.bot_pickupbasevalue = 0;
+
+       float i;
+       weaponorder_byid = "";
+       for(i = WEP_MAXCOUNT; i >= 1; --i)
+               if(weapon_info[i-1])
+                       weaponorder_byid = strcat(weaponorder_byid, " ", ftos(i));
+       weaponorder_byid = strzone(substring(weaponorder_byid, 1, strlen(weaponorder_byid) - 1));
+}
+entity get_weaponinfo(float id)
+{
+       entity w;
+       if(id < WEP_FIRST || id > WEP_LAST)
+               return dummy_weapon_info;
+       w = weapon_info[id - 1];
+       if(w)
+               return w;
+       return dummy_weapon_info;
+}
+string W_FixWeaponOrder(string order, float complete)
+{
+       return fixPriorityList(order, WEP_FIRST, WEP_LAST, 230 - WEP_FIRST, complete);
+}
+string W_NameWeaponOrder_MapFunc(string s)
+{
+       entity wi;
+       if(s == "0" || stof(s))
+       {
+               wi = get_weaponinfo(stof(s));
+               if(wi != dummy_weapon_info)
+                       return wi.netname;
+       }
+       return s;
+}
+string W_NameWeaponOrder(string order)
+{
+       return mapPriorityList(order, W_NameWeaponOrder_MapFunc);
+}
+string W_NumberWeaponOrder_MapFunc(string s)
+{
+       float i;
+       if(s == "0" || stof(s))
+               return s;
+       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+               if(s == get_weaponinfo(i).netname)
+                       return ftos(i);
+       return s;
+}
+string W_NumberWeaponOrder(string order)
+{
+       return mapPriorityList(order, W_NumberWeaponOrder_MapFunc);
+}
+
+float W_FixWeaponOrder_BuildImpulseList_buf[WEP_MAXCOUNT];
+string W_FixWeaponOrder_BuildImpulseList_order;
+void W_FixWeaponOrder_BuildImpulseList_swap(float i, float j, entity pass)
+{
+       float h;
+       h = W_FixWeaponOrder_BuildImpulseList_buf[i];
+       W_FixWeaponOrder_BuildImpulseList_buf[i] = W_FixWeaponOrder_BuildImpulseList_buf[j];
+       W_FixWeaponOrder_BuildImpulseList_buf[j] = h;
+}
+float W_FixWeaponOrder_BuildImpulseList_cmp(float i, float j, entity pass)
+{
+       entity e1, e2;
+       float d;
+       e1 = get_weaponinfo(W_FixWeaponOrder_BuildImpulseList_buf[i]);
+       e2 = get_weaponinfo(W_FixWeaponOrder_BuildImpulseList_buf[j]);
+       d = mod(e1.impulse + 9, 10) - mod(e2.impulse + 9, 10);
+       if(d != 0)
+               return -d; // high impulse first!
+       return
+               strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "), sprintf(" %d ", W_FixWeaponOrder_BuildImpulseList_buf[i]), 0)
+               -
+               strstrofs(strcat(" ", W_FixWeaponOrder_BuildImpulseList_order, " "), sprintf(" %d ", W_FixWeaponOrder_BuildImpulseList_buf[j]), 0)
+               ; // low char index first!
+}
+string W_FixWeaponOrder_BuildImpulseList(string o)
+{
+       float i;
+       W_FixWeaponOrder_BuildImpulseList_order = o;
+       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+               W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST] = i;
+       heapsort(WEP_LAST - WEP_FIRST + 1, W_FixWeaponOrder_BuildImpulseList_swap, W_FixWeaponOrder_BuildImpulseList_cmp, world);
+       o = "";
+       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+               o = strcat(o, " ", ftos(W_FixWeaponOrder_BuildImpulseList_buf[i - WEP_FIRST]));
+       W_FixWeaponOrder_BuildImpulseList_order = string_null;
+       return substring(o, 1, -1);
+}
+
+string W_FixWeaponOrder_AllowIncomplete(string order)
+{
+       return W_FixWeaponOrder(order, 0);
+}
+
+string W_FixWeaponOrder_ForceComplete(string order)
+{
+       if(order == "")
+               order = W_NumberWeaponOrder(cvar_defstring("cl_weaponpriority"));
+       return W_FixWeaponOrder(order, 1);
+}
+
+void W_RandomWeapons(entity e, float n)
+{
+       float i, j;
+       WepSet remaining;
+       WepSet result;
+       remaining = e.weapons;
+       result = '0 0 0';
+       for(i = 0; i < n; ++i)
+       {
+               RandomSelection_Init();
+               for(j = WEP_FIRST; j <= WEP_LAST; ++j)
+                       if(remaining & WepSet_FromWeapon(j))
+                               RandomSelection_Add(world, j, string_null, 1, 1);
+               result |= WepSet_FromWeapon(RandomSelection_chosen_float);
+               remaining &= ~WepSet_FromWeapon(RandomSelection_chosen_float);
+       }
+       e.weapons = result;
+}
+
+string W_Name(float weaponid)
+{
+       return (get_weaponinfo(weaponid)).message;
+}
+
+float W_AmmoItemCode(float wpn)
+{
+       return (get_weaponinfo(wpn)).items & IT_AMMO;
+}
diff --git a/qcsrc/common/weapons/weapons.qh b/qcsrc/common/weapons/weapons.qh
new file mode 100644 (file)
index 0000000..75a1a67
--- /dev/null
@@ -0,0 +1,238 @@
+#ifndef MENUQC
+#include "calculations.qh"
+#endif
+
+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
+const float    IT_UNLIMITED_WEAPON_AMMO     = 1;
+// when this bit is set, using a weapon does not reduce ammo. Checkpoints can give this powerup.
+const float    IT_UNLIMITED_SUPERWEAPONS    = 2;
+// when this bit is set, superweapons don't expire. Checkpoints can give this powerup.
+const float   IT_CTF_SHIELDED              = 4; // set for the flag shield
+const float   IT_USING_JETPACK             = 8; // confirmation that button is pressed
+const float   IT_JETPACK                   = 16; // actual item
+const float   IT_FUEL_REGEN                = 32; // fuel regeneration trigger
+WANT_CONST float   IT_SHELLS                    = 256;
+WANT_CONST float   IT_NAILS                     = 512;
+WANT_CONST float   IT_ROCKETS                   = 1024;
+WANT_CONST float   IT_CELLS                     = 2048;
+const float   IT_SUPERWEAPON               = 4096;
+const float   IT_FUEL                      = 128;
+const float   IT_STRENGTH                  = 8192;
+const float   IT_INVINCIBLE                = 16384;
+const float   IT_HEALTH                    = 32768;
+// union:
+       // for items:
+       WANT_CONST float        IT_KEY1                                 = 131072;
+       WANT_CONST float        IT_KEY2                                 = 262144;
+       // for players:
+       const float     IT_RED_FLAG_TAKEN               = 32768;
+       const float     IT_RED_FLAG_LOST                = 65536;
+       const float     IT_RED_FLAG_CARRYING            = 98304;
+       const float     IT_BLUE_FLAG_TAKEN              = 131072;
+       const float     IT_BLUE_FLAG_LOST               = 262144;
+       const float     IT_BLUE_FLAG_CARRYING   = 393216;
+// end
+const float   IT_5HP                       = 524288;
+const float   IT_25HP                      = 1048576;
+const float   IT_ARMOR_SHARD               = 2097152;
+const float   IT_ARMOR                     = 4194304;
+
+const float   IT_AMMO                      = 3968; // IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS | IT_FUEL;
+const float   IT_PICKUPMASK                = 51; // IT_FUEL_REGEN | IT_JETPACK | IT_UNLIMITED_AMMO; // strength and invincible are handled separately
+const float   IT_UNLIMITED_AMMO            = 3; // IT_UNLIMITED_SUPERWEAPONS | IT_UNLIMITED_WEAPON_AMMO;
+
+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
+
+// other useful macros
+#define WEP_ACTION(wpn,wrequest) (get_weaponinfo(wpn)).weapon_func(wrequest)
+
+// =====================
+//  Weapon Registration
+// =====================
+
+float w_null(float dummy);
+void register_weapon(float id, WepSet bit, float(float) func, float ammotype, float i, float weapontype, float pickupbasevalue, string modelname, string shortname, string wname);
+void register_weapons_done();
+
+// note: the fabs call is just there to hide "if result is constant" warning
+#define REGISTER_WEAPON_2(id,bit,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \
+       float id; \
+       WepSet bit; \
+       float func(float); \
+       void RegisterWeapons_##id() \
+       { \
+               WEP_LAST = (id = WEP_FIRST + WEP_COUNT); \
+               bit = WepSet_FromWeapon(id); \
+               WEPSET_ALL |= bit; \
+               if((weapontype) & WEP_FLAG_SUPERWEAPON) \
+                       WEPSET_SUPERWEAPONS |= bit; \
+               ++WEP_COUNT; \
+               register_weapon(id,bit,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname); \
+       } \
+       ACCUMULATE_FUNCTION(RegisterWeapons, RegisterWeapons_##id)
+#ifdef MENUQC
+#define REGISTER_WEAPON(id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \
+       REGISTER_WEAPON_2(WEP_##id,WEPSET_##id,w_null,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname)
+#else
+#define REGISTER_WEAPON(id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname) \
+       REGISTER_WEAPON_2(WEP_##id,WEPSET_##id,func,ammotype,i,weapontype,pickupbasevalue,modelname,shortname,wname)
+#endif
+
+#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 4900b9d96bb52db193c8aaf82771634c94279755..61e3ecba8d3aa8e522650856ad7bb2e3e55b3d19 100644 (file)
@@ -18,7 +18,7 @@ oo/base.h
 ../common/constants.qh
 ../common/mapinfo.qh
 ../common/campaign_common.qh
-../common/items.qh
+../common/weapons/weapons.qh // TODO
 ../common/counting.qh
 ../common/command/markup.qh
 ../common/command/rpn.qh
@@ -50,7 +50,7 @@ xonotic/util.qc
 ../common/campaign_file.qc
 ../common/campaign_setup.qc
 ../common/mapinfo.qc
-../common/items.qc
+../common/weapons/weapons.qc // TODO
 ../common/urllib.qc
 
 ../warpzonelib/mathlib.qc
diff --git a/qcsrc/server/accuracy.qc b/qcsrc/server/accuracy.qc
deleted file mode 100644 (file)
index aca64a4..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-.float accuracy_hit[WEP_MAXCOUNT];
-.float accuracy_fired[WEP_MAXCOUNT];
-.float accuracy_cnt_hit[WEP_MAXCOUNT];
-.float accuracy_cnt_fired[WEP_MAXCOUNT];
-
-float accuracy_byte(float n, float d)
-{
-       //print(sprintf("accuracy: %d / %d\n", n, d));
-       if(n <= 0)
-               return 0;
-       if(n > d)
-               return 255;
-       return 1 + rint(n * 100.0 / d);
-}
-
-float accuracy_send(entity to, float sf)
-{
-       float w, f;
-       entity a;
-       WriteByte(MSG_ENTITY, ENT_CLIENT_ACCURACY);
-
-       a = self.owner;
-       if(IS_SPEC(a))
-               a = a.enemy;
-       a = a.accuracy;
-
-       if(to != a.owner)
-               if (!(self.owner.cvar_cl_accuracy_data_share && autocvar_sv_accuracy_data_share))
-                       sf = 0;
-       // note: zero sendflags can never be sent... so we can use that to say that we send no accuracy!
-       WriteInt24_t(MSG_ENTITY, sf);
-       if(sf == 0)
-               return TRUE;
-       // note: we know that client and server agree about SendFlags...
-       for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w)
-       {
-               if(sf & f)
-                       WriteByte(MSG_ENTITY, accuracy_byte(self.(accuracy_hit[w]), self.(accuracy_fired[w])));
-               if(f == 0x800000)
-                       f = 1;
-               else
-                       f *= 2;
-       }
-       return TRUE;
-}
-
-// init/free
-void accuracy_init(entity e)
-{
-       e.accuracy = spawn();
-       e.accuracy.owner = e;
-       e.accuracy.classname = "accuracy";
-       e.accuracy.drawonlytoclient = e;
-       Net_LinkEntity(e.accuracy, FALSE, 0, accuracy_send);
-}
-
-void accuracy_free(entity e)
-{
-       remove(e.accuracy);
-}
-
-// force a resend of a player's accuracy stats
-void accuracy_resend(entity e)
-{
-       e.accuracy.SendFlags = 0xFFFFFF;
-}
-
-// update accuracy stats
-.float hit_time;
-.float fired_time;
-
-void accuracy_add(entity e, float w, float fired, float hit)
-{
-       entity a;
-       float b;
-       if(IS_INDEPENDENT_PLAYER(e))
-               return;
-       a = e.accuracy;
-       if(!a || !(hit || fired))
-               return;
-       w -= WEP_FIRST;
-       b = accuracy_byte(a.(accuracy_hit[w]), a.(accuracy_fired[w]));
-       if(hit)
-               a.(accuracy_hit[w]) += hit;
-       if(fired)
-               a.(accuracy_fired[w]) += fired;
-
-    if(hit && a.hit_time != time) // only run this once per frame
-    {
-        a.(accuracy_cnt_hit[w]) += 1;
-        a.hit_time = time;
-    }
-
-    if(fired && a.fired_time != time) // only run this once per frame
-    {
-        a.(accuracy_cnt_fired[w]) += 1;
-        a.fired_time = time;
-    }
-
-       if(b == accuracy_byte(a.(accuracy_hit[w]), a.(accuracy_fired[w])))
-               return;
-       w = pow(2, mod(w, 24));
-       a.SendFlags |= w;
-       FOR_EACH_CLIENT(a)
-               if(IS_SPEC(a))
-                       if(a.enemy == e)
-                               a.SendFlags |= w;
-}
-
-float accuracy_isgooddamage(entity attacker, entity targ)
-{
-       if(!warmup_stage)
-       if(IS_CLIENT(targ))
-       if(targ.deadflag == DEAD_NO)
-       if(DIFF_TEAM(attacker, targ))
-               return TRUE;
-       return FALSE;
-}
-
-float accuracy_canbegooddamage(entity attacker)
-{
-       if(!warmup_stage)
-               return TRUE;
-       return FALSE;
-}
diff --git a/qcsrc/server/accuracy.qh b/qcsrc/server/accuracy.qh
deleted file mode 100644 (file)
index 90dbb66..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-.float cvar_cl_accuracy_data_share;
-
-// init/free
-void accuracy_init(entity e);
-void accuracy_free(entity e);
-
-// force a resend of a player's accuracy stats
-void accuracy_resend(entity e);
-
-// update accuracy stats
-void accuracy_add(entity e, float w, float fired, float hit);
-
-// helper
-float accuracy_isgooddamage(entity attacker, entity targ);
-float accuracy_canbegooddamage(entity attacker);
index 19187a39deeab1c16cab1a8b05834d997dcb35d0..97996ce22fcc18396ddad4513b6e803975face6f 100644 (file)
@@ -80,37 +80,7 @@ float autocvar_g_balance_armor_rot;
 float autocvar_g_balance_armor_rotlinear;
 float autocvar_g_balance_armor_rotstable;
 float autocvar_g_balance_armor_start;
-float autocvar_g_balance_rifle_bursttime;
-float autocvar_g_balance_rifle_primary_ammo;
-float autocvar_g_balance_rifle_primary_animtime;
-float autocvar_g_balance_rifle_primary_bulletconstant;
-float autocvar_g_balance_rifle_primary_bullethail;
-float autocvar_g_balance_rifle_primary_burstcost;
-float autocvar_g_balance_rifle_primary_damage;
-float autocvar_g_balance_rifle_primary_force;
-float autocvar_g_balance_rifle_primary_lifetime;
-float autocvar_g_balance_rifle_primary_refire;
-float autocvar_g_balance_rifle_primary_shots;
-float autocvar_g_balance_rifle_primary_speed;
-float autocvar_g_balance_rifle_primary_spread;
-float autocvar_g_balance_rifle_primary_tracer;
-float autocvar_g_balance_rifle_secondary;
-float autocvar_g_balance_rifle_secondary_ammo;
-float autocvar_g_balance_rifle_secondary_animtime;
-float autocvar_g_balance_rifle_secondary_bulletconstant;
-float autocvar_g_balance_rifle_secondary_bullethail;
-float autocvar_g_balance_rifle_secondary_burstcost;
-float autocvar_g_balance_rifle_secondary_damage;
-float autocvar_g_balance_rifle_secondary_force;
-float autocvar_g_balance_rifle_secondary_lifetime;
-float autocvar_g_balance_rifle_secondary_reload;
-float autocvar_g_balance_rifle_secondary_refire;
-float autocvar_g_balance_rifle_secondary_shots;
-float autocvar_g_balance_rifle_secondary_speed;
-float autocvar_g_balance_rifle_secondary_spread;
-float autocvar_g_balance_rifle_secondary_tracer;
 float autocvar_g_balance_rifle_reload_ammo;
-float autocvar_g_balance_rifle_reload_time;
 float autocvar_g_balance_cloaked_alpha;
 float autocvar_g_balance_contents_damagerate;
 float autocvar_g_balance_contents_drowndelay;
@@ -118,58 +88,8 @@ float autocvar_g_balance_contents_playerdamage_drowning;
 float autocvar_g_balance_contents_playerdamage_lava;
 float autocvar_g_balance_contents_playerdamage_slime;
 float autocvar_g_balance_contents_projectiledamage;
-float autocvar_g_balance_crylink_primary_ammo;
-float autocvar_g_balance_crylink_primary_animtime;
-float autocvar_g_balance_crylink_primary_bouncedamagefactor;
-float autocvar_g_balance_crylink_primary_bounces;
-float autocvar_g_balance_crylink_primary_damage;
-float autocvar_g_balance_crylink_primary_edgedamage;
-float autocvar_g_balance_crylink_primary_force;
-float autocvar_g_balance_crylink_primary_joindelay;
-float autocvar_g_balance_crylink_primary_joinexplode;
-float autocvar_g_balance_crylink_primary_joinexplode_damage;
-float autocvar_g_balance_crylink_primary_joinexplode_edgedamage;
-float autocvar_g_balance_crylink_primary_joinexplode_force;
-float autocvar_g_balance_crylink_primary_joinexplode_radius;
-float autocvar_g_balance_crylink_primary_joinspread;
-float autocvar_g_balance_crylink_primary_linkexplode;
-float autocvar_g_balance_crylink_primary_middle_fadetime;
-float autocvar_g_balance_crylink_primary_middle_lifetime;
-float autocvar_g_balance_crylink_primary_other_fadetime;
-float autocvar_g_balance_crylink_primary_other_lifetime;
-float autocvar_g_balance_crylink_primary_radius;
-float autocvar_g_balance_crylink_primary_refire;
-float autocvar_g_balance_crylink_primary_shots;
-float autocvar_g_balance_crylink_primary_speed;
-float autocvar_g_balance_crylink_primary_spread;
 float autocvar_g_balance_crylink_secondary;
-float autocvar_g_balance_crylink_secondary_ammo;
-float autocvar_g_balance_crylink_secondary_animtime;
-float autocvar_g_balance_crylink_secondary_bouncedamagefactor;
-float autocvar_g_balance_crylink_secondary_bounces;
-float autocvar_g_balance_crylink_secondary_damage;
-float autocvar_g_balance_crylink_secondary_edgedamage;
-float autocvar_g_balance_crylink_secondary_force;
-float autocvar_g_balance_crylink_secondary_joindelay;
-float autocvar_g_balance_crylink_secondary_joinexplode;
-float autocvar_g_balance_crylink_secondary_joinexplode_damage;
-float autocvar_g_balance_crylink_secondary_joinexplode_edgedamage;
-float autocvar_g_balance_crylink_secondary_joinexplode_force;
-float autocvar_g_balance_crylink_secondary_joinexplode_radius;
-float autocvar_g_balance_crylink_secondary_joinspread;
-float autocvar_g_balance_crylink_secondary_line_fadetime;
-float autocvar_g_balance_crylink_secondary_line_lifetime;
-float autocvar_g_balance_crylink_secondary_linkexplode;
-float autocvar_g_balance_crylink_secondary_middle_fadetime;
-float autocvar_g_balance_crylink_secondary_middle_lifetime;
-float autocvar_g_balance_crylink_secondary_radius;
-float autocvar_g_balance_crylink_secondary_refire;
-float autocvar_g_balance_crylink_secondary_shots;
-float autocvar_g_balance_crylink_secondary_speed;
-float autocvar_g_balance_crylink_secondary_spread;
-float autocvar_g_balance_crylink_secondary_spreadtype;
 float autocvar_g_balance_crylink_reload_ammo;
-float autocvar_g_balance_crylink_reload_time;
 float autocvar_g_balance_damagepush_speedfactor;
 float autocvar_g_balance_electro_combo_comboradius;
 float autocvar_g_balance_electro_combo_damage;
@@ -211,41 +131,10 @@ float autocvar_g_balance_electro_secondary_refire;
 float autocvar_g_balance_electro_secondary_refire2;
 float autocvar_g_balance_electro_secondary_speed;
 float autocvar_g_balance_electro_reload_ammo;
-float autocvar_g_balance_electro_reload_time;
 float autocvar_g_balance_falldamage_deadminspeed;
 float autocvar_g_balance_falldamage_factor;
 float autocvar_g_balance_falldamage_maxdamage;
 float autocvar_g_balance_falldamage_minspeed;
-float autocvar_g_balance_fireball_primary_animtime;
-float autocvar_g_balance_fireball_primary_bfgdamage;
-float autocvar_g_balance_fireball_primary_bfgforce;
-float autocvar_g_balance_fireball_primary_bfgradius;
-float autocvar_g_balance_fireball_primary_damage;
-float autocvar_g_balance_fireball_primary_damageforcescale;
-float autocvar_g_balance_fireball_primary_edgedamage;
-float autocvar_g_balance_fireball_primary_force;
-float autocvar_g_balance_fireball_primary_health;
-float autocvar_g_balance_fireball_primary_laserburntime;
-float autocvar_g_balance_fireball_primary_laserdamage;
-float autocvar_g_balance_fireball_primary_laseredgedamage;
-float autocvar_g_balance_fireball_primary_laserradius;
-float autocvar_g_balance_fireball_primary_lifetime;
-float autocvar_g_balance_fireball_primary_radius;
-float autocvar_g_balance_fireball_primary_refire;
-float autocvar_g_balance_fireball_primary_refire2;
-float autocvar_g_balance_fireball_primary_speed;
-float autocvar_g_balance_fireball_secondary_animtime;
-float autocvar_g_balance_fireball_secondary_damage;
-float autocvar_g_balance_fireball_secondary_damageforcescale;
-float autocvar_g_balance_fireball_secondary_damagetime;
-float autocvar_g_balance_fireball_secondary_laserburntime;
-float autocvar_g_balance_fireball_secondary_laserdamage;
-float autocvar_g_balance_fireball_secondary_laseredgedamage;
-float autocvar_g_balance_fireball_secondary_laserradius;
-float autocvar_g_balance_fireball_secondary_lifetime;
-float autocvar_g_balance_fireball_secondary_refire;
-float autocvar_g_balance_fireball_secondary_speed;
-float autocvar_g_balance_fireball_secondary_speed_up;
 float autocvar_g_balance_firetransfer_damage;
 float autocvar_g_balance_firetransfer_time;
 float autocvar_g_balance_fuel_limit;
@@ -264,75 +153,7 @@ float autocvar_g_balance_grapplehook_speed_fly;
 float autocvar_g_balance_grapplehook_speed_pull;
 float autocvar_g_balance_grapplehook_stretch;
 float autocvar_g_balance_grapplehook_damagedbycontents;
-float autocvar_g_balance_grenadelauncher_bouncefactor;
-float autocvar_g_balance_grenadelauncher_bouncestop;
-float autocvar_g_balance_grenadelauncher_primary_ammo;
-float autocvar_g_balance_grenadelauncher_primary_animtime;
-float autocvar_g_balance_grenadelauncher_primary_damage;
-float autocvar_g_balance_grenadelauncher_primary_damageforcescale;
-float autocvar_g_balance_grenadelauncher_primary_edgedamage;
-float autocvar_g_balance_grenadelauncher_primary_force;
-float autocvar_g_balance_grenadelauncher_primary_health;
-float autocvar_g_balance_grenadelauncher_primary_lifetime;
-float autocvar_g_balance_grenadelauncher_primary_lifetime_stick;
-float autocvar_g_balance_grenadelauncher_primary_radius;
-float autocvar_g_balance_grenadelauncher_primary_refire;
-float autocvar_g_balance_grenadelauncher_primary_remote_minbouncecnt;
-float autocvar_g_balance_grenadelauncher_primary_speed;
-float autocvar_g_balance_grenadelauncher_primary_speed_up;
-float autocvar_g_balance_grenadelauncher_primary_type;
-float autocvar_g_balance_grenadelauncher_secondary_ammo;
-float autocvar_g_balance_grenadelauncher_secondary_animtime;
-float autocvar_g_balance_grenadelauncher_secondary_damage;
-float autocvar_g_balance_grenadelauncher_secondary_damageforcescale;
-float autocvar_g_balance_grenadelauncher_secondary_edgedamage;
-float autocvar_g_balance_grenadelauncher_secondary_force;
-float autocvar_g_balance_grenadelauncher_secondary_health;
-float autocvar_g_balance_grenadelauncher_secondary_lifetime;
-float autocvar_g_balance_grenadelauncher_secondary_lifetime_bounce;
-float autocvar_g_balance_grenadelauncher_secondary_lifetime_stick;
-float autocvar_g_balance_grenadelauncher_secondary_radius;
-float autocvar_g_balance_grenadelauncher_secondary_refire;
-float autocvar_g_balance_grenadelauncher_secondary_speed;
-float autocvar_g_balance_grenadelauncher_secondary_speed_up;
-float autocvar_g_balance_grenadelauncher_secondary_type;
-float autocvar_g_balance_grenadelauncher_reload_ammo;
-float autocvar_g_balance_grenadelauncher_reload_time;
-float autocvar_g_balance_hagar_primary_ammo;
-float autocvar_g_balance_hagar_primary_damage;
-float autocvar_g_balance_hagar_primary_edgedamage;
-float autocvar_g_balance_hagar_primary_force;
-float autocvar_g_balance_hagar_primary_health;
-float autocvar_g_balance_hagar_primary_damageforcescale;
-float autocvar_g_balance_hagar_primary_lifetime;
-float autocvar_g_balance_hagar_primary_radius;
-float autocvar_g_balance_hagar_primary_refire;
-float autocvar_g_balance_hagar_primary_speed;
-float autocvar_g_balance_hagar_secondary;
-float autocvar_g_balance_hagar_secondary_load;
-float autocvar_g_balance_hagar_secondary_load_speed;
-float autocvar_g_balance_hagar_secondary_load_spread;
-float autocvar_g_balance_hagar_secondary_load_spread_bias;
-float autocvar_g_balance_hagar_secondary_load_max;
-float autocvar_g_balance_hagar_secondary_load_hold;
-float autocvar_g_balance_hagar_secondary_load_releasedeath;
-float autocvar_g_balance_hagar_secondary_load_abort;
-float autocvar_g_balance_hagar_secondary_load_linkexplode;
-float autocvar_g_balance_hagar_secondary_load_animtime;
-float autocvar_g_balance_hagar_secondary_ammo;
-float autocvar_g_balance_hagar_secondary_damage;
-float autocvar_g_balance_hagar_secondary_edgedamage;
-float autocvar_g_balance_hagar_secondary_force;
-float autocvar_g_balance_hagar_secondary_health;
-float autocvar_g_balance_hagar_secondary_damageforcescale;
-float autocvar_g_balance_hagar_secondary_lifetime_min;
-float autocvar_g_balance_hagar_secondary_lifetime_rand;
-float autocvar_g_balance_hagar_secondary_radius;
-float autocvar_g_balance_hagar_secondary_refire;
-float autocvar_g_balance_hagar_secondary_speed;
-float autocvar_g_balance_hagar_secondary_spread;
 float autocvar_g_balance_hagar_reload_ammo;
-float autocvar_g_balance_hagar_reload_time;
 float autocvar_g_balance_health_limit;
 float autocvar_g_balance_health_regen;
 float autocvar_g_balance_health_regenlinear;
@@ -341,54 +162,7 @@ float autocvar_g_balance_health_rot;
 float autocvar_g_balance_health_rotlinear;
 float autocvar_g_balance_health_rotstable;
 float autocvar_g_balance_health_start;
-float autocvar_g_balance_hlac_primary_ammo;
-float autocvar_g_balance_hlac_primary_animtime;
-float autocvar_g_balance_hlac_primary_damage;
-float autocvar_g_balance_hlac_primary_edgedamage;
-float autocvar_g_balance_hlac_primary_force;
-float autocvar_g_balance_hlac_primary_lifetime;
-float autocvar_g_balance_hlac_primary_radius;
-float autocvar_g_balance_hlac_primary_refire;
-float autocvar_g_balance_hlac_primary_speed;
-float autocvar_g_balance_hlac_primary_spread_add;
-float autocvar_g_balance_hlac_primary_spread_crouchmod;
-float autocvar_g_balance_hlac_primary_spread_max;
-float autocvar_g_balance_hlac_primary_spread_min;
-float autocvar_g_balance_hlac_secondary;
-float autocvar_g_balance_hlac_secondary_ammo;
-float autocvar_g_balance_hlac_secondary_animtime;
-float autocvar_g_balance_hlac_secondary_damage;
-float autocvar_g_balance_hlac_secondary_edgedamage;
-float autocvar_g_balance_hlac_secondary_force;
-float autocvar_g_balance_hlac_secondary_lifetime;
-float autocvar_g_balance_hlac_secondary_radius;
-float autocvar_g_balance_hlac_secondary_refire;
-float autocvar_g_balance_hlac_secondary_shots;
-float autocvar_g_balance_hlac_secondary_speed;
-float autocvar_g_balance_hlac_secondary_spread;
-float autocvar_g_balance_hlac_secondary_spread_crouchmod;
 float autocvar_g_balance_hlac_reload_ammo;
-float autocvar_g_balance_hlac_reload_time;
-float autocvar_g_balance_hook_primary_animtime;
-float autocvar_g_balance_hook_primary_fuel;
-float autocvar_g_balance_hook_primary_hooked_fuel;
-float autocvar_g_balance_hook_primary_hooked_time_free;
-float autocvar_g_balance_hook_primary_hooked_time_max;
-float autocvar_g_balance_hook_primary_refire;
-float autocvar_g_balance_hook_secondary_ammo;
-float autocvar_g_balance_hook_secondary_animtime;
-float autocvar_g_balance_hook_secondary_damage;
-float autocvar_g_balance_hook_secondary_duration;
-float autocvar_g_balance_hook_secondary_edgedamage;
-float autocvar_g_balance_hook_secondary_force;
-float autocvar_g_balance_hook_secondary_gravity;
-float autocvar_g_balance_hook_secondary_lifetime;
-float autocvar_g_balance_hook_secondary_power;
-float autocvar_g_balance_hook_secondary_radius;
-float autocvar_g_balance_hook_secondary_refire;
-float autocvar_g_balance_hook_secondary_speed;
-float autocvar_g_balance_hook_secondary_health;
-float autocvar_g_balance_hook_secondary_damageforcescale;
 float autocvar_g_balance_keyhunt_damageforcescale;
 float autocvar_g_balance_keyhunt_delay_collect;
 float autocvar_g_balance_keyhunt_delay_return;
@@ -406,55 +180,76 @@ float autocvar_g_balance_keyhunt_score_push;
 float autocvar_g_balance_keyhunt_throwvelocity;
 float autocvar_g_balance_kill_delay;
 float autocvar_g_balance_kill_antispam;
+float autocvar_g_balance_laser_melee_animtime;
+float autocvar_g_balance_laser_melee_damage;
+float autocvar_g_balance_laser_melee_delay;
+float autocvar_g_balance_laser_melee_force;
+float autocvar_g_balance_laser_melee_multihit;
+float autocvar_g_balance_laser_melee_no_doubleslap;
+float autocvar_g_balance_laser_melee_nonplayerdamage;
+float autocvar_g_balance_laser_melee_range;
+float autocvar_g_balance_laser_melee_refire;
+float autocvar_g_balance_laser_melee_swing_side;
+float autocvar_g_balance_laser_melee_swing_up;
+float autocvar_g_balance_laser_melee_time;
+float autocvar_g_balance_laser_melee_traces;
+float autocvar_g_balance_laser_primary;
 float autocvar_g_balance_laser_primary_animtime;
 float autocvar_g_balance_laser_primary_damage;
 float autocvar_g_balance_laser_primary_delay;
 float autocvar_g_balance_laser_primary_edgedamage;
 float autocvar_g_balance_laser_primary_force;
-float autocvar_g_balance_laser_primary_force_other_scale;
-float autocvar_g_balance_laser_primary_force_velocitybias;
-float autocvar_g_balance_laser_primary_force_zscale;
+//float autocvar_g_balance_laser_primary_force_other_scale;
+//float autocvar_g_balance_laser_primary_force_velocitybias;
+//float autocvar_g_balance_laser_primary_force_zscale;
 float autocvar_g_balance_laser_primary_lifetime;
 float autocvar_g_balance_laser_primary_radius;
 float autocvar_g_balance_laser_primary_refire;
 float autocvar_g_balance_laser_primary_shotangle;
 float autocvar_g_balance_laser_primary_speed;
+//float autocvar_g_balance_laser_primary_spread;
+float autocvar_g_balance_laser_reload_ammo;
 float autocvar_g_balance_laser_secondary;
 float autocvar_g_balance_laser_secondary_animtime;
 float autocvar_g_balance_laser_secondary_damage;
 float autocvar_g_balance_laser_secondary_edgedamage;
 float autocvar_g_balance_laser_secondary_force;
-float autocvar_g_balance_laser_secondary_force_other_scale;
-float autocvar_g_balance_laser_secondary_force_velocitybias;
-float autocvar_g_balance_laser_secondary_force_zscale;
+//float autocvar_g_balance_laser_secondary_force_other_scale;
+//float autocvar_g_balance_laser_secondary_force_velocitybias;
+//float autocvar_g_balance_laser_secondary_force_zscale;
 float autocvar_g_balance_laser_secondary_lifetime;
 float autocvar_g_balance_laser_secondary_radius;
-float autocvar_g_balance_laser_secondary_speed;
-float autocvar_g_balance_laser_reload_ammo;
-float autocvar_g_balance_laser_reload_time;
-float autocvar_g_balance_minelayer_ammo;
-float autocvar_g_balance_minelayer_animtime;
-float autocvar_g_balance_minelayer_damage;
-float autocvar_g_balance_minelayer_damageforcescale;
-float autocvar_g_balance_minelayer_detonatedelay;
-float autocvar_g_balance_minelayer_edgedamage;
-float autocvar_g_balance_minelayer_force;
-float autocvar_g_balance_minelayer_health;
-float autocvar_g_balance_minelayer_lifetime;
-float autocvar_g_balance_minelayer_lifetime_countdown;
-float autocvar_g_balance_minelayer_limit;
-float autocvar_g_balance_minelayer_protection;
-float autocvar_g_balance_minelayer_proximityradius;
-float autocvar_g_balance_minelayer_radius;
-float autocvar_g_balance_minelayer_refire;
-float autocvar_g_balance_minelayer_remote_damage;
-float autocvar_g_balance_minelayer_remote_edgedamage;
-float autocvar_g_balance_minelayer_remote_force;
-float autocvar_g_balance_minelayer_remote_radius;
-float autocvar_g_balance_minelayer_speed;
-float autocvar_g_balance_minelayer_time;
+float autocvar_g_balance_laser_secondary_refire;
+//float autocvar_g_balance_laser_secondary_speed;
+float autocvar_g_balance_laser_shockwave_damage;
+float autocvar_g_balance_laser_shockwave_distance;
+float autocvar_g_balance_laser_shockwave_edgedamage;
+float autocvar_g_balance_laser_shockwave_force;
+float autocvar_g_balance_laser_shockwave_force_forwardbias;
+float autocvar_g_balance_laser_shockwave_force_zscale;
+float autocvar_g_balance_laser_shockwave_jump_damage;
+float autocvar_g_balance_laser_shockwave_jump_edgedamage;
+float autocvar_g_balance_laser_shockwave_jump_force;
+float autocvar_g_balance_laser_shockwave_jump_force_velocitybias;
+float autocvar_g_balance_laser_shockwave_jump_force_zscale;
+float autocvar_g_balance_laser_shockwave_jump_multiplier_accuracy;
+float autocvar_g_balance_laser_shockwave_jump_multiplier_distance;
+float autocvar_g_balance_laser_shockwave_jump_multiplier_min;
+float autocvar_g_balance_laser_shockwave_jump_radius;
+float autocvar_g_balance_laser_shockwave_multiplier_accuracy;
+float autocvar_g_balance_laser_shockwave_multiplier_distance;
+float autocvar_g_balance_laser_shockwave_multiplier_min;
+float autocvar_g_balance_laser_shockwave_splash_damage;
+float autocvar_g_balance_laser_shockwave_splash_edgedamage;
+float autocvar_g_balance_laser_shockwave_splash_force;
+float autocvar_g_balance_laser_shockwave_splash_force_forwardbias;
+float autocvar_g_balance_laser_shockwave_splash_multiplier_accuracy;
+float autocvar_g_balance_laser_shockwave_splash_multiplier_distance;
+float autocvar_g_balance_laser_shockwave_splash_multiplier_min;
+float autocvar_g_balance_laser_shockwave_splash_radius;
+float autocvar_g_balance_laser_shockwave_spread_max;
+float autocvar_g_balance_laser_shockwave_spread_min;
 float autocvar_g_balance_minelayer_reload_ammo;
-float autocvar_g_balance_minelayer_reload_time;
 float autocvar_g_balance_minstanex_ammo;
 float autocvar_g_balance_minstanex_laser_ammo;
 float autocvar_g_balance_minstanex_laser_animtime;
@@ -462,46 +257,7 @@ float autocvar_g_balance_minstanex_laser_refire;
 float autocvar_g_balance_minstanex_animtime;
 float autocvar_g_balance_minstanex_refire;
 float autocvar_g_balance_minstanex_reload_ammo;
-float autocvar_g_balance_minstanex_reload_time;
-float autocvar_g_balance_nex_charge;
-float autocvar_g_balance_nex_charge_animlimit;
-float autocvar_g_balance_nex_charge_limit;
-float autocvar_g_balance_nex_charge_maxspeed;
-float autocvar_g_balance_nex_charge_mindmg;
-float autocvar_g_balance_nex_charge_minspeed;
-float autocvar_g_balance_nex_charge_rate;
-float autocvar_g_balance_nex_charge_rot_pause;
-float autocvar_g_balance_nex_charge_rot_rate;
-float autocvar_g_balance_nex_charge_shot_multiplier;
-float autocvar_g_balance_nex_charge_start;
-float autocvar_g_balance_nex_charge_velocity_rate;
-float autocvar_g_balance_nex_primary_ammo;
-float autocvar_g_balance_nex_primary_animtime;
-float autocvar_g_balance_nex_primary_damage;
-float autocvar_g_balance_nex_primary_damagefalloff_forcehalflife;
-float autocvar_g_balance_nex_primary_damagefalloff_halflife;
-float autocvar_g_balance_nex_primary_damagefalloff_maxdist;
-float autocvar_g_balance_nex_primary_damagefalloff_mindist;
-float autocvar_g_balance_nex_primary_force;
-float autocvar_g_balance_nex_primary_refire;
-float autocvar_g_balance_nex_secondary;
-float autocvar_g_balance_nex_secondary_ammo;
-float autocvar_g_balance_nex_secondary_animtime;
-float autocvar_g_balance_nex_secondary_charge;
-float autocvar_g_balance_nex_secondary_charge_rate;
-float autocvar_g_balance_nex_secondary_chargepool;
-float autocvar_g_balance_nex_secondary_chargepool_pause_health_regen;
-float autocvar_g_balance_nex_secondary_chargepool_pause_regen;
-float autocvar_g_balance_nex_secondary_chargepool_regen;
-float autocvar_g_balance_nex_secondary_damage;
-float autocvar_g_balance_nex_secondary_damagefalloff_forcehalflife;
-float autocvar_g_balance_nex_secondary_damagefalloff_halflife;
-float autocvar_g_balance_nex_secondary_damagefalloff_maxdist;
-float autocvar_g_balance_nex_secondary_damagefalloff_mindist;
-float autocvar_g_balance_nex_secondary_force;
-float autocvar_g_balance_nex_secondary_refire;
 float autocvar_g_balance_nex_reload_ammo;
-float autocvar_g_balance_nex_reload_time;
 float autocvar_g_balance_nexball_primary_animtime;
 float autocvar_g_balance_nexball_primary_refire;
 float autocvar_g_balance_nexball_primary_speed;
@@ -533,15 +289,6 @@ float autocvar_g_balance_pause_health_rot;
 float autocvar_g_balance_pause_health_rot_spawn;
 float autocvar_g_balance_portal_health;
 float autocvar_g_balance_portal_lifetime;
-float autocvar_g_balance_porto_primary_animtime;
-float autocvar_g_balance_porto_primary_lifetime;
-float autocvar_g_balance_porto_primary_refire;
-float autocvar_g_balance_porto_primary_speed;
-float autocvar_g_balance_porto_secondary;
-float autocvar_g_balance_porto_secondary_animtime;
-float autocvar_g_balance_porto_secondary_lifetime;
-float autocvar_g_balance_porto_secondary_refire;
-float autocvar_g_balance_porto_secondary_speed;
 float autocvar_g_balance_powerup_invincible_takedamage;
 float autocvar_g_balance_powerup_invincible_time;
 float autocvar_g_balance_powerup_strength_damage;
@@ -550,74 +297,7 @@ float autocvar_g_balance_powerup_strength_selfdamage;
 float autocvar_g_balance_powerup_strength_selfforce;
 float autocvar_g_balance_powerup_strength_time;
 float autocvar_g_balance_superweapons_time;
-float autocvar_g_balance_rocketlauncher_ammo;
-float autocvar_g_balance_rocketlauncher_animtime;
-float autocvar_g_balance_rocketlauncher_damage;
-float autocvar_g_balance_rocketlauncher_damageforcescale;
-float autocvar_g_balance_rocketlauncher_detonatedelay;
-float autocvar_g_balance_rocketlauncher_edgedamage;
-float autocvar_g_balance_rocketlauncher_force;
-float autocvar_g_balance_rocketlauncher_guidedelay;
-float autocvar_g_balance_rocketlauncher_guidegoal;
-float autocvar_g_balance_rocketlauncher_guiderate;
-float autocvar_g_balance_rocketlauncher_guideratedelay;
-float autocvar_g_balance_rocketlauncher_guidestop;
-float autocvar_g_balance_rocketlauncher_health;
-float autocvar_g_balance_rocketlauncher_lifetime;
-float autocvar_g_balance_rocketlauncher_radius;
-float autocvar_g_balance_rocketlauncher_refire;
-float autocvar_g_balance_rocketlauncher_remote_damage;
-float autocvar_g_balance_rocketlauncher_remote_edgedamage;
-float autocvar_g_balance_rocketlauncher_remote_force;
-float autocvar_g_balance_rocketlauncher_remote_radius;
-float autocvar_g_balance_rocketlauncher_speed;
-float autocvar_g_balance_rocketlauncher_speedaccel;
-float autocvar_g_balance_rocketlauncher_speedstart;
-float autocvar_g_balance_rocketlauncher_reload_ammo;
-float autocvar_g_balance_rocketlauncher_reload_time;
-float autocvar_g_balance_seeker_type;
-float autocvar_g_balance_seeker_flac_ammo;
-float autocvar_g_balance_seeker_flac_animtime;
-float autocvar_g_balance_seeker_flac_damage;
-float autocvar_g_balance_seeker_flac_edgedamage;
-float autocvar_g_balance_seeker_flac_force;
-float autocvar_g_balance_seeker_flac_lifetime;
-float autocvar_g_balance_seeker_flac_lifetime_rand;
-float autocvar_g_balance_seeker_flac_radius;
-float autocvar_g_balance_seeker_flac_refire;
-float autocvar_g_balance_seeker_missile_accel;
-float autocvar_g_balance_seeker_missile_ammo;
-float autocvar_g_balance_seeker_missile_animtime;
-float autocvar_g_balance_seeker_missile_count;
-float autocvar_g_balance_seeker_missile_damage;
-float autocvar_g_balance_seeker_missile_damageforcescale;
-float autocvar_g_balance_seeker_missile_decel;
-float autocvar_g_balance_seeker_missile_delay;
-float autocvar_g_balance_seeker_missile_edgedamage;
-float autocvar_g_balance_seeker_missile_force;
-float autocvar_g_balance_seeker_missile_health;
-float autocvar_g_balance_seeker_missile_lifetime;
-float autocvar_g_balance_seeker_missile_proxy;
-float autocvar_g_balance_seeker_missile_proxy_delay;
-float autocvar_g_balance_seeker_missile_proxy_maxrange;
-float autocvar_g_balance_seeker_missile_radius;
-float autocvar_g_balance_seeker_missile_refire;
-float autocvar_g_balance_seeker_missile_smart;
-float autocvar_g_balance_seeker_missile_smart_mindist;
-float autocvar_g_balance_seeker_missile_smart_trace_max;
-float autocvar_g_balance_seeker_missile_smart_trace_min;
-float autocvar_g_balance_seeker_missile_speed_max;
-float autocvar_g_balance_seeker_missile_turnrate;
-float autocvar_g_balance_seeker_tag_ammo;
-float autocvar_g_balance_seeker_tag_animtime;
-float autocvar_g_balance_seeker_tag_damageforcescale;
-float autocvar_g_balance_seeker_tag_health;
-float autocvar_g_balance_seeker_tag_lifetime;
-float autocvar_g_balance_seeker_tag_refire;
-float autocvar_g_balance_seeker_tag_speed;
-float autocvar_g_balance_seeker_tag_tracker_lifetime;
 float autocvar_g_balance_seeker_reload_ammo;
-float autocvar_g_balance_seeker_reload_time;
 float autocvar_g_balance_selfdamagepercent;
 float autocvar_g_balance_shotgun_primary_ammo;
 float autocvar_g_balance_shotgun_primary_animtime;
@@ -643,42 +323,10 @@ float autocvar_g_balance_shotgun_secondary_melee_nonplayerdamage;
 float autocvar_g_balance_shotgun_secondary_melee_multihit;
 float autocvar_g_balance_shotgun_secondary_refire;
 float autocvar_g_balance_shotgun_reload_ammo;
-float autocvar_g_balance_shotgun_reload_time;
 float autocvar_g_balance_teams;
 float autocvar_g_balance_teams_prevent_imbalance;
 float autocvar_g_balance_teams_scorefactor;
-float autocvar_g_balance_tuba_animtime;
-float autocvar_g_balance_tuba_attenuation;
-float autocvar_g_balance_tuba_damage;
-float autocvar_g_balance_tuba_edgedamage;
-float autocvar_g_balance_tuba_force;
-float autocvar_g_balance_tuba_radius;
-float autocvar_g_balance_tuba_refire;
-float autocvar_g_balance_uzi_bulletconstant;
-float autocvar_g_balance_uzi_burst;
-float autocvar_g_balance_uzi_burst_ammo;
-float autocvar_g_balance_uzi_burst_animtime;
-float autocvar_g_balance_uzi_burst_refire;
-float autocvar_g_balance_uzi_burst_refire2;
-float autocvar_g_balance_uzi_burst_spread;
-float autocvar_g_balance_uzi_first;
-float autocvar_g_balance_uzi_first_ammo;
-float autocvar_g_balance_uzi_first_damage;
-float autocvar_g_balance_uzi_first_force;
-float autocvar_g_balance_uzi_first_refire;
-float autocvar_g_balance_uzi_first_spread;
-float autocvar_g_balance_uzi_mode;
-float autocvar_g_balance_uzi_speed;
-float autocvar_g_balance_uzi_spread_add;
-float autocvar_g_balance_uzi_spread_max;
-float autocvar_g_balance_uzi_spread_min;
-float autocvar_g_balance_uzi_sustained_ammo;
-float autocvar_g_balance_uzi_sustained_damage;
-float autocvar_g_balance_uzi_sustained_force;
-float autocvar_g_balance_uzi_sustained_refire;
-float autocvar_g_balance_uzi_sustained_spread;
 float autocvar_g_balance_uzi_reload_ammo;
-float autocvar_g_balance_uzi_reload_time;
 float autocvar_g_ballistics_density_corpse;
 float autocvar_g_ballistics_density_player;
 float autocvar_g_ballistics_materialconstant;
index a0e6bfffd42926004503c8e9f63b8e9eaaa07836..fb79a3318c37748721e5da4bcf4e7beff7990629 100644 (file)
@@ -92,7 +92,7 @@ void havocbot_ai()
 
                if(self.weapons)
                {
-                       weapon_action(self.weapon, WR_AIM);
+                       WEP_ACTION(self.weapon, WR_AIM);
                        if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(self))
                        {
                                self.BUTTON_ATCK = FALSE;
@@ -544,7 +544,7 @@ void havocbot_movetogoal()
        else if(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 (!(self.flags & FL_ONGROUND))
        {
@@ -590,10 +590,10 @@ void havocbot_movetogoal()
 
                        return;
                }
-               else if(self.health>autocvar_g_balance_rocketlauncher_damage*0.5)
+               else if(self.health>WEP_CVAR(devastator, damage)*0.5)
                {
                        if(self.velocity_z < 0)
-                       if(client_hasweapon(self, WEP_ROCKET_LAUNCHER, TRUE, FALSE))
+                       if(client_hasweapon(self, WEP_DEVASTATOR, TRUE, FALSE))
                        {
                                self.movement_x = maxspeed;
 
@@ -607,10 +607,10 @@ void havocbot_movetogoal()
                                        return;
                                }
 
-                               self.switchweapon = WEP_ROCKET_LAUNCHER;
+                               self.switchweapon = WEP_DEVASTATOR;
                                self.v_angle_x = 90;
                                self.BUTTON_ATCK = TRUE;
-                               self.rocketjumptime = time + autocvar_g_balance_rocketlauncher_detonatedelay;
+                               self.rocketjumptime = time + WEP_CVAR(devastator, detonatedelay);
                                return;
                        }
                }
@@ -956,7 +956,7 @@ float havocbot_chooseweapon_checkreload(float new_weapon)
                for(i = WEP_FIRST; i <= WEP_LAST; ++i)
                {
                        // if we are out of ammo for all other weapons, it's an emergency to switch to anything else
-                       if (weapon_action(i, WR_CHECKAMMO1) + weapon_action(i, WR_CHECKAMMO2))
+                       if (WEP_ACTION(i, WR_CHECKAMMO1) + WEP_ACTION(i, WR_CHECKAMMO2))
                                other_weapon_available = TRUE;
                }
                if(other_weapon_available)
index 5bd3b49a3ab8021b2d305912f8f662ae78adb990..189eeea839caff1e3adebd31f75b7e1f5c02aee8 100644 (file)
@@ -264,7 +264,7 @@ float CheatImpulse(float i)
 
                        e2 = spawn();
                        setorigin(e2, e.origin);
-                       RadiusDamage(e2, self, 1000, 0, 128, world, 500, DEATH_CHEAT, e);
+                       RadiusDamage(e2, self, 1000, 0, 128, world, world, 500, DEATH_CHEAT, e);
                        remove(e2);
 
                        print("404 Sportsmanship not found.\n");
index d803602ad6ccd6c229e5b61ffa20f4dfc976b6ae..a839145009af42ce7e604300e1d43084cd8c9548 100644 (file)
@@ -423,11 +423,11 @@ void PutClientInServer (void)
                self.effects |= EF_TELEPORT_BIT | EF_RESTARTANIM_BIT;
                self.air_finished = time + 12;
                self.dmg = 2;
-               if(autocvar_g_balance_nex_charge)
+               if(WEP_CVAR(nex, charge))
                {
-                       if(autocvar_g_balance_nex_secondary_chargepool)
+                       if(WEP_CVAR_SEC(nex, chargepool))
                                self.nex_chargepool_ammo = 1;
-                       self.nex_charge = autocvar_g_balance_nex_charge_start;
+                       self.nex_charge = WEP_CVAR(nex, charge_start);
                }
 
                if(warmup_stage)
@@ -564,13 +564,13 @@ void PutClientInServer (void)
                // reset fields the weapons may use
                for (j = WEP_FIRST; j <= WEP_LAST; ++j)
                {
-                       weapon_action(j, WR_RESETPLAYER);
+                       WEP_ACTION(j, WR_RESETPLAYER);
 
                        // all weapons must be fully loaded when we spawn
                        entity e;
                        e = get_weaponinfo(j);
                        if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars
-                               self.(weapon_load[j]) = cvar(strcat("g_balance_", e.netname, "_reload_ammo"));
+                               self.(weapon_load[j]) = e.reloading_ammo;
                }
 
                oldself = self;
@@ -626,26 +626,27 @@ float ClientInit_SendEntity(entity to, float sf)
        WriteInt24_t(MSG_ENTITY, compressShotOrigin(electro_shotorigin[1]));
        WriteInt24_t(MSG_ENTITY, compressShotOrigin(electro_shotorigin[2]));
        WriteInt24_t(MSG_ENTITY, compressShotOrigin(electro_shotorigin[3]));
-       WriteInt24_t(MSG_ENTITY, compressShotOrigin(gauntlet_shotorigin[0]));
-       WriteInt24_t(MSG_ENTITY, compressShotOrigin(gauntlet_shotorigin[1]));
-       WriteInt24_t(MSG_ENTITY, compressShotOrigin(gauntlet_shotorigin[2]));
-       WriteInt24_t(MSG_ENTITY, compressShotOrigin(gauntlet_shotorigin[3]));
+       WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[0]));
+       WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[1]));
+       WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[2]));
+       WriteInt24_t(MSG_ENTITY, compressShotOrigin(arc_shotorigin[3]));
+
        if(sv_foginterval && world.fog != "")
                WriteString(MSG_ENTITY, world.fog);
        else
                WriteString(MSG_ENTITY, "");
        WriteByte(MSG_ENTITY, self.count * 255.0); // g_balance_armor_blockpercent
-       WriteCoord(MSG_ENTITY, self.bouncefactor); // g_balance_grenadelauncher_bouncefactor
-       WriteCoord(MSG_ENTITY, self.bouncestop); // g_balance_grenadelauncher_bouncestop
-       WriteCoord(MSG_ENTITY, self.ebouncefactor); // g_balance_grenadelauncher_bouncefactor
-       WriteCoord(MSG_ENTITY, self.ebouncestop); // g_balance_grenadelauncher_bouncestop
-       WriteByte(MSG_ENTITY, autocvar_g_balance_nex_secondary); // client has to know if it should zoom or not
-       WriteByte(MSG_ENTITY, autocvar_g_balance_rifle_secondary); // client has to know if it should zoom or not
+       WriteCoord(MSG_ENTITY, self.bouncefactor); // g_balance_mortar_bouncefactor // WEAPONTODO
+       WriteCoord(MSG_ENTITY, self.bouncestop); // g_balance_mortar_bouncestop
+       WriteCoord(MSG_ENTITY, self.ebouncefactor); // g_balance_mortar_bouncefactor
+       WriteCoord(MSG_ENTITY, self.ebouncestop); // g_balance_mortar_bouncestop
+       WriteByte(MSG_ENTITY, WEP_CVAR(nex, secondary)); // client has to know if it should zoom or not // WEAPONTODO
+       WriteByte(MSG_ENTITY, WEP_CVAR(rifle, secondary)); // client has to know if it should zoom or not // WEAPONTODO
        WriteByte(MSG_ENTITY, serverflags); // client has to know if it should zoom or not
-       WriteByte(MSG_ENTITY, autocvar_g_balance_minelayer_limit); // minelayer max mines
-       WriteByte(MSG_ENTITY, autocvar_g_balance_hagar_secondary_load_max); // hagar max loadable rockets
+       WriteByte(MSG_ENTITY, WEP_CVAR(minelayer, limit)); // minelayer max mines // WEAPONTODO
+       WriteByte(MSG_ENTITY, WEP_CVAR_SEC(hagar, load_max)); // hagar max loadable rockets // WEAPONTODO
        WriteCoord(MSG_ENTITY, autocvar_g_trueaim_minrange);
-       WriteByte(MSG_ENTITY, autocvar_g_balance_porto_secondary);
+       WriteByte(MSG_ENTITY, WEP_CVAR(porto, secondary)); // WEAPONTODO
        return TRUE;
 }
 
@@ -657,14 +658,14 @@ void ClientInit_CheckUpdate()
                self.count = autocvar_g_balance_armor_blockpercent;
                self.SendFlags |= 1;
        }
-       if(self.bouncefactor != autocvar_g_balance_grenadelauncher_bouncefactor)
+       if(self.bouncefactor != autocvar_g_balance_mortar_bouncefactor) // WEAPONTODO
        {
-               self.bouncefactor = autocvar_g_balance_grenadelauncher_bouncefactor;
+               self.bouncefactor = autocvar_g_balance_mortar_bouncefactor;
                self.SendFlags |= 1;
        }
-       if(self.bouncestop != autocvar_g_balance_grenadelauncher_bouncestop)
+       if(self.bouncestop != autocvar_g_balance_mortar_bouncestop)
        {
-               self.bouncestop = autocvar_g_balance_grenadelauncher_bouncestop;
+               self.bouncestop = autocvar_g_balance_mortar_bouncestop;
                self.SendFlags |= 1;
        }
        if(self.ebouncefactor != autocvar_g_balance_electro_secondary_bouncefactor)
@@ -1193,13 +1194,7 @@ void ClientConnect (void)
        if(!sv_foginterval && world.fog != "")
                stuffcmd(self, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n"));
 
-       if(autocvar_g_hitplots || strstrofs(strcat(" ", autocvar_g_hitplots_individuals, " "), strcat(" ", self.netaddress, " "), 0) >= 0)
-       {
-               self.hitplotfh = fopen(strcat("hits-", matchid, "-", self.netaddress, "-", ftos(self.playerid), ".plot"), FILE_WRITE);
-               fputs(self.hitplotfh, strcat("#name ", self.netname, "\n"));
-       }
-       else
-               self.hitplotfh = -1;
+       W_HitPlotOpen(self);
 
        if(g_race || g_cts) {
                string rr;
@@ -1259,11 +1254,7 @@ void ClientDisconnect (void)
 
        CheatShutdownClient();
 
-       if(self.hitplotfh >= 0)
-       {
-               fclose(self.hitplotfh);
-               self.hitplotfh = -1;
-       }
+       W_HitPlotClose(self);
 
        anticheat_report();
        anticheat_shutdown();
@@ -2268,17 +2259,17 @@ void PlayerPreThink (void)
 
                if(frametime)
                {
-                       if(self.weapon == WEP_NEX && autocvar_g_balance_nex_charge)
+                       if(self.weapon == WEP_NEX && WEP_CVAR(nex, charge))
                        {
-                               self.weaponentity_glowmod_x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit);
-                               self.weaponentity_glowmod_y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit);
-                               self.weaponentity_glowmod_z = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_half * min(1, self.nex_charge / autocvar_g_balance_nex_charge_animlimit);
+                               self.weaponentity_glowmod_x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, self.nex_charge / WEP_CVAR(nex, charge_animlimit));
+                               self.weaponentity_glowmod_y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, self.nex_charge / WEP_CVAR(nex, charge_animlimit));
+                               self.weaponentity_glowmod_z = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_half * min(1, self.nex_charge / WEP_CVAR(nex, charge_animlimit));
 
-                               if(self.nex_charge > autocvar_g_balance_nex_charge_animlimit)
+                               if(self.nex_charge > WEP_CVAR(nex, charge_animlimit))
                                {
-                                       self.weaponentity_glowmod_x = self.weaponentity_glowmod_x + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit);
-                                       self.weaponentity_glowmod_y = self.weaponentity_glowmod_y + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit);
-                                       self.weaponentity_glowmod_z = self.weaponentity_glowmod_z + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (self.nex_charge - autocvar_g_balance_nex_charge_animlimit) / (1 - autocvar_g_balance_nex_charge_animlimit);
+                                       self.weaponentity_glowmod_x = self.weaponentity_glowmod_x + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (self.nex_charge - WEP_CVAR(nex, charge_animlimit)) / (1 - WEP_CVAR(nex, charge_animlimit));
+                                       self.weaponentity_glowmod_y = self.weaponentity_glowmod_y + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (self.nex_charge - WEP_CVAR(nex, charge_animlimit)) / (1 - WEP_CVAR(nex, charge_animlimit));
+                                       self.weaponentity_glowmod_z = self.weaponentity_glowmod_z + autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (self.nex_charge - WEP_CVAR(nex, charge_animlimit)) / (1 - WEP_CVAR(nex, charge_animlimit));
                                }
                        }
                        else
@@ -2410,8 +2401,8 @@ void PlayerPreThink (void)
                player_regen();
 
                // rot nex charge to the charge limit
-               if(autocvar_g_balance_nex_charge_rot_rate && self.nex_charge > autocvar_g_balance_nex_charge_limit && self.nex_charge_rottime < time)
-                       self.nex_charge = bound(autocvar_g_balance_nex_charge_limit, self.nex_charge - autocvar_g_balance_nex_charge_rot_rate * frametime / W_TICSPERFRAME, 1);
+               if(WEP_CVAR(nex, charge_rot_rate) && self.nex_charge > WEP_CVAR(nex, charge_limit) && self.nex_charge_rottime < time)
+                       self.nex_charge = bound(WEP_CVAR(nex, charge_limit), self.nex_charge - WEP_CVAR(nex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1);
 
                if(frametime)
                        player_anim();
@@ -2433,7 +2424,7 @@ void PlayerPreThink (void)
        }
 
        if(!zoomstate_set)
-               SetZoomState(self.BUTTON_ZOOM || self.BUTTON_ZOOMSCRIPT || (self.BUTTON_ATCK2 && self.weapon == WEP_NEX) || (self.BUTTON_ATCK2 && self.weapon == WEP_RIFLE && autocvar_g_balance_rifle_secondary == 0));
+               SetZoomState(self.BUTTON_ZOOM || self.BUTTON_ZOOMSCRIPT || (self.BUTTON_ATCK2 && self.weapon == WEP_NEX) || (self.BUTTON_ATCK2 && self.weapon == WEP_RIFLE && WEP_CVAR(rifle, secondary) == 0)); // WEAPONTODO
 
        float oldspectatee_status;
        oldspectatee_status = self.spectatee_status;
index a4376bd418a101096d5fdf7d2e68c76070796578..148eafa84875877e72a5fcae8163f2ff2d041f58 100644 (file)
@@ -77,37 +77,37 @@ void ImpulseCommands (void)
                        switch(imp)
                        {
                                case 10:
-                                       W_NextWeapon (0);
+                                       W_NextWeapon(0);
                                        break;
                                case 11:
                                        W_LastWeapon();
                                        break;
                                case 12:
-                                       W_PreviousWeapon (0);
+                                       W_PreviousWeapon(0);
                                        break;
                                case 13:
-                                       W_SwitchWeapon (w_getbestweapon(self));
+                                       W_SwitchWeapon(w_getbestweapon(self));
                                        break;
                                case 14:
                                        W_NextWeaponOnImpulse(0);
                                        break;
                                case 15:
-                                       W_NextWeapon (2);
+                                       W_NextWeapon(2);
                                        break;
                                case 16:
-                                       W_PreviousWeapon (2);
+                                       W_PreviousWeapon(2);
                                        break;
                                case 17:
                                        W_ThrowWeapon(W_CalculateProjectileVelocity(self.velocity, v_forward * 750, FALSE), '0 0 0', TRUE);
                                        break;
                                case 18:
-                                       W_NextWeapon (1);
+                                       W_NextWeapon(1);
                                        break;
                                case 19:
-                                       W_PreviousWeapon (1);
+                                       W_PreviousWeapon(1);
                                        break;
                                case 20:
-                                       W_TriggerReload ();
+                                       WEP_ACTION(self.weapon, WR_RELOAD);
                                        break;
                        }
                }
index edad6d63f845881338547c06bc59d2a4285a0ce7..12cc888fee7b383d119dd6185151d8f69a6a8f33 100644 (file)
@@ -1269,15 +1269,16 @@ void SV_PlayerPhysics()
                }
        }
 
+       // WEAPONTODO
        float xyspeed;
        xyspeed = vlen('1 0 0' * self.velocity_x + '0 1 0' * self.velocity_y);
-       if(self.weapon == WEP_NEX && autocvar_g_balance_nex_charge && autocvar_g_balance_nex_charge_velocity_rate && xyspeed > autocvar_g_balance_nex_charge_minspeed)
+       if(self.weapon == WEP_NEX && WEP_CVAR(nex, charge) && WEP_CVAR(nex, charge_velocity_rate) && xyspeed > WEP_CVAR(nex, charge_minspeed))
        {
                // add a maximum of charge_velocity_rate when going fast (f = 1), gradually increasing from minspeed (f = 0) to maxspeed
-               xyspeed = min(xyspeed, autocvar_g_balance_nex_charge_maxspeed);
-               f = (xyspeed - autocvar_g_balance_nex_charge_minspeed) / (autocvar_g_balance_nex_charge_maxspeed - autocvar_g_balance_nex_charge_minspeed);
+               xyspeed = min(xyspeed, WEP_CVAR(nex, charge_maxspeed));
+               f = (xyspeed - WEP_CVAR(nex, charge_minspeed)) / (WEP_CVAR(nex, charge_maxspeed) - WEP_CVAR(nex, charge_minspeed));
                // add the extra charge
-               self.nex_charge = min(1, self.nex_charge + autocvar_g_balance_nex_charge_velocity_rate * f * frametime);
+               self.nex_charge = min(1, self.nex_charge + WEP_CVAR(nex, charge_velocity_rate) * f * frametime);
        }
 :end
        if(self.flags & FL_ONGROUND)
index eb3e81fbb280a7d226e65f2554b22df8c5eab99d..e7256766607d87988296dbae02556508435423b7 100644 (file)
@@ -1,6 +1,3 @@
-.entity accuracy;
-.float accuracy_frags[WEP_MAXCOUNT];
-
 float weaponstats_buffer;
 
 void WeaponStats_Init()
@@ -570,7 +567,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                frag_deathtype = deathtype;
                MUTATOR_CALLHOOK(PlayerDies);
 
-               weapon_action(self.weapon, WR_PLAYERDEATH);
+               WEP_ACTION(self.weapon, WR_PLAYERDEATH);
 
                RemoveGrapplingHook(self);
 
@@ -674,7 +671,7 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                // reset fields the weapons may use just in case
                for (j = WEP_FIRST; j <= WEP_LAST; ++j)
                {
-                       weapon_action(j, WR_RESETPLAYER);
+                       WEP_ACTION(j, WR_RESETPLAYER);
                        ATTACK_FINISHED_FOR(self, j) = 0;
                }
        }
diff --git a/qcsrc/server/cl_weaponsystem.qc b/qcsrc/server/cl_weaponsystem.qc
deleted file mode 100644 (file)
index 8d72a79..0000000
+++ /dev/null
@@ -1,1403 +0,0 @@
-/*
-===========================================================================
-
-  CLIENT WEAPONSYSTEM CODE
-  Bring back W_Weaponframe
-
-===========================================================================
-*/
-
-.float weapon_frametime;
-
-float W_WeaponRateFactor()
-{
-       float t;
-       t = 1.0 / g_weaponratefactor;
-
-       return t;
-}
-
-void W_SwitchWeapon_Force(entity e, float w)
-{
-       e.cnt = e.switchweapon;
-       e.switchweapon = w;
-       e.selectweapon = w;
-}
-
-.float antilag_debug;
-
-// VorteX: static frame globals
-const float WFRAME_DONTCHANGE = -1;
-const float WFRAME_FIRE1 = 0;
-const float WFRAME_FIRE2 = 1;
-const float WFRAME_IDLE = 2;
-const float WFRAME_RELOAD = 3;
-.float wframe;
-
-void(float fr, float t, void() func) weapon_thinkf;
-
-vector W_HitPlotUnnormalizedUntransform(vector screenforward, vector screenright, vector screenup, vector v)
-{
-       vector ret;
-       ret_x = screenright * v;
-       ret_y = screenup * v;
-       ret_z = screenforward * v;
-       return ret;
-}
-
-vector W_HitPlotNormalizedUntransform(vector org, entity targ, vector screenforward, vector screenright, vector screenup, vector v)
-{
-       float i, j, k;
-       vector mi, ma, thisv, myv, ret;
-
-       myv = W_HitPlotUnnormalizedUntransform(screenforward, screenright, screenup, org);
-
-       // x = 0..1 relative to hitbox; y = 0..1 relative to hitbox; z = distance
-
-       mi = ma = targ.origin + 0.5 * (targ.mins + targ.maxs);
-       for(i = 0; i < 2; ++i) for(j = 0; j < 2; ++j) for(k = 0; k < 2; ++k)
-       {
-               thisv = targ.origin;
-               if(i) thisv_x += targ.maxs_x; else thisv_x += targ.mins_x;
-               if(j) thisv_y += targ.maxs_y; else thisv_y += targ.mins_y;
-               if(k) thisv_z += targ.maxs_z; else thisv_z += targ.mins_z;
-               thisv = W_HitPlotUnnormalizedUntransform(screenforward, screenright, screenup, thisv);
-               if(i || j || k)
-               {
-                       if(mi_x > thisv_x) mi_x = thisv_x; if(ma_x < thisv_x) ma_x = thisv_x;
-                       if(mi_y > thisv_y) mi_y = thisv_y; if(ma_y < thisv_y) ma_y = thisv_y;
-                       //if(mi_z > thisv_z) mi_z = thisv_z; if(ma_z < thisv_z) ma_y = thisv_z;
-               }
-               else
-               {
-                       // first run
-                       mi = ma = thisv;
-               }
-       }
-
-       thisv = W_HitPlotUnnormalizedUntransform(screenforward, screenright, screenup, v);
-       ret_x = (thisv_x - mi_x) / (ma_x - mi_x);
-       ret_y = (thisv_y - mi_y) / (ma_y - mi_y);
-       ret_z = thisv_z - myv_z;
-       return ret;
-}
-
-void W_HitPlotAnalysis(entity player, vector screenforward, vector screenright, vector screenup)
-{
-       vector hitplot;
-       vector org;
-       float lag;
-
-       if(player.hitplotfh >= 0)
-       {
-               lag = ANTILAG_LATENCY(player);
-               if(lag < 0.001)
-                       lag = 0;
-               if (!IS_REAL_CLIENT(player))
-                       lag = 0; // only antilag for clients
-
-               org = player.origin + player.view_ofs;
-               traceline_antilag_force(player, org, org + screenforward * MAX_SHOT_DISTANCE, MOVE_NORMAL, player, lag);
-               if(IS_CLIENT(trace_ent))
-               {
-                       antilag_takeback(trace_ent, time - lag);
-                       hitplot = W_HitPlotNormalizedUntransform(org, trace_ent, screenforward, screenright, screenup, trace_endpos);
-                       antilag_restore(trace_ent);
-                       fputs(player.hitplotfh, strcat(ftos(hitplot_x), " ", ftos(hitplot_y), " ", ftos(hitplot_z), " ", ftos(player.switchweapon), "\n"));
-                       //print(strcat(ftos(hitplot_x), " ", ftos(hitplot_y), " ", ftos(hitplot_z), "\n"));
-               }
-       }
-}
-
-vector w_shotorg;
-vector w_shotdir;
-vector w_shotend;
-
-.float prevstrengthsound;
-.float prevstrengthsoundattempt;
-void W_PlayStrengthSound(entity player) // void W_PlayStrengthSound
-{
-       if((player.items & IT_STRENGTH)
-               && ((time > player.prevstrengthsound + autocvar_sv_strengthsound_antispam_time) // prevent insane sound spam
-               || (time > player.prevstrengthsoundattempt + autocvar_sv_strengthsound_antispam_refire_threshold)))
-               {
-                       sound(player, CH_TRIGGER, "weapons/strength_fire.wav", VOL_BASE, ATTEN_NORM);
-                       player.prevstrengthsound = time;
-               }
-               player.prevstrengthsoundattempt = time;
-}
-
-// this function calculates w_shotorg and w_shotdir based on the weapon model
-// offset, trueaim and antilag, and won't put w_shotorg inside a wall.
-// make sure you call makevectors first (FIXME?)
-void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector mi, vector ma, float antilag, float recoil, string snd, float chan, float maxdamage, float range)
-{
-       float nudge = 1; // added to traceline target and subtracted from result
-       float oldsolid;
-       vector vecs, dv;
-       oldsolid = ent.dphitcontentsmask;
-       if(ent.weapon == WEP_RIFLE)
-               ent.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_CORPSE;
-       else
-               ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
-       if(antilag)
-               WarpZone_traceline_antilag(world, ent.origin + ent.view_ofs, ent.origin + ent.view_ofs + s_forward * range, MOVE_NORMAL, ent, ANTILAG_LATENCY(ent));
-               // passing world, because we do NOT want it to touch dphitcontentsmask
-       else
-               WarpZone_TraceLine(ent.origin + ent.view_ofs, ent.origin + ent.view_ofs + s_forward * range, MOVE_NOMONSTERS, ent);
-       ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
-
-       vector vf, vr, vu;
-       vf = v_forward;
-       vr = v_right;
-       vu = v_up;
-       w_shotend = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos); // warpzone support
-       v_forward = vf;
-       v_right = vr;
-       v_up = vu;
-
-       // un-adjust trueaim if shotend is too close
-       if(vlen(w_shotend - (ent.origin + ent.view_ofs)) < autocvar_g_trueaim_minrange)
-               w_shotend = ent.origin + ent.view_ofs + s_forward * autocvar_g_trueaim_minrange;
-
-       // track max damage
-       if(accuracy_canbegooddamage(ent))
-               accuracy_add(ent, ent.weapon, maxdamage, 0);
-
-       W_HitPlotAnalysis(ent, v_forward, v_right, v_up);
-
-       if(ent.weaponentity.movedir_x > 0)
-               vecs = ent.weaponentity.movedir;
-       else
-               vecs = '0 0 0';
-
-       dv = v_right * -vecs_y + v_up * vecs_z;
-       w_shotorg = ent.origin + ent.view_ofs + dv;
-
-       // now move the shotorg forward as much as requested if possible
-       if(antilag)
-       {
-               if(ent.antilag_debug)
-                       tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, ent, ent.antilag_debug);
-               else
-                       tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, ent, ANTILAG_LATENCY(ent));
-       }
-       else
-               tracebox(w_shotorg, mi, ma, w_shotorg + v_forward * (vecs_x + nudge), MOVE_NORMAL, ent);
-       w_shotorg = trace_endpos - v_forward * nudge;
-       // calculate the shotdir from the chosen shotorg
-       w_shotdir = normalize(w_shotend - w_shotorg);
-
-       if (antilag)
-       if (!ent.cvar_cl_noantilag)
-       {
-               if (autocvar_g_antilag == 1) // switch to "ghost" if not hitting original
-               {
-                       traceline(w_shotorg, w_shotorg + w_shotdir * range, MOVE_NORMAL, ent);
-                       if (!trace_ent.takedamage)
-                       {
-                               traceline_antilag_force (ent, w_shotorg, w_shotorg + w_shotdir * range, MOVE_NORMAL, ent, ANTILAG_LATENCY(ent));
-                               if (trace_ent.takedamage && IS_PLAYER(trace_ent))
-                               {
-                                       entity e;
-                                       e = trace_ent;
-                                       traceline(w_shotorg, e.origin, MOVE_NORMAL, ent);
-                                       if(trace_ent == e)
-                                               w_shotdir = normalize(trace_ent.origin - w_shotorg);
-                               }
-                       }
-               }
-               else if(autocvar_g_antilag == 3) // client side hitscan
-               {
-                       // this part MUST use prydon cursor
-                       if (ent.cursor_trace_ent)                 // client was aiming at someone
-                       if (ent.cursor_trace_ent != ent)         // just to make sure
-                       if (ent.cursor_trace_ent.takedamage)      // and that person is killable
-                       if (IS_PLAYER(ent.cursor_trace_ent)) // and actually a player
-                       {
-                               // verify that the shot would miss without antilag
-                               // (avoids an issue where guns would always shoot at their origin)
-                               traceline(w_shotorg, w_shotorg + w_shotdir * range, MOVE_NORMAL, ent);
-                               if (!trace_ent.takedamage)
-                               {
-                                       // verify that the shot would hit if altered
-                                       traceline(w_shotorg, ent.cursor_trace_ent.origin, MOVE_NORMAL, ent);
-                                       if (trace_ent == ent.cursor_trace_ent)
-                                               w_shotdir = normalize(ent.cursor_trace_ent.origin - w_shotorg);
-                                       else
-                                               print("antilag fail\n");
-                               }
-                       }
-               }
-       }
-
-       ent.dphitcontentsmask = oldsolid; // restore solid type (generally SOLID_SLIDEBOX)
-
-       if (!autocvar_g_norecoil)
-               ent.punchangle_x = recoil * -1;
-
-       if (snd != "")
-       {
-               sound (ent, chan, snd, VOL_BASE, ATTEN_NORM);
-               W_PlayStrengthSound(ent);
-       }
-
-       // nudge w_shotend so a trace to w_shotend hits
-       w_shotend = w_shotend + normalize(w_shotend - w_shotorg) * nudge;
-}
-
-#define W_SetupShot_Dir_ProjectileSize(ent,s_forward,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize_Range(ent, s_forward, mi, ma, antilag, recoil, snd, chan, maxdamage, MAX_SHOT_DISTANCE)
-#define W_SetupShot_ProjectileSize(ent,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, v_forward, mi, ma, antilag, recoil, snd, chan, maxdamage)
-#define W_SetupShot_Dir(ent,s_forward,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, s_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage)
-#define W_SetupShot(ent,antilag,recoil,snd,chan,maxdamage) W_SetupShot_ProjectileSize(ent, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage)
-#define W_SetupShot_Range(ent,antilag,recoil,snd,chan,maxdamage,range) W_SetupShot_Dir_ProjectileSize_Range(ent, v_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage, range)
-
-float CL_Weaponentity_CustomizeEntityForClient()
-{
-       self.viewmodelforclient = self.owner;
-       if(IS_SPEC(other))
-               if(other.enemy == self.owner)
-                       self.viewmodelforclient = other;
-       return TRUE;
-}
-
-/*
- * supported formats:
- *
- * 1. simple animated model, muzzle flash handling on h_ model:
- *    h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation
- *      tags:
- *        shot = muzzle end (shot origin, also used for muzzle flashes)
- *        shell = casings ejection point (must be on the right hand side of the gun)
- *        weapon = attachment for v_tuba.md3
- *    v_tuba.md3 - first and third person model
- *    g_tuba.md3 - pickup model
- *
- * 2. simple animated model, muzzle flash handling on v_ model:
- *    h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation
- *      tags:
- *        weapon = attachment for v_tuba.md3
- *    v_tuba.md3 - first and third person model
- *      tags:
- *        shot = muzzle end (shot origin, also used for muzzle flashes)
- *        shell = casings ejection point (must be on the right hand side of the gun)
- *    g_tuba.md3 - pickup model
- *
- * 3. fully animated model, muzzle flash handling on h_ model:
- *    h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model
- *      tags:
- *        shot = muzzle end (shot origin, also used for muzzle flashes)
- *        shell = casings ejection point (must be on the right hand side of the gun)
- *        handle = corresponding to the origin of v_tuba.md3 (used for muzzle flashes)
- *    v_tuba.md3 - third person model
- *    g_tuba.md3 - pickup model
- *
- * 4. fully animated model, muzzle flash handling on v_ model:
- *    h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model
- *      tags:
- *        shot = muzzle end (shot origin)
- *        shell = casings ejection point (must be on the right hand side of the gun)
- *    v_tuba.md3 - third person model
- *      tags:
- *        shot = muzzle end (for muzzle flashes)
- *    g_tuba.md3 - pickup model
- */
-
-// writes:
-//   self.origin, self.angles
-//   self.weaponentity
-//   self.movedir, self.view_ofs
-//   attachment stuff
-//   anim stuff
-// to free:
-//   call again with ""
-//   remove the ent
-void CL_WeaponEntity_SetModel(string name)
-{
-       float v_shot_idx;
-       if (name != "")
-       {
-               // if there is a child entity, hide it until we're sure we use it
-               if (self.weaponentity)
-                       self.weaponentity.model = "";
-               setmodel(self, strcat("models/weapons/v_", name, ".md3")); // precision set below
-               v_shot_idx = gettagindex(self, "shot"); // used later
-               if(!v_shot_idx)
-                       v_shot_idx = gettagindex(self, "tag_shot");
-
-               setmodel(self, strcat("models/weapons/h_", name, ".iqm")); // precision set below
-               // preset some defaults that work great for renamed zym files (which don't need an animinfo)
-               self.anim_fire1  = animfixfps(self, '0 1 0.01', '0 0 0');
-               self.anim_fire2  = animfixfps(self, '1 1 0.01', '0 0 0');
-               self.anim_idle   = animfixfps(self, '2 1 0.01', '0 0 0');
-               self.anim_reload = animfixfps(self, '3 1 0.01', '0 0 0');
-
-               // if we have a "weapon" tag, let's attach the v_ model to it ("invisible hand" style model)
-               // if we don't, this is a "real" animated model
-               if(gettagindex(self, "weapon"))
-               {
-                       if (!self.weaponentity)
-                               self.weaponentity = spawn();
-                       setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter
-                       setattachment(self.weaponentity, self, "weapon");
-               }
-               else if(gettagindex(self, "tag_weapon"))
-               {
-                       if (!self.weaponentity)
-                               self.weaponentity = spawn();
-                       setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter
-                       setattachment(self.weaponentity, self, "tag_weapon");
-               }
-               else
-               {
-                       if(self.weaponentity)
-                               remove(self.weaponentity);
-                       self.weaponentity = world;
-               }
-
-               setorigin(self,'0 0 0');
-               self.angles = '0 0 0';
-               self.frame = 0;
-               self.viewmodelforclient = world;
-
-               float idx;
-
-               if(v_shot_idx) // v_ model attached to invisible h_ model
-               {
-                       self.movedir = gettaginfo(self.weaponentity, v_shot_idx);
-               }
-               else
-               {
-                       idx = gettagindex(self, "shot");
-                       if(!idx)
-                               idx = gettagindex(self, "tag_shot");
-                       if(idx)
-                               self.movedir = gettaginfo(self, idx);
-                       else
-                       {
-                               print("WARNING: weapon model ", self.model, " does not support the 'shot' tag, will display shots TOTALLY wrong\n");
-                               self.movedir = '0 0 0';
-                       }
-               }
-
-               if(self.weaponentity) // v_ model attached to invisible h_ model
-               {
-                       idx = gettagindex(self.weaponentity, "shell");
-                       if(!idx)
-                               idx = gettagindex(self.weaponentity, "tag_shell");
-                       if(idx)
-                               self.spawnorigin = gettaginfo(self.weaponentity, idx);
-               }
-               else
-                       idx = 0;
-               if(!idx)
-               {
-                       idx = gettagindex(self, "shell");
-                       if(!idx)
-                               idx = gettagindex(self, "tag_shell");
-                       if(idx)
-                               self.spawnorigin = gettaginfo(self, idx);
-                       else
-                       {
-                               print("WARNING: weapon model ", self.model, " does not support the 'shell' tag, will display casings wrong\n");
-                               self.spawnorigin = self.movedir;
-                       }
-               }
-
-               if(v_shot_idx)
-               {
-                       self.oldorigin = '0 0 0'; // use regular attachment
-               }
-               else
-               {
-                       if(self.weaponentity)
-                       {
-                               idx = gettagindex(self, "weapon");
-                               if(!idx)
-                                       idx = gettagindex(self, "tag_weapon");
-                       }
-                       else
-                       {
-                               idx = gettagindex(self, "handle");
-                               if(!idx)
-                                       idx = gettagindex(self, "tag_handle");
-                       }
-                       if(idx)
-                       {
-                               self.oldorigin = self.movedir - gettaginfo(self, idx);
-                       }
-                       else
-                       {
-                               print("WARNING: weapon model ", self.model, " does not support the 'handle' tag and neither does the v_ model support the 'shot' tag, will display muzzle flashes TOTALLY wrong\n");
-                               self.oldorigin = '0 0 0'; // there is no way to recover from this
-                       }
-               }
-
-               self.viewmodelforclient = self.owner;
-       }
-       else
-       {
-               self.model = "";
-               if(self.weaponentity)
-                       remove(self.weaponentity);
-               self.weaponentity = world;
-               self.movedir = '0 0 0';
-               self.spawnorigin = '0 0 0';
-               self.oldorigin = '0 0 0';
-               self.anim_fire1  = '0 1 0.01';
-               self.anim_fire2  = '0 1 0.01';
-               self.anim_idle   = '0 1 0.01';
-               self.anim_reload = '0 1 0.01';
-       }
-
-       self.view_ofs = '0 0 0';
-
-       if(self.movedir_x >= 0)
-       {
-               vector v0;
-               v0 = self.movedir;
-               self.movedir = shotorg_adjust(v0, FALSE, FALSE);
-               self.view_ofs = shotorg_adjust(v0, FALSE, TRUE) - v0;
-       }
-       self.owner.stat_shotorg = compressShotOrigin(self.movedir);
-       self.movedir = decompressShotOrigin(self.owner.stat_shotorg); // make them match perfectly
-
-       self.spawnorigin += self.view_ofs; // offset the casings origin by the same amount
-
-       // check if an instant weapon switch occurred
-       setorigin(self, self.view_ofs);
-       // reset animstate now
-       self.wframe = WFRAME_IDLE;
-       setanim(self, self.anim_idle, TRUE, FALSE, TRUE);
-}
-
-vector CL_Weapon_GetShotOrg(float wpn)
-{
-       entity wi, oldself;
-       vector ret;
-       wi = get_weaponinfo(wpn);
-       oldself = self;
-       self = spawn();
-       CL_WeaponEntity_SetModel(wi.mdl);
-       ret = self.movedir;
-       CL_WeaponEntity_SetModel("");
-       remove(self);
-       self = oldself;
-       return ret;
-}
-
-void CL_Weaponentity_Think()
-{
-       float tb;
-       self.nextthink = time;
-       if (intermission_running)
-               self.frame = self.anim_idle_x;
-       if (self.owner.weaponentity != self)
-       {
-               if (self.weaponentity)
-                       remove(self.weaponentity);
-               remove(self);
-               return;
-       }
-       if (self.owner.deadflag != DEAD_NO)
-       {
-               self.model = "";
-               if (self.weaponentity)
-                       self.weaponentity.model = "";
-               return;
-       }
-       if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
-       {
-               self.weaponname = self.owner.weaponname;
-               self.dmg = self.owner.modelindex;
-               self.deadflag = self.owner.deadflag;
-
-               CL_WeaponEntity_SetModel(self.owner.weaponname);
-       }
-
-       tb = (self.effects & (EF_TELEPORT_BIT | EF_RESTARTANIM_BIT));
-       self.effects = self.owner.effects & EFMASK_CHEAP;
-       self.effects &= ~EF_LOWPRECISION;
-       self.effects &= ~EF_FULLBRIGHT; // can mask team color, so get rid of it
-       self.effects &= ~EF_TELEPORT_BIT;
-       self.effects &= ~EF_RESTARTANIM_BIT;
-       self.effects |= tb;
-
-       if(self.owner.alpha == default_player_alpha)
-               self.alpha = default_weapon_alpha;
-       else if(self.owner.alpha != 0)
-               self.alpha = self.owner.alpha;
-       else
-               self.alpha = 1;
-
-       self.glowmod = self.owner.weaponentity_glowmod;
-       self.colormap = self.owner.colormap;
-       if (self.weaponentity)
-       {
-               self.weaponentity.effects = self.effects;
-               self.weaponentity.alpha = self.alpha;
-               self.weaponentity.colormap = self.colormap;
-               self.weaponentity.glowmod = self.glowmod;
-       }
-
-       self.angles = '0 0 0';
-
-       float f = (self.owner.weapon_nextthink - time);
-       if (self.state == WS_RAISE && !intermission_running)
-       {
-               entity newwep = get_weaponinfo(self.owner.switchweapon);
-               f = f * g_weaponratefactor / max(f, cvar(sprintf("g_balance_%s_switchdelay_raise", newwep.netname)));
-               //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, cvar(sprintf("g_balance_%s_switchdelay_drop", oldwep.netname)));
-               //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;
-       }
-}
-
-void Send_WeaponComplain (entity e, float wpn, string wpnname, float type)
-{
-       msg_entity = e;
-       WriteByte(MSG_ONE, SVC_TEMPENTITY);
-       WriteByte(MSG_ONE, TE_CSQC_WEAPONCOMPLAIN);
-       WriteByte(MSG_ONE, wpn);
-       WriteString(MSG_ONE, wpnname);
-       WriteByte(MSG_ONE, type);
-}
-
-.float hasweapon_complain_spam;
-
-float client_hasweapon(entity cl, float wpn, float andammo, float complain)
-{
-       float f;
-       entity oldself;
-
-       if(time < self.hasweapon_complain_spam)
-               complain = 0;
-       if(complain)
-               self.hasweapon_complain_spam = time + 0.2;
-
-       if(wpn == WEP_HOOK && !g_grappling_hook && autocvar_g_nades && !((cl.weapons | weaponsInMap) & WepSet_FromWeapon(wpn)))
-               complain = 0;
-
-       if (wpn < WEP_FIRST || wpn > WEP_LAST)
-       {
-               if (complain)
-                       sprint(self, "Invalid weapon\n");
-               return FALSE;
-       }
-       if (cl.weapons & WepSet_FromWeapon(wpn))
-       {
-               if (andammo)
-               {
-                       if(cl.items & IT_UNLIMITED_WEAPON_AMMO)
-                       {
-                               f = 1;
-                       }
-                       else
-                       {
-                               oldself = self;
-                               self = cl;
-                               f = weapon_action(wpn, WR_CHECKAMMO1);
-                               f = f + weapon_action(wpn, WR_CHECKAMMO2);
-
-                               // always allow selecting the Mine Layer if we placed mines, so that we can detonate them
-                               entity mine;
-                               if(wpn == WEP_MINE_LAYER)
-                               for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
-                                       f = 1;
-
-                               self = oldself;
-                       }
-                       if (!f)
-                       {
-                               if (complain)
-                               if(IS_REAL_CLIENT(cl))
-                               {
-                                       play2(cl, "weapons/unavailable.wav");
-                                       Send_WeaponComplain (cl, wpn, W_Name(wpn), 0);
-                               }
-                               return FALSE;
-                       }
-               }
-               return TRUE;
-       }
-       if (complain)
-       {
-               // DRESK - 3/16/07
-               // Report Proper Weapon Status / Modified Weapon Ownership Message
-               if (weaponsInMap & WepSet_FromWeapon(wpn))
-               {
-                       Send_WeaponComplain(cl, wpn, W_Name(wpn), 1);
-
-                       if(autocvar_g_showweaponspawns)
-                       {
-                               entity e;
-                               string s;
-
-                               e = get_weaponinfo(wpn);
-                               s = e.model2;
-
-                               for(e = world; (e = findfloat(e, weapon, wpn)); )
-                               {
-                                       if(e.classname == "droppedweapon")
-                                               continue;
-                                       if (!(e.flags & FL_ITEM))
-                                               continue;
-                                       WaypointSprite_Spawn(
-                                               s,
-                                               1, 0,
-                                               world, e.origin,
-                                               self, 0,
-                                               world, enemy,
-                                               0,
-                                               RADARICON_NONE, '0 0 0'
-                                       );
-                               }
-                       }
-               }
-               else
-               {
-                       Send_WeaponComplain (cl, wpn, W_Name(wpn), 2);
-               }
-
-               play2(cl, "weapons/unavailable.wav");
-       }
-       return FALSE;
-}
-
-// Weapon subs
-void w_clear()
-{
-       if (self.weapon != -1)
-       {
-               self.weapon = 0;
-               self.switchingweapon = 0;
-       }
-       if (self.weaponentity)
-       {
-               self.weaponentity.state = WS_CLEAR;
-               self.weaponentity.effects = 0;
-       }
-}
-
-void w_ready()
-{
-       if (self.weaponentity)
-               self.weaponentity.state = WS_READY;
-       weapon_thinkf(WFRAME_IDLE, 1000000, w_ready);
-}
-
-// Setup weapon for client (after this raise frame will be launched)
-void weapon_setup(float windex)
-{
-       entity e;
-       e = get_weaponinfo(windex);
-       self.items &= ~IT_AMMO;
-       self.items = self.items | (e.items & IT_AMMO);
-
-       // the two weapon entities will notice this has changed and update their models
-       self.weapon = windex;
-       self.switchingweapon = windex; // to make sure
-       self.weaponname = e.mdl;
-       self.bulletcounter = 0;
-}
-
-// perform weapon to attack (weaponstate and attack_finished check is here)
-void W_SwitchToOtherWeapon(entity pl)
-{
-       // hack to ensure it switches to an OTHER weapon (in case the other fire mode still has ammo, we want that anyway)
-       float w, ww;
-       w = pl.weapon;
-       if(pl.weapons & WepSet_FromWeapon(w))
-       {
-               pl.weapons &= ~WepSet_FromWeapon(w);
-               ww = w_getbestweapon(pl);
-               pl.weapons |= WepSet_FromWeapon(w);
-       }
-       else
-               ww = w_getbestweapon(pl);
-       if(ww)
-               W_SwitchWeapon_Force(pl, ww);
-}
-
-.float prevdryfire;
-.float prevwarntime;
-float weapon_prepareattack_checkammo(float secondary)
-{
-       if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
-       if (!weapon_action(self.weapon, WR_CHECKAMMO1 + secondary))
-       {
-               // always keep the Mine Layer if we placed mines, so that we can detonate them
-               entity mine;
-               if(self.weapon == WEP_MINE_LAYER)
-               for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
-                       return FALSE;
-
-               if(self.weapon == self.switchweapon && time - self.prevdryfire > 1) // only play once BEFORE starting to switch weapons
-               {
-                       sound (self, CH_WEAPON_A, "weapons/dryfire.wav", VOL_BASE, ATTEN_NORM);
-                       self.prevdryfire = time;
-               }
-
-               if(weapon_action(self.weapon, WR_CHECKAMMO2 - secondary)) // check if the other firing mode has enough ammo
-               {
-                       if(time - self.prevwarntime > 1)
-                       {
-                               Send_Notification(
-                                       NOTIF_ONE,
-                                       self,
-                                       MSG_MULTI,
-                                       ITEM_WEAPON_PRIMORSEC,
-                                       self.weapon,
-                                       secondary,
-                                       (1 - secondary)
-                               );
-                       }
-                       self.prevwarntime = time;
-               }
-               else // this weapon is totally unable to fire, switch to another one
-               {
-                       W_SwitchToOtherWeapon(self);
-               }
-
-               return FALSE;
-       }
-       return TRUE;
-}
-.float race_penalty;
-float weapon_prepareattack_check(float secondary, float attacktime)
-{
-       if(!weapon_prepareattack_checkammo(secondary))
-               return FALSE;
-
-       //if sv_ready_restart_after_countdown is set, don't allow the player to shoot
-       //if all players readied up and the countdown is running
-       if(time < game_starttime || time < self.race_penalty) {
-               return FALSE;
-       }
-
-       if (timeout_status == TIMEOUT_ACTIVE) //don't allow the player to shoot while game is paused
-               return FALSE;
-
-       // do not even think about shooting if switching
-       if(self.switchweapon != self.weapon)
-               return FALSE;
-
-       if(attacktime >= 0)
-       {
-               // don't fire if previous attack is not finished
-               if (ATTACK_FINISHED(self) > time + self.weapon_frametime * 0.5)
-                       return FALSE;
-               // don't fire while changing weapon
-               if (self.weaponentity.state != WS_READY)
-                       return FALSE;
-       }
-
-       return TRUE;
-}
-float weapon_prepareattack_do(float secondary, float attacktime)
-{
-       self.weaponentity.state = WS_INUSE;
-
-       self.spawnshieldtime = min(self.spawnshieldtime, time); // kill spawn shield when you fire
-
-       // if the weapon hasn't been firing continuously, reset the timer
-       if(attacktime >= 0)
-       {
-               if (ATTACK_FINISHED(self) < time - self.weapon_frametime * 1.5)
-               {
-                       ATTACK_FINISHED(self) = time;
-                       //dprint("resetting attack finished to ", ftos(time), "\n");
-               }
-               ATTACK_FINISHED(self) = ATTACK_FINISHED(self) + attacktime * W_WeaponRateFactor();
-       }
-       self.bulletcounter += 1;
-       //dprint("attack finished ", ftos(ATTACK_FINISHED(self)), "\n");
-       return TRUE;
-}
-float weapon_prepareattack(float secondary, float attacktime)
-{
-       if(weapon_prepareattack_check(secondary, attacktime))
-       {
-               weapon_prepareattack_do(secondary, attacktime);
-               return TRUE;
-       }
-       else
-               return FALSE;
-}
-
-void weapon_thinkf(float fr, float t, void() func)
-{
-       vector a;
-       vector of, or, ou;
-       float restartanim;
-
-       if(fr == WFRAME_DONTCHANGE)
-       {
-               fr = self.weaponentity.wframe;
-               restartanim = FALSE;
-       }
-       else if (fr == WFRAME_IDLE)
-               restartanim = FALSE;
-       else
-               restartanim = TRUE;
-
-       of = v_forward;
-       or = v_right;
-       ou = v_up;
-
-       if (self.weaponentity)
-       {
-               self.weaponentity.wframe = fr;
-               a = '0 0 0';
-               if (fr == WFRAME_IDLE)
-                       a = self.weaponentity.anim_idle;
-               else if (fr == WFRAME_FIRE1)
-                       a = self.weaponentity.anim_fire1;
-               else if (fr == WFRAME_FIRE2)
-                       a = self.weaponentity.anim_fire2;
-               else // if (fr == WFRAME_RELOAD)
-                       a = self.weaponentity.anim_reload;
-               a_z *= g_weaponratefactor;
-               setanim(self.weaponentity, a, restartanim == FALSE, restartanim, restartanim);
-       }
-
-       v_forward = of;
-       v_right = or;
-       v_up = ou;
-
-       if(self.weapon_think == w_ready && func != w_ready && self.weaponentity.state == WS_RAISE)
-       {
-               backtrace("Tried to override initial weapon think function - should this really happen?");
-       }
-
-       t *= W_WeaponRateFactor();
-
-       // VorteX: haste can be added here
-       if (self.weapon_think == w_ready)
-       {
-               self.weapon_nextthink = time;
-               //dprint("started firing at ", ftos(time), "\n");
-       }
-       if (self.weapon_nextthink < time - self.weapon_frametime * 1.5 || self.weapon_nextthink > time + self.weapon_frametime * 1.5)
-       {
-               self.weapon_nextthink = time;
-               //dprint("reset weapon animation timer at ", ftos(time), "\n");
-       }
-       self.weapon_nextthink = self.weapon_nextthink + t;
-       self.weapon_think = func;
-       //dprint("next ", ftos(self.weapon_nextthink), "\n");
-
-       if((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t)
-       {
-               if(self.weapon == WEP_SHOTGUN && fr == WFRAME_FIRE2)
-                       animdecide_setaction(self, ANIMACTION_MELEE, restartanim);
-               else
-                       animdecide_setaction(self, ANIMACTION_SHOOT, restartanim);
-       }
-       else
-       {
-               if(self.anim_upper_action == ANIMACTION_SHOOT || self.anim_upper_action == ANIMACTION_MELEE)
-                       self.anim_upper_action = 0;
-       }
-}
-
-void weapon_boblayer1(float spd, vector org)
-{
-       // VorteX: haste can be added here
-}
-
-vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity, float forceAbsolute)
-{
-       vector mdirection;
-       float mspeed;
-       vector outvelocity;
-
-       mvelocity = mvelocity * g_weaponspeedfactor;
-
-       mdirection = normalize(mvelocity);
-       mspeed = vlen(mvelocity);
-
-       outvelocity = get_shotvelocity(pvelocity, mdirection, mspeed, (forceAbsolute ? 0 : autocvar_g_projectiles_newton_style), autocvar_g_projectiles_newton_style_2_minfactor, autocvar_g_projectiles_newton_style_2_maxfactor);
-
-       return outvelocity;
-}
-
-void W_AttachToShotorg(entity flash, vector offset)
-{
-       entity xflash;
-       flash.owner = self;
-       flash.angles_z = random() * 360;
-
-       if(gettagindex(self.weaponentity, "shot"))
-               setattachment(flash, self.weaponentity, "shot");
-       else
-               setattachment(flash, self.weaponentity, "tag_shot");
-       setorigin(flash, offset);
-
-       xflash = spawn();
-       copyentity(flash, xflash);
-
-       flash.viewmodelforclient = self;
-
-       if(self.weaponentity.oldorigin_x > 0)
-       {
-               setattachment(xflash, self.exteriorweaponentity, "");
-               setorigin(xflash, self.weaponentity.oldorigin + offset);
-       }
-       else
-       {
-               if(gettagindex(self.exteriorweaponentity, "shot"))
-                       setattachment(xflash, self.exteriorweaponentity, "shot");
-               else
-                       setattachment(xflash, self.exteriorweaponentity, "tag_shot");
-               setorigin(xflash, offset);
-       }
-}
-
-vector cliptoplane(vector v, vector p)
-{
-       return v - (v * p) * p;
-}
-
-vector solve_cubic_pq(float p, float q)
-{
-       float D, u, v, a;
-       D = q*q/4.0 + p*p*p/27.0;
-       if(D < 0)
-       {
-               // irreducibilis
-               a = 1.0/3.0 * acos(-q/2.0 * sqrt(-27.0/(p*p*p)));
-               u = sqrt(-4.0/3.0 * p);
-               // a in range 0..pi/3
-               // cos(a)
-               // cos(a + 2pi/3)
-               // cos(a + 4pi/3)
-               return
-                       u *
-                       (
-                               '1 0 0' * cos(a + 2.0/3.0*M_PI)
-                               +
-                               '0 1 0' * cos(a + 4.0/3.0*M_PI)
-                               +
-                               '0 0 1' * cos(a)
-                       );
-       }
-       else if(D == 0)
-       {
-               // simple
-               if(p == 0)
-                       return '0 0 0';
-               u = 3*q/p;
-               v = -u/2;
-               if(u >= v)
-                       return '1 1 0' * v + '0 0 1' * u;
-               else
-                       return '0 1 1' * v + '1 0 0' * u;
-       }
-       else
-       {
-               // cardano
-               u = cbrt(-q/2.0 + sqrt(D));
-               v = cbrt(-q/2.0 - sqrt(D));
-               return '1 1 1' * (u + v);
-       }
-}
-vector solve_cubic_abcd(float a, float b, float c, float d)
-{
-       // y = 3*a*x + b
-       // x = (y - b) / 3a
-       float p, q;
-       vector v;
-       p = (9*a*c - 3*b*b);
-       q = (27*a*a*d - 9*a*b*c + 2*b*b*b);
-       v = solve_cubic_pq(p, q);
-       v = (v -  b * '1 1 1') * (1.0 / (3.0 * a));
-       if(a < 0)
-               v += '1 0 -1' * (v_z - v_x); // swap x, z
-       return v;
-}
-
-vector findperpendicular(vector v)
-{
-       vector p;
-       p_x = v_z;
-       p_y = -v_x;
-       p_z = v_y;
-       return normalize(cliptoplane(p, v));
-}
-
-vector W_CalculateProjectileSpread(vector forward, float spread)
-{
-       float sigma;
-       vector v1 = '0 0 0', v2;
-       float dx, dy, r;
-       float sstyle;
-       spread *= g_weaponspreadfactor;
-       if(spread <= 0)
-               return forward;
-       sstyle = autocvar_g_projectiles_spread_style;
-
-       if(sstyle == 0)
-       {
-               // this is the baseline for the spread value!
-               // standard deviation: sqrt(2/5)
-               // density function: sqrt(1-r^2)
-               return forward + randomvec() * spread;
-       }
-       else if(sstyle == 1)
-       {
-               // same thing, basically
-               return normalize(forward + cliptoplane(randomvec() * spread, forward));
-       }
-       else if(sstyle == 2)
-       {
-               // circle spread... has at sigma=1 a standard deviation of sqrt(1/2)
-               sigma = spread * 0.89442719099991587855; // match baseline stddev
-               v1 = findperpendicular(forward);
-               v2 = cross(forward, v1);
-               // random point on unit circle
-               dx = random() * 2 * M_PI;
-               dy = sin(dx);
-               dx = cos(dx);
-               // radius in our dist function
-               r = random();
-               r = sqrt(r);
-               return normalize(forward + (v1 * dx + v2 * dy) * r * sigma);
-       }
-       else if(sstyle == 3) // gauss 3d
-       {
-               sigma = spread * 0.44721359549996; // match baseline stddev
-               // note: 2D gaussian has sqrt(2) times the stddev of 1D, so this factor is right
-               v1 = forward;
-               v1_x += gsl_ran_gaussian(sigma);
-               v1_y += gsl_ran_gaussian(sigma);
-               v1_z += gsl_ran_gaussian(sigma);
-               return v1;
-       }
-       else if(sstyle == 4) // gauss 2d
-       {
-               sigma = spread * 0.44721359549996; // match baseline stddev
-               // note: 2D gaussian has sqrt(2) times the stddev of 1D, so this factor is right
-               v1_x = gsl_ran_gaussian(sigma);
-               v1_y = gsl_ran_gaussian(sigma);
-               v1_z = gsl_ran_gaussian(sigma);
-               return normalize(forward + cliptoplane(v1, forward));
-       }
-       else if(sstyle == 5) // 1-r
-       {
-               sigma = spread * 1.154700538379252; // match baseline stddev
-               v1 = findperpendicular(forward);
-               v2 = cross(forward, v1);
-               // random point on unit circle
-               dx = random() * 2 * M_PI;
-               dy = sin(dx);
-               dx = cos(dx);
-               // radius in our dist function
-               r = random();
-               r = solve_cubic_abcd(-2, 3, 0, -r) * '0 1 0';
-               return normalize(forward + (v1 * dx + v2 * dy) * r * sigma);
-       }
-       else if(sstyle == 6) // 1-r^2
-       {
-               sigma = spread * 1.095445115010332; // match baseline stddev
-               v1 = findperpendicular(forward);
-               v2 = cross(forward, v1);
-               // random point on unit circle
-               dx = random() * 2 * M_PI;
-               dy = sin(dx);
-               dx = cos(dx);
-               // radius in our dist function
-               r = random();
-               r = sqrt(1 - r);
-               r = sqrt(1 - r);
-               return normalize(forward + (v1 * dx + v2 * dy) * r * sigma);
-       }
-       else if(sstyle == 7) // (1-r) (2-r)
-       {
-               sigma = spread * 1.224744871391589; // match baseline stddev
-               v1 = findperpendicular(forward);
-               v2 = cross(forward, v1);
-               // random point on unit circle
-               dx = random() * 2 * M_PI;
-               dy = sin(dx);
-               dx = cos(dx);
-               // radius in our dist function
-               r = random();
-               r = 1 - sqrt(r);
-               r = 1 - sqrt(r);
-               return normalize(forward + (v1 * dx + v2 * dy) * r * sigma);
-       }
-       else
-               error("g_projectiles_spread_style must be 0 (sphere), 1 (flattened sphere), 2 (circle), 3 (gauss 3D), 4 (gauss plane), 5 (linear falloff), 6 (quadratic falloff), 7 (stronger falloff)!");
-       return '0 0 0';
-       /*
-        * how to derive falloff functions:
-        * rho(r) := (2-r) * (1-r);
-        * a : 0;
-        * b : 1;
-        * rhor(r) := r * rho(r);
-        * cr(t) := integrate(rhor(r), r, a, t);
-        * scr(t) := integrate(rhor(r) * r^2, r, a, t);
-        * variance : scr(b) / cr(b);
-        * solve(cr(r) = rand * cr(b), r), programmmode:false;
-        * sqrt(0.4 / variance), numer;
-        */
-}
-
-#if 0
-float mspercallsum;
-float mspercallsstyle;
-float mspercallcount;
-#endif
-void W_SetupProjectileVelocityEx(entity missile, vector dir, vector upDir, float pSpeed, float pUpSpeed, float pZSpeed, float spread, float forceAbsolute)
-{
-       if(missile.owner == world)
-               error("Unowned missile");
-
-       dir = dir + upDir * (pUpSpeed / pSpeed);
-       dir_z += pZSpeed / pSpeed;
-       pSpeed *= vlen(dir);
-       dir = normalize(dir);
-
-#if 0
-       if(autocvar_g_projectiles_spread_style != mspercallsstyle)
-       {
-               mspercallsum = mspercallcount = 0;
-               mspercallsstyle = autocvar_g_projectiles_spread_style;
-       }
-       mspercallsum -= gettime(GETTIME_HIRES);
-#endif
-       dir = W_CalculateProjectileSpread(dir, spread);
-#if 0
-       mspercallsum += gettime(GETTIME_HIRES);
-       mspercallcount += 1;
-       print("avg: ", ftos(mspercallcount / mspercallsum), " per sec\n");
-#endif
-
-       missile.velocity = W_CalculateProjectileVelocity(missile.owner.velocity, pSpeed * dir, forceAbsolute);
-}
-
-void W_SetupProjectileVelocity(entity missile, float pSpeed, float spread)
-{
-       W_SetupProjectileVelocityEx(missile, w_shotdir, v_up, pSpeed, 0, 0, spread, FALSE);
-}
-
-#define W_SETUPPROJECTILEVELOCITY_UP(m,s) W_SetupProjectileVelocityEx(m, w_shotdir, v_up, cvar(#s "_speed"), cvar(#s "_speed_up"), cvar(#s "_speed_z"), cvar(#s "_spread"), FALSE)
-#define W_SETUPPROJECTILEVELOCITY(m,s) W_SetupProjectileVelocityEx(m, w_shotdir, v_up, cvar(#s "_speed"), 0, 0, cvar(#s "_spread"), FALSE)
-
-void W_DecreaseAmmo(.float ammo_type, float ammo_use, float ammo_reload)
-{
-       if((self.items & IT_UNLIMITED_WEAPON_AMMO) && !ammo_reload)
-               return;
-
-       // if this weapon is reloadable, decrease its load. Else decrease the player's ammo
-       if(ammo_reload)
-       {
-               self.clip_load -= ammo_use;
-               self.(weapon_load[self.weapon]) = self.clip_load;
-       }
-       else
-               self.(self.current_ammo) -= ammo_use;
-}
-
-// weapon reloading code
-
-.float reload_ammo_amount, reload_ammo_min, reload_time;
-.float reload_complain;
-.string reload_sound;
-
-void W_ReloadedAndReady()
-{
-       // finish the reloading process, and do the ammo transfer
-
-       self.clip_load = self.old_clip_load; // restore the ammo counter, in case we still had ammo in the weapon before reloading
-
-       // if the gun uses no ammo, max out weapon load, else decrease ammo as we increase weapon load
-       if(!self.reload_ammo_min || self.items & IT_UNLIMITED_WEAPON_AMMO)
-               self.clip_load = self.reload_ammo_amount;
-       else
-       {
-               while(self.clip_load < self.reload_ammo_amount && self.(self.current_ammo)) // make sure we don't add more ammo than we have
-               {
-                       self.clip_load += 1;
-                       self.(self.current_ammo) -= 1;
-               }
-       }
-       self.(weapon_load[self.weapon]) = self.clip_load;
-
-       // do not set ATTACK_FINISHED in reload code any more. This causes annoying delays if eg: You start reloading a weapon,
-       // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there,
-       // so your weapon is disabled for a few seconds without reason
-
-       //ATTACK_FINISHED(self) -= self.reload_time - 1;
-
-       w_ready();
-}
-
-void W_Reload(float sent_ammo_min, float sent_ammo_amount, float sent_time, string sent_sound)
-{
-       // set global values to work with
-
-       self.reload_ammo_min = sent_ammo_min;
-       self.reload_ammo_amount = sent_ammo_amount;
-       self.reload_time = sent_time;
-       self.reload_sound = sent_sound;
-
-       // check if we meet the necessary conditions to reload
-
-       entity e;
-       e = get_weaponinfo(self.weapon);
-
-       // don't reload weapons that don't have the RELOADABLE flag
-       if (!(e.spawnflags & WEP_FLAG_RELOADABLE))
-       {
-               dprint("Warning: Attempted to reload a weapon that does not have the WEP_FLAG_RELOADABLE flag. Fix your code!\n");
-               return;
-       }
-
-       // return if reloading is disabled for this weapon
-       if(!self.reload_ammo_amount)
-               return;
-
-       // our weapon is fully loaded, no need to reload
-       if (self.clip_load >= self.reload_ammo_amount)
-               return;
-
-       // no ammo, so nothing to load
-       if(!self.(self.current_ammo) && self.reload_ammo_min)
-       if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
-       {
-               if(IS_REAL_CLIENT(self) && self.reload_complain < time)
-               {
-                       play2(self, "weapons/unavailable.wav");
-                       sprint(self, strcat("You don't have enough ammo to reload the ^2", W_Name(self.weapon), "\n"));
-                       self.reload_complain = time + 1;
-               }
-               // switch away if the amount of ammo is not enough to keep using this weapon
-               if (!(weapon_action(self.weapon, WR_CHECKAMMO1) + weapon_action(self.weapon, WR_CHECKAMMO2)))
-               {
-                       self.clip_load = -1; // reload later
-                       W_SwitchToOtherWeapon(self);
-               }
-               return;
-       }
-
-       if (self.weaponentity)
-       {
-               if (self.weaponentity.wframe == WFRAME_RELOAD)
-                       return;
-
-               // allow switching away while reloading, but this will cause a new reload!
-               self.weaponentity.state = WS_READY;
-       }
-
-       // now begin the reloading process
-
-       sound (self, CH_WEAPON_SINGLE, self.reload_sound, VOL_BASE, ATTEN_NORM);
-
-       // do not set ATTACK_FINISHED in reload code any more. This causes annoying delays if eg: You start reloading a weapon,
-       // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there,
-       // so your weapon is disabled for a few seconds without reason
-
-       //ATTACK_FINISHED(self) = max(time, ATTACK_FINISHED(self)) + self.reload_time + 1;
-
-       weapon_thinkf(WFRAME_RELOAD, self.reload_time, W_ReloadedAndReady);
-
-       if(self.clip_load < 0)
-               self.clip_load = 0;
-       self.old_clip_load = self.clip_load;
-       self.clip_load = self.(weapon_load[self.weapon]) = -1;
-}
index 4b626546d6d78d0409957691efa39b7de402ef74..ea13aedd5588e0c599ffc835935e61247de485b3 100644 (file)
@@ -1,7 +1,7 @@
 void te_csqc_lightningarc(vector from,vector to)
 {
        WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
-       WriteByte(MSG_BROADCAST, TE_CSQC_LIGHTNINGARC);
+       WriteByte(MSG_BROADCAST, TE_CSQC_ARC);
 
        WriteCoord(MSG_BROADCAST, from_x);
        WriteCoord(MSG_BROADCAST, from_y);
diff --git a/qcsrc/server/csqcprojectile.qc b/qcsrc/server/csqcprojectile.qc
deleted file mode 100644 (file)
index 4d208eb..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-.float csqcprojectile_type;
-
-float CSQCProjectile_SendEntity(entity to, float sf)
-{
-       float ft, fr;
-
-       // note: flag 0x08 = no trail please (teleport bit)
-       sf = sf & 0x0F;
-
-       if(self.csqcprojectile_clientanimate)
-               sf |= 0x80; // client animated, not interpolated
-
-       if(self.flags & FL_ONGROUND)
-               sf |= 0x40;
-
-       ft = fr = 0;
-       if(self.fade_time != 0 || self.fade_rate != 0)
-       {
-               ft = (self.fade_time - time) / sys_frametime;
-               fr = (1 / self.fade_rate) / sys_frametime;
-               if(ft <= 255 && fr <= 255 && fr >= 1)
-                       sf |= 0x20;
-       }
-
-       if(self.gravity != 0)
-               sf |= 0x10;
-
-       WriteByte(MSG_ENTITY, ENT_CLIENT_PROJECTILE);
-       WriteByte(MSG_ENTITY, sf);
-
-       if(sf & 1)
-       {
-               WriteCoord(MSG_ENTITY, self.origin_x);
-               WriteCoord(MSG_ENTITY, self.origin_y);
-               WriteCoord(MSG_ENTITY, self.origin_z);
-
-               if(sf & 0x80)
-               {
-                       WriteCoord(MSG_ENTITY, self.velocity_x);
-                       WriteCoord(MSG_ENTITY, self.velocity_y);
-                       WriteCoord(MSG_ENTITY, self.velocity_z);
-                       if(sf & 0x10)
-                               WriteCoord(MSG_ENTITY, self.gravity);
-               }
-
-               if(sf & 0x20)
-               {
-                       WriteByte(MSG_ENTITY, ft);
-                       WriteByte(MSG_ENTITY, fr);
-               }
-       }
-
-       if(sf & 2)
-               WriteByte(MSG_ENTITY, self.csqcprojectile_type); // TODO maybe put this into sf?
-
-       return 1;
-}
-
-.vector csqcprojectile_oldorigin;
-void CSQCProjectile_Check(entity e)
-{
-       if(e.csqcprojectile_clientanimate)
-       if(e.flags & FL_ONGROUND)
-       if(e.origin != e.csqcprojectile_oldorigin)
-               UpdateCSQCProjectile(e);
-       e.csqcprojectile_oldorigin = e.origin;
-}
-
-void CSQCProjectile(entity e, float clientanimate, float type, float docull)
-{
-       Net_LinkEntity(e, docull, 0, CSQCProjectile_SendEntity);
-
-       e.csqcprojectile_clientanimate = clientanimate;
-
-       if(e.movetype == MOVETYPE_TOSS || e.movetype == MOVETYPE_BOUNCE)
-       {
-               if(e.gravity == 0)
-                       e.gravity = 1;
-       }
-       else
-               e.gravity = 0;
-
-       if(!sound_allowed(MSG_BROADCAST, e))
-               type |= 0x80;
-       e.csqcprojectile_type = type;
-}
-
-// FIXME HACK
-float ItemSend(entity to, float sf);
-void ItemUpdate(entity item);
-// END HACK
-void UpdateCSQCProjectile(entity e)
-{
-       if(e.SendEntity == CSQCProjectile_SendEntity)
-       {
-               // send new origin data
-               e.SendFlags |= 0x01;
-       }
-// FIXME HACK
-       else if(e.SendEntity == ItemSend)
-       {
-               ItemUpdate(e);
-       }
-// END HACK
-}
-
-void UpdateCSQCProjectileAfterTeleport(entity e)
-{
-       if(e.SendEntity == CSQCProjectile_SendEntity)
-       {
-               // send new origin data
-               e.SendFlags |= 0x01;
-               // mark as teleported
-               e.SendFlags |= 0x08;
-       }
-}
diff --git a/qcsrc/server/csqcprojectile.qh b/qcsrc/server/csqcprojectile.qh
deleted file mode 100644 (file)
index e00c098..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-.float csqcprojectile_clientanimate;
-
-void CSQCProjectile(entity e, float clientanimate, float type, float docull);
-void UpdateCSQCProjectile(entity e);
-void UpdateCSQCProjectileAfterTeleport(entity e);
-void CSQCProjectile_Check(entity e);
index 72f4457d61d9fe99dff84bc1c2e728b2dcacab62..0ebc93756bc0c2b82e3d5bd8da5f5f2bf8adbd6e 100644 (file)
@@ -183,8 +183,9 @@ void setanim(entity e, vector anim, float looping, float override, float restart
 .float switchingweapon; // weapon currently being switched to (is copied from switchweapon once switch is possible)
 .string weaponname; // name of .weapon
 
+// WEAPONTODO
 .float autoswitch;
-float weapon_action(float wpn, float wrequest);
+//float WEP_ACTION(float wpn, float wrequest);
 float client_hasweapon(entity cl, float wpn, float andammo, float complain);
 void w_clear();
 void w_ready();
@@ -192,9 +193,6 @@ void w_ready();
 .float weapon_nextthink;
 .void() weapon_think;
 
-//float        PLAYER_WEAPONSELECTION_DELAY = );
-const float    PLAYER_WEAPONSELECTION_SPEED = 18;
-const vector   PLAYER_WEAPONSELECTION_RANGE = '0 20 -40';
 
 // weapon states (self.weaponentity.state)
 const float WS_CLEAR                   = 0; // no weapon selected
@@ -513,7 +511,6 @@ float servertime, serverprevtime, serverframetime;
 .float stat_shotorg; // networked stat for trueaim HUD
 
 string matchid;
-.float hitplotfh;
 
 .float last_pickup;
 
@@ -561,9 +558,6 @@ string deathmessage;
 
 .float just_joined;
 
-.float cvar_cl_accuracy_data_share;
-.float cvar_cl_accuracy_data_receive;
-
 .float cvar_cl_weaponimpulsemode;
 .float selectweapon; // last selected weapon of the player
 
index d67e79cd4d3396e45b74e9747291036680fc968d..0bf71059e6029ae10d9e94b1ed0ba861842cd609 100644 (file)
@@ -173,7 +173,7 @@ void func_breakable_destroy() {
                sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
 
        if(self.dmg)
-               RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, self.dmg_force, DEATH_HURTTRIGGER, world);
+               RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, world, self.dmg_force, DEATH_HURTTRIGGER, world);
 
        if(self.cnt)
                pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count);
index 195829b40d00c701f9e95d494b19178caeb1a815..7b63c3866016c2dd1faf904d7e78b14f3d3c01a8 100644 (file)
@@ -287,7 +287,7 @@ float Obituary_WeaponDeath(
        if(death_weapon)
        {
                w_deathtype = deathtype;
-               float death_message = weapon_action(death_weapon, ((murder) ? WR_KILLMESSAGE : WR_SUICIDEMESSAGE));
+               float death_message = WEP_ACTION(death_weapon, ((murder) ? WR_KILLMESSAGE : WR_SUICIDEMESSAGE));
                w_deathtype = FALSE;
 
                if(death_message)
@@ -817,11 +817,10 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float
 }
 
 float RadiusDamage_running;
-float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity ignore, float forceintensity, float deathtype, entity directhitentity)
+float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float inflictorselfdamage, float forceintensity, float deathtype, entity directhitentity)
        // Returns total damage applies to creatures
 {
        entity  targ;
-       vector  blastorigin;
        vector  force;
        float   total_damage_to_creatures;
        entity  next;
@@ -841,202 +840,156 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e
        tfloordmg = autocvar_g_throughfloor_damage;
        tfloorforce = autocvar_g_throughfloor_force;
 
-       blastorigin = (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5);
        total_damage_to_creatures = 0;
 
        if(deathtype != (WEP_HOOK | HITTYPE_SECONDARY | HITTYPE_BOUNCE)) // only send gravity bomb damage once
                if(DEATH_WEAPONOF(deathtype) != WEP_TUBA) // do not send tuba damage (bandwidth hog)
                {
-                       force = inflictor.velocity;
+                       force = inflictorvelocity;
                        if(vlen(force) == 0)
                                force = '0 0 -1';
                        else
                                force = normalize(force);
                        if(forceintensity >= 0)
-                               Damage_DamageInfo(blastorigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker);
+                               Damage_DamageInfo(inflictororigin, coredamage, edgedamage, rad, forceintensity * force, deathtype, 0, attacker);
                        else
-                               Damage_DamageInfo(blastorigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker);
+                               Damage_DamageInfo(inflictororigin, coredamage, edgedamage, -rad, (-forceintensity) * force, deathtype, 0, attacker);
                }
 
        stat_damagedone = 0;
 
-       targ = WarpZone_FindRadius (blastorigin, rad + MAX_DAMAGEEXTRARADIUS, FALSE);
+       targ = WarpZone_FindRadius (inflictororigin, rad + MAX_DAMAGEEXTRARADIUS, FALSE);
        while (targ)
        {
                next = targ.chain;
-               if (targ != inflictor)
-                       if (ignore != targ) if(targ.takedamage)
+               if ((targ != inflictor) || inflictorselfdamage)
+               if (((cantbe != targ) && !mustbe) || (mustbe == targ))
+               if (targ.takedamage)
+               {
+                       vector nearest;
+                       vector diff;
+                       float power;
+
+                       // LordHavoc: measure distance to nearest point on target (not origin)
+                       // (this guarentees 100% damage on a touch impact)
+                       nearest = targ.WarpZone_findradius_nearest;
+                       diff = targ.WarpZone_findradius_dist;
+                       // round up a little on the damage to ensure full damage on impacts
+                       // and turn the distance into a fraction of the radius
+                       power = 1 - ((vlen (diff) - bound(MIN_DAMAGEEXTRARADIUS, targ.damageextraradius, MAX_DAMAGEEXTRARADIUS)) / rad);
+                       //bprint(" ");
+                       //bprint(ftos(power));
+                       //if (targ == attacker)
+                       //      print(ftos(power), "\n");
+                       if (power > 0)
                        {
-                               vector nearest;
-                               vector diff;
-                               float power;
-
-                               // LordHavoc: measure distance to nearest point on target (not origin)
-                               // (this guarentees 100% damage on a touch impact)
-                               nearest = targ.WarpZone_findradius_nearest;
-                               diff = targ.WarpZone_findradius_dist;
-                               // round up a little on the damage to ensure full damage on impacts
-                               // and turn the distance into a fraction of the radius
-                               power = 1 - ((vlen (diff) - bound(MIN_DAMAGEEXTRARADIUS, targ.damageextraradius, MAX_DAMAGEEXTRARADIUS)) / rad);
-                               //bprint(" ");
-                               //bprint(ftos(power));
-                               //if (targ == attacker)
-                               //      print(ftos(power), "\n");
-                               if (power > 0)
+                               float finaldmg;
+                               if (power > 1)
+                                       power = 1;
+                               finaldmg = coredamage * power + edgedamage * (1 - power);
+                               if (finaldmg > 0)
                                {
-                                       float finaldmg;
-                                       if (power > 1)
-                                               power = 1;
-                                       finaldmg = coredamage * power + edgedamage * (1 - power);
-                                       if (finaldmg > 0)
-                                       {
-                                               float a;
-                                               float c;
-                                               vector hitloc;
-                                               vector myblastorigin;
-                                               vector center;
-
-                                               myblastorigin = WarpZone_TransformOrigin(targ, blastorigin);
-
-                                               // if it's a player, use the view origin as reference
-                                               center = CENTER_OR_VIEWOFS(targ);
+                                       float a;
+                                       float c;
+                                       vector hitloc;
+                                       vector myblastorigin;
+                                       vector center;
 
-                                               force = normalize(center - myblastorigin);
-                                               force = force * (finaldmg / coredamage) * forceintensity;
-                                               hitloc = nearest;
+                                       myblastorigin = WarpZone_TransformOrigin(targ, inflictororigin);
 
-                                               if(targ != directhitentity)
-                                               {
-                                                       float hits;
-                                                       float total;
-                                                       float hitratio;
-                                                       float mininv_f, mininv_d;
+                                       // if it's a player, use the view origin as reference
+                                       center = CENTER_OR_VIEWOFS(targ);
 
-                                                       // test line of sight to multiple positions on box,
-                                                       // and do damage if any of them hit
-                                                       hits = 0;
+                                       force = normalize(center - myblastorigin);
+                                       force = force * (finaldmg / coredamage) * forceintensity;
+                                       hitloc = nearest;
 
-                                                       // we know: max stddev of hitratio = 1 / (2 * sqrt(n))
-                                                       // so for a given max stddev:
-                                                       // n = (1 / (2 * max stddev of hitratio))^2
+                                       if(targ != directhitentity)
+                                       {
+                                               float hits;
+                                               float total;
+                                               float hitratio;
+                                               float mininv_f, mininv_d;
 
-                                                       mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev;
-                                                       mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev;
+                                               // test line of sight to multiple positions on box,
+                                               // and do damage if any of them hit
+                                               hits = 0;
 
-                                                       if(autocvar_g_throughfloor_debug)
-                                                               print(sprintf("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f));
+                                               // we know: max stddev of hitratio = 1 / (2 * sqrt(n))
+                                               // so for a given max stddev:
+                                               // n = (1 / (2 * max stddev of hitratio))^2
 
-                                                       total = 0.25 * pow(max(mininv_f, mininv_d), 2);
+                                               mininv_d = (finaldmg * (1-tfloordmg)) / autocvar_g_throughfloor_damage_max_stddev;
+                                               mininv_f = (vlen(force) * (1-tfloorforce)) / autocvar_g_throughfloor_force_max_stddev;
 
-                                                       if(autocvar_g_throughfloor_debug)
-                                                               print(sprintf(" steps=%f", total));
+                                               if(autocvar_g_throughfloor_debug)
+                                                       print(sprintf("THROUGHFLOOR: D=%f F=%f max(dD)=1/%f max(dF)=1/%f", finaldmg, vlen(force), mininv_d, mininv_f));
 
-                                                       if (IS_PLAYER(targ))
-                                                               total = ceil(bound(autocvar_g_throughfloor_min_steps_player, total, autocvar_g_throughfloor_max_steps_player));
-                                                       else
-                                                               total = ceil(bound(autocvar_g_throughfloor_min_steps_other, total, autocvar_g_throughfloor_max_steps_other));
+                                               total = 0.25 * pow(max(mininv_f, mininv_d), 2);
 
-                                                       if(autocvar_g_throughfloor_debug)
-                                                               print(sprintf(" steps=%f dD=%f dF=%f", total, finaldmg * (1-tfloordmg) / (2 * sqrt(total)), vlen(force) * (1-tfloorforce) / (2 * sqrt(total))));
+                                               if(autocvar_g_throughfloor_debug)
+                                                       print(sprintf(" steps=%f", total));
 
-                                                       for(c = 0; c < total; ++c)
-                                                       {
-                                                               //traceline(targ.WarpZone_findradius_findorigin, nearest, MOVE_NOMONSTERS, inflictor);
-                                                               WarpZone_TraceLine(blastorigin, WarpZone_UnTransformOrigin(targ, nearest), MOVE_NOMONSTERS, inflictor);
-                                                               if (trace_fraction == 1 || trace_ent == targ)
-                                                               {
-                                                                       ++hits;
-                                                                       if (hits > 1)
-                                                                               hitloc = hitloc + nearest;
-                                                                       else
-                                                                               hitloc = nearest;
-                                                               }
-                                                               nearest_x = targ.origin_x + targ.mins_x + random() * targ.size_x;
-                                                               nearest_y = targ.origin_y + targ.mins_y + random() * targ.size_y;
-                                                               nearest_z = targ.origin_z + targ.mins_z + random() * targ.size_z;
-                                                       }
-
-                                                       nearest = hitloc * (1 / max(1, hits));
-                                                       hitratio = (hits / total);
-                                                       a = bound(0, tfloordmg + (1-tfloordmg) * hitratio, 1);
-                                                       finaldmg = finaldmg * a;
-                                                       a = bound(0, tfloorforce + (1-tfloorforce) * hitratio, 1);
-                                                       force = force * a;
+                                               if (IS_PLAYER(targ))
+                                                       total = ceil(bound(autocvar_g_throughfloor_min_steps_player, total, autocvar_g_throughfloor_max_steps_player));
+                                               else
+                                                       total = ceil(bound(autocvar_g_throughfloor_min_steps_other, total, autocvar_g_throughfloor_max_steps_other));
 
-                                                       if(autocvar_g_throughfloor_debug)
-                                                               print(sprintf(" D=%f F=%f\n", finaldmg, vlen(force)));
-                                               }
+                                               if(autocvar_g_throughfloor_debug)
+                                                       print(sprintf(" steps=%f dD=%f dF=%f", total, finaldmg * (1-tfloordmg) / (2 * sqrt(total)), vlen(force) * (1-tfloorforce) / (2 * sqrt(total))));
 
-                                               // laser force adjustments :P
-                                               if(DEATH_WEAPONOF(deathtype) == WEP_LASER)
+                                               for(c = 0; c < total; ++c)
                                                {
-                                                       if (targ == attacker)
-                                                       {
-                                                               vector vel;
-
-                                                               float force_zscale;
-                                                               float force_velocitybiasramp;
-                                                               float force_velocitybias;
-
-                                                               force_velocitybiasramp = autocvar_sv_maxspeed;
-                                                               if(deathtype & HITTYPE_SECONDARY)
-                                                               {
-                                                                       force_zscale = autocvar_g_balance_laser_secondary_force_zscale;
-                                                                       force_velocitybias = autocvar_g_balance_laser_secondary_force_velocitybias;
-                                                               }
-                                                               else
-                                                               {
-                                                                       force_zscale = autocvar_g_balance_laser_primary_force_zscale;
-                                                                       force_velocitybias = autocvar_g_balance_laser_primary_force_velocitybias;
-                                                               }
-
-                                                               vel = targ.velocity;
-                                                               vel_z = 0;
-                                                               vel = normalize(vel) * bound(0, vlen(vel) / force_velocitybiasramp, 1) * force_velocitybias;
-                                                               force =
-                                                                       vlen(force)
-                                                                       *
-                                                                       normalize(normalize(force) + vel);
-
-                                                               force_z *= force_zscale;
-                                                       }
-                                                       else
+                                                       //traceline(targ.WarpZone_findradius_findorigin, nearest, MOVE_NOMONSTERS, inflictor);
+                                                       WarpZone_TraceLine(inflictororigin, WarpZone_UnTransformOrigin(targ, nearest), MOVE_NOMONSTERS, inflictor);
+                                                       if (trace_fraction == 1 || trace_ent == targ)
                                                        {
-                                                               if(deathtype & HITTYPE_SECONDARY)
-                                                               {
-                                                                       force *= autocvar_g_balance_laser_secondary_force_other_scale;
-                                                               }
+                                                               ++hits;
+                                                               if (hits > 1)
+                                                                       hitloc = hitloc + nearest;
                                                                else
-                                                               {
-                                                                       force *= autocvar_g_balance_laser_primary_force_other_scale;
-                                                               }
+                                                                       hitloc = nearest;
                                                        }
+                                                       nearest_x = targ.origin_x + targ.mins_x + random() * targ.size_x;
+                                                       nearest_y = targ.origin_y + targ.mins_y + random() * targ.size_y;
+                                                       nearest_z = targ.origin_z + targ.mins_z + random() * targ.size_z;
                                                }
 
-                                               //if (targ == attacker)
-                                               //{
-                                               //      print("hits ", ftos(hits), " / ", ftos(total));
-                                               //      print(" finaldmg ", ftos(finaldmg), " force ", vtos(force));
-                                               //      print(" (", ftos(a), ")\n");
-                                               //}
-                                               if(finaldmg || vlen(force))
-                                               {
-                                                       if(targ.iscreature)
-                                                       {
-                                                               total_damage_to_creatures += finaldmg;
+                                               nearest = hitloc * (1 / max(1, hits));
+                                               hitratio = (hits / total);
+                                               a = bound(0, tfloordmg + (1-tfloordmg) * hitratio, 1);
+                                               finaldmg = finaldmg * a;
+                                               a = bound(0, tfloorforce + (1-tfloorforce) * hitratio, 1);
+                                               force = force * a;
 
-                                                               if(accuracy_isgooddamage(attacker, targ))
-                                                                       stat_damagedone += finaldmg;
-                                                       }
+                                               if(autocvar_g_throughfloor_debug)
+                                                       print(sprintf(" D=%f F=%f\n", finaldmg, vlen(force)));
+                                       }
 
-                                                       if(targ == directhitentity || DEATH_ISSPECIAL(deathtype))
-                                                               Damage (targ, inflictor, attacker, finaldmg, deathtype, nearest, force);
-                                                       else
-                                                               Damage (targ, inflictor, attacker, finaldmg, deathtype | HITTYPE_SPLASH, nearest, force);
+                                       //if (targ == attacker)
+                                       //{
+                                       //      print("hits ", ftos(hits), " / ", ftos(total));
+                                       //      print(" finaldmg ", ftos(finaldmg), " force ", vtos(force));
+                                       //      print(" (", ftos(a), ")\n");
+                                       //}
+                                       if(finaldmg || vlen(force))
+                                       {
+                                               if(targ.iscreature)
+                                               {
+                                                       total_damage_to_creatures += finaldmg;
+
+                                                       if(accuracy_isgooddamage(attacker, targ))
+                                                               stat_damagedone += finaldmg;
                                                }
+
+                                               if(targ == directhitentity || DEATH_ISSPECIAL(deathtype))
+                                                       Damage (targ, inflictor, attacker, finaldmg, deathtype, nearest, force);
+                                               else
+                                                       Damage (targ, inflictor, attacker, finaldmg, deathtype | HITTYPE_SPLASH, nearest, force);
                                        }
                                }
                        }
+               }
                targ = next;
        }
 
@@ -1048,6 +1001,11 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e
        return total_damage_to_creatures;
 }
 
+float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, float deathtype, entity directhitentity)
+{
+       return RadiusDamageForSource (inflictor, (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5), inflictor.velocity, attacker, coredamage, edgedamage, rad, cantbe, mustbe, FALSE, forceintensity, deathtype, directhitentity);
+}
+
 .float fire_damagepersec;
 .float fire_endtime;
 .float fire_deathtype;
index 0df6ba0aee59c4cc5a070ce9f20bdc6da263d50c..c2768cf6ae777a95b235bdef1d2f0255ad926b4d 100644 (file)
@@ -475,7 +475,7 @@ void GrappleHookInit()
        }
        else
        {
-               weapon_action(WEP_HOOK, WR_PRECACHE);
+               WEP_ACTION(WEP_HOOK, WR_INIT);
                hook_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK), FALSE, FALSE, 1);
                hook_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK), FALSE, FALSE, 2);
                hook_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_HOOK), FALSE, FALSE, 3);
index 42c2d7a8e4e74cad37e0a1cacc88f0b74361fd6e..d2a2273891fb8fdc01d666f066ea9dfe22152ff5 100644 (file)
@@ -650,8 +650,8 @@ void spawnfunc_worldspawn (void)
        InitGameplayMode();
        readlevelcvars();
        GrappleHookInit();
+       ArcInit();
        ElectroInit();
-       LaserInit();
 
        player_count = 0;
        bot_waypoints_for_items = autocvar_g_waypoints_for_items;
index e7e771dc6ed03a26ff89f050aa1499ff2fc835ad..33d43cbc23876c9990834e1391c2c72ec0381033 100644 (file)
@@ -458,7 +458,6 @@ void GetCvars_handleFloatOnce(string thisname, float f, .float field, string nam
                        stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
        }
 }
-float w_getbestweapon(entity e);
 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
 {
        string o;
@@ -643,7 +642,7 @@ float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
                        d = FALSE;
        }
        else if (g_cts)
-               d = (i == WEP_SHOTGUN);
+               d = (i == WEP_SHOTGUN); // todo: how to handle shotgun in CTS mode? we're removing it.. so.... 
        else if (g_nexball)
                d = 0; // weapon is set a few lines later
        else
@@ -866,7 +865,7 @@ void readplayerstartcvars()
        {
                e = get_weaponinfo(i);
                if(precache_weapons & WepSet_FromWeapon(i))
-                       weapon_action(i, WR_PRECACHE);
+                       WEP_ACTION(i, WR_INIT);
        }
 
        start_ammo_shells = max(0, start_ammo_shells);
@@ -1389,18 +1388,6 @@ void precache()
         precache_sound ("weapons/hook_impact.wav"); // hook
     }
 
-    if(autocvar_sv_precacheweapons)
-    {
-        //precache weapon models/sounds
-        float wep;
-        wep = WEP_FIRST;
-        while (wep <= WEP_LAST)
-        {
-            weapon_action(wep, WR_PRECACHE);
-            wep = wep + 1;
-        }
-    }
-
     precache_model("models/elaser.mdl");
     precache_model("models/laser.mdl");
     precache_model("models/ebomb.mdl");
index 2d2c857e90dbff166c673e53f05488bb6970e878..fc9e45b96e8f011edd8d997f4335ff9b011e212c 100644 (file)
@@ -142,7 +142,7 @@ void GiveBall(entity plyr, entity ball)
        self.weaponentity.weapons = self.weapons;
        self.weaponentity.switchweapon = self.weapon;
        self.weapons = WEPSET_PORTO;
-       weapon_action(WEP_PORTO, WR_RESETPLAYER);
+       WEP_ACTION(WEP_PORTO, WR_RESETPLAYER);
        self.switchweapon = WEP_PORTO;
        W_SwitchWeapon(WEP_PORTO);
        self = ownr;
@@ -833,7 +833,7 @@ float w_nexball_weapon(float req)
                        weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready);
                }
        }
-       else if(req == WR_PRECACHE)
+       else if(req == WR_INIT)
        {
                precache_model("models/weapons/g_porto.md3");
                precache_model("models/weapons/v_porto.md3");
@@ -845,7 +845,7 @@ float w_nexball_weapon(float req)
        }
        else if(req == WR_SETUP)
        {
-               weapon_setup(WEP_PORTO);
+               //weapon_setup(WEP_PORTO);
        }
        // No need to check WR_CHECKAMMO* or WR_AIM, it should always return TRUE
        return TRUE;
@@ -920,7 +920,7 @@ MUTATOR_HOOKFUNCTION(nexball_PlayerPreThink)
                        if(self.weaponentity.weapons)
                        {
                                self.weapons = self.weaponentity.weapons;
-                               weapon_action(WEP_PORTO, WR_RESETPLAYER);
+                               WEP_ACTION(WEP_PORTO, WR_RESETPLAYER);
                                self.switchweapon = self.weaponentity.switchweapon;
                                W_SwitchWeapon(self.switchweapon);
 
index ac6e158ed4c824c2b8f41fb9d5422211c413ea14..9c27e4df05e9556a8a158e83af1b60373e79c51a 100644 (file)
@@ -293,7 +293,7 @@ MUTATOR_HOOKFUNCTION(minstagib_FilterItem)
                return FALSE;
        }
 
-       if(self.weapon == WEP_ROCKET_LAUNCHER || self.weapon == WEP_NEX)
+       if(self.weapon == WEP_DEVASTATOR || self.weapon == WEP_NEX)
        {
                entity e = spawn();
                setorigin(e, self.origin);
index 5ab6df75dc720e2d6b385ed665d1cf0ccf9fcfc3..2e6f7ea3046e089575c1167c300bc519a5f934f7 100644 (file)
@@ -72,7 +72,7 @@ void nade_boom()
 
        self.takedamage = DAMAGE_NO;
        RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
-                                autocvar_g_nades_nade_radius, self, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy);
+                                autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy);
 
        remove(self);
 }
index dc12b05209eccc70baa32187ab62b033eb8fb961..70357067e466c6ee429e133f06347c0917a8395a 100644 (file)
@@ -1,5 +1,5 @@
 float g_nix_with_laser;
-
+// WEAPONTODO
 float nix_weapon;
 float nix_weapon_ammo;
 float nix_nextchange;
@@ -60,7 +60,7 @@ void NIX_GiveCurrentWeapon()
                        nix_nextchange = time; // start the first round now!
                else
                        nix_nextchange = time + autocvar_g_balance_nix_roundtime;
-               //weapon_action(nix_weapon, WR_PRECACHE); // forget it, too slow
+               //WEP_ACTION(nix_weapon, WR_INIT); // forget it, too slow
        }
 
        if(nix_nextchange != self.nix_lastchange_id) // this shall only be called once per round!
@@ -98,20 +98,20 @@ void NIX_GiveCurrentWeapon()
                else
                        Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon);
 
-               weapon_action(nix_weapon, WR_RESETPLAYER);
+               WEP_ACTION(nix_weapon, WR_RESETPLAYER);
 
                // all weapons must be fully loaded when we spawn
                entity e;
                e = get_weaponinfo(nix_weapon);
                if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars
-                       self.(weapon_load[nix_weapon]) = cvar(strcat("g_balance_", e.netname, "_reload_ammo"));
+                       self.(weapon_load[nix_weapon]) = e.reloading_ammo;
 
                // nex too
-               if(autocvar_g_balance_nex_charge)
+               if(WEP_CVAR(nex, charge))
                {
-                       if(autocvar_g_balance_nex_secondary_chargepool)
+                       if(WEP_CVAR_SEC(nex, chargepool))
                                self.nex_chargepool_ammo = 1;
-                       self.nex_charge = autocvar_g_balance_nex_charge_start;
+                       self.nex_charge = WEP_CVAR(nex, charge_start);
                }
        }
        if(self.nix_lastinfotime != dt)
@@ -152,7 +152,7 @@ void NIX_precache()
        float i;
        for (i = WEP_FIRST; i <= WEP_LAST; ++i)
                if (NIX_CanChooseWeapon(i))
-                       weapon_action(i, WR_PRECACHE);
+                       WEP_ACTION(i, WR_INIT);
 }
 
 MUTATOR_HOOKFUNCTION(nix_ForbidThrowCurrentWeapon)
index fabf13639cf89d6ccf4ba810a099d303a74d0ff7..9e3023b7215496e3bce03026982b74b8f58d1df5 100644 (file)
@@ -12,7 +12,7 @@ void PlayerTouchExplode(entity p1, entity p2)
        entity e;
        e = spawn();
        setorigin(e, org);
-       RadiusDamage(e, world, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, world, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE, world);
+       RadiusDamage(e, world, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, world, world, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE, world);
        remove(e);
 }
 
index f29324a478a8e622e4b72b81d995681a01d511b4..1a505f5fed26a64768b022e58542af24d298ece1 100644 (file)
@@ -11,14 +11,11 @@ sys-post.qh
 ../warpzonelib/common.qh
 ../warpzonelib/util_server.qh
 ../warpzonelib/server.qh
-
 ../common/constants.qh
 ../common/teams.qh
 ../common/util.qh
 ../common/test.qh
 ../common/counting.qh
-../common/items.qh
-../common/explosion_equation.qh
 ../common/urllib.qh
 ../common/command/markup.qh
 ../common/command/rpn.qh
@@ -27,6 +24,20 @@ sys-post.qh
 ../common/net_notice.qh
 ../common/animdecide.qh
 
+../common/weapons/config.qh
+../common/weapons/weapons.qh // TODO
+weapons/accuracy.qh
+weapons/common.qh
+weapons/csqcprojectile.qh // TODO
+weapons/hitplot.qh
+weapons/selection.qh
+weapons/spawning.qh
+weapons/throwing.qh
+weapons/tracing.qh
+weapons/weaponsystem.qh
+
+t_items.qh
+
 autocvars.qh
 constants.qh
 defs.qh                // Should rename this, it has fields and globals
@@ -63,8 +74,7 @@ command/getreplies.qh
 command/cmd.qh
 command/sv_cmd.qh
 
-accuracy.qh
-csqcprojectile.qh
+
 ../common/csqcmodel_settings.qh
 ../csqcmodellib/common.qh
 ../csqcmodellib/sv_model.qh
@@ -76,9 +86,7 @@ playerstats.qh
 
 portals.qh
 
-g_hook.qh
-w_electro.qh
-w_laser.qh
+g_hook.qh // TODO
 
 scores.qh
 
@@ -139,13 +147,19 @@ g_models.qc
 item_key.qc
 secret.qc
 
-cl_weaponsystem.qc
-w_common.qc
-
-w_all.qc
+weapons/accuracy.qc
+weapons/common.qc
+weapons/csqcprojectile.qc // TODO
+weapons/hitplot.qc
+weapons/selection.qc
+weapons/spawning.qc
+weapons/throwing.qc
+weapons/tracing.qc
+weapons/weaponsystem.qc
+../common/weapons/config.qc
+../common/weapons/weapons.qc // TODO
 
 t_items.qc
-cl_weapons.qc
 cl_impulse.qc
 
 ent_cs.qc
@@ -208,12 +222,7 @@ target_spawn.qc
 func_breakable.qc
 target_music.qc
 
-../common/items.qc
-
-
-accuracy.qc
 ../csqcmodellib/sv_model.qc
-csqcprojectile.qc
 
 playerdemo.qc
 
@@ -223,8 +232,6 @@ playerstats.qc
 
 round_handler.qc
 
-../common/explosion_equation.qc
-
 mutators/base.qc
 mutators/gamemode_assault.qc
 mutators/gamemode_ca.qc
index 1db8f6cd7630fcf7913d3fd229696986f8aa5e7d..493ca337dc4822c6bb9d5a1a2b0519db2a359d96 100644 (file)
@@ -1,32 +1,4 @@
-#define ISF_LOCATION 2
-#define ISF_MODEL    4
-#define ISF_STATUS   8
-    #define ITS_STAYWEP   1
-    #define ITS_ANIMATE1  2
-    #define ITS_ANIMATE2  4
-    #define ITS_AVAILABLE 8
-    #define ITS_ALLOWFB   16
-    #define ITS_ALLOWSI   32
-    #define ITS_POWERUP   64
-#define ISF_COLORMAP 16
-#define ISF_DROP 32
-#define ISF_ANGLES 64
-
-.float ItemStatus;
-
 #ifdef CSQC
-
-var float  autocvar_cl_animate_items = 1;
-var float  autocvar_cl_ghost_items = 0.45;
-var vector autocvar_cl_ghost_items_color = '-1 -1 -1';
-var float  autocvar_cl_fullbright_items = 0;
-var vector autocvar_cl_weapon_stay_color = '2 0.5 0.5';
-var float  autocvar_cl_weapon_stay_alpha = 0.75;
-var float  autocvar_cl_simple_items = 0;
-var string autocvr_cl_simpleitems_postfix = "_simple";
-.float  spawntime;
-.float  gravity;
-.vector colormod;
 void ItemDraw()
 {
     if(self.gravity)
@@ -216,7 +188,6 @@ void ItemRead(float _IsNew)
 #endif
 
 #ifdef SVQC
-float autocvar_sv_simple_items;
 float ItemSend(entity to, float sf)
 {
     if(self.gravity)
@@ -295,13 +266,6 @@ float have_pickup_item(void)
        return TRUE;
 }
 
-#define ITEM_RESPAWN_TICKS 10
-
-#define ITEM_RESPAWNTIME(i)         ((i).respawntime + crandom() * (i).respawntimejitter)
-       // range: respawntime - respawntimejitter .. respawntime + respawntimejitter
-#define ITEM_RESPAWNTIME_INITIAL(i) (ITEM_RESPAWN_TICKS + random() * ((i).respawntime + (i).respawntimejitter - ITEM_RESPAWN_TICKS))
-       // range: 10 .. respawntime + respawntimejitter
-
 floatfield Item_CounterField(float it)
 {
        switch(it)
@@ -344,8 +308,6 @@ string Item_CounterFieldName(float it)
 #endif
 }
 
-.float max_armorvalue;
-.float pickup_anyway;
 /*
 float Item_Customize()
 {
@@ -537,10 +499,6 @@ void Item_ScheduleInitialRespawn(entity e)
        Item_ScheduleRespawnIn(e, game_starttime - time + ITEM_RESPAWNTIME_INITIAL(e));
 }
 
-const float ITEM_MODE_NONE = 0;
-const float ITEM_MODE_HEALTH = 1;
-const float ITEM_MODE_ARMOR = 2;
-const float ITEM_MODE_FUEL = 3;
 float Item_GiveAmmoTo(entity item, entity player, .float ammofield, float ammomax, float mode)
 {
        if (!item.ammofield)
@@ -923,7 +881,6 @@ void Item_Damage(entity inflictor, entity attacker, float damage, float deathtyp
                RemoveItem();
 }
 
-.float is_item;
 void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, float defaultrespawntimejitter, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue)
 {
        startitem_failed = FALSE;
@@ -1143,183 +1100,6 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime,
                return;
        }
 }
-
-float weaponswapping;
-float internalteam;
-
-void weapon_defaultspawnfunc(float wpn)
-{
-       entity e;
-       float t;
-       var .float ammofield;
-       string s;
-       entity oldself;
-       float i, j;
-       float f;
-
-       if(self.classname != "droppedweapon" && self.classname != "replacedweapon")
-       {
-               e = get_weaponinfo(wpn);
-
-               if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
-               {
-                       objerror("Attempted to spawn a mutator-blocked weapon rejected");
-                       startitem_failed = TRUE;
-                       return;
-               }
-
-               s = W_Apply_Weaponreplace(e.netname);
-               ret_string = s;
-               other = e;
-               MUTATOR_CALLHOOK(SetWeaponreplace);
-               s = ret_string;
-               if(s == "")
-               {
-                       remove(self);
-                       startitem_failed = TRUE;
-                       return;
-               }
-               t = tokenize_console(s);
-               if(t >= 2)
-               {
-                       self.team = --internalteam;
-                       oldself = self;
-                       for(i = 1; i < t; ++i)
-                       {
-                               s = argv(i);
-                               for(j = WEP_FIRST; j <= WEP_LAST; ++j)
-                               {
-                                       e = get_weaponinfo(j);
-                                       if(e.netname == s)
-                                       {
-                                               self = spawn();
-                                               copyentity(oldself, self);
-                                               self.classname = "replacedweapon";
-                                               weapon_defaultspawnfunc(j);
-                                               break;
-                                       }
-                               }
-                               if(j > WEP_LAST)
-                               {
-                                       print("The weapon replace list for ", oldself.classname, " contains an unknown weapon ", s, ". Skipped.\n");
-                               }
-                       }
-                       self = oldself;
-               }
-               if(t >= 1) // always the case!
-               {
-                       s = argv(0);
-                       wpn = 0;
-                       for(j = WEP_FIRST; j <= WEP_LAST; ++j)
-                       {
-                               e = get_weaponinfo(j);
-                               if(e.netname == s)
-                               {
-                                       wpn = j;
-                                       break;
-                               }
-                       }
-                       if(j > WEP_LAST)
-                       {
-                               print("The weapon replace list for ", self.classname, " contains an unknown weapon ", s, ". Skipped.\n");
-                       }
-               }
-               if(wpn == 0)
-               {
-                       remove(self);
-                       startitem_failed = TRUE;
-                       return;
-               }
-       }
-
-       e = get_weaponinfo(wpn);
-
-       if(!self.respawntime)
-       {
-               if(e.weapons & WEPSET_SUPERWEAPONS)
-               {
-                       self.respawntime = g_pickup_respawntime_superweapon;
-                       self.respawntimejitter = g_pickup_respawntimejitter_superweapon;
-               }
-               else
-               {
-                       self.respawntime = g_pickup_respawntime_weapon;
-                       self.respawntimejitter = g_pickup_respawntimejitter_weapon;
-               }
-       }
-
-       if(e.weapons & WEPSET_SUPERWEAPONS)
-               if(!self.superweapons_finished)
-                       self.superweapons_finished = autocvar_g_balance_superweapons_time;
-
-       if(e.items)
-       {
-               for(i = 0, j = 1; i < 24; ++i, j *= 2)
-               {
-                       if(e.items & j)
-                       {
-                               ammofield = Item_CounterField(j);
-                               if(!self.ammofield)
-                                       self.ammofield = cvar(strcat("g_pickup_", Item_CounterFieldName(j), "_weapon"));
-                       }
-               }
-       }
-
-       // pickup anyway
-       if(g_pickup_weapons_anyway)
-               self.pickup_anyway = TRUE;
-
-       f = FL_WEAPON;
-
-       // no weapon-stay on superweapons
-       if(e.weapons & WEPSET_SUPERWEAPONS)
-               f |= FL_NO_WEAPON_STAY;
-
-       // weapon stay isn't supported for teamed weapons
-       if(self.team)
-               f |= FL_NO_WEAPON_STAY;
-
-       StartItem(e.model, "weapons/weaponpickup.wav", self.respawntime, self.respawntimejitter, e.message, 0, e.weapon, f, weapon_pickupevalfunc, e.bot_pickupbasevalue);
-       if (self.modelindex) // don't precache if self was removed
-               weapon_action(e.weapon, WR_PRECACHE);
-}
-
-void spawnfunc_weapon_shotgun (void);
-void spawnfunc_weapon_uzi (void) {
-       if(autocvar_sv_q3acompat_machineshotgunswap)
-       if(self.classname != "droppedweapon")
-       {
-               weapon_defaultspawnfunc(WEP_SHOTGUN);
-               return;
-       }
-       weapon_defaultspawnfunc(WEP_UZI);
-}
-
-void spawnfunc_weapon_shotgun (void) {
-       if(autocvar_sv_q3acompat_machineshotgunswap)
-       if(self.classname != "droppedweapon")
-       {
-               weapon_defaultspawnfunc(WEP_UZI);
-               return;
-       }
-       weapon_defaultspawnfunc(WEP_SHOTGUN);
-}
-
-void spawnfunc_weapon_nex (void)
-{
-       weapon_defaultspawnfunc(WEP_NEX);
-}
-
-void spawnfunc_weapon_minstanex (void)
-{
-       weapon_defaultspawnfunc(WEP_MINSTANEX);
-}
-
-void spawnfunc_weapon_rocketlauncher (void)
-{
-       weapon_defaultspawnfunc(WEP_ROCKET_LAUNCHER);
-}
-
 void spawnfunc_item_rockets (void) {
        if(!self.ammo_rockets)
                self.ammo_rockets = g_pickup_rockets;
@@ -1476,7 +1256,6 @@ void spawnfunc_item_invincible (void) {
 // compatibility:
 void spawnfunc_item_quad (void) {self.classname = "item_strength";spawnfunc_item_strength();}
 
-float GiveItems(entity e, float beginarg, float endarg);
 void target_items_use (void)
 {
        if(activator.classname == "droppedweapon")
@@ -1547,7 +1326,7 @@ void spawnfunc_target_items (void)
                                        {
                                                self.weapons |= WepSet_FromWeapon(j);
                                                if(self.spawnflags == 0 || self.spawnflags == 2)
-                                                       weapon_action(e.weapon, WR_PRECACHE);
+                                                       WEP_ACTION(e.weapon, WR_INIT);
                                                break;
                                        }
                                }
@@ -1619,7 +1398,7 @@ void spawnfunc_target_items (void)
                        e = get_weaponinfo(j);
                        if(argv(i) == e.netname)
                        {
-                               weapon_action(e.weapon, WR_PRECACHE);
+                               WEP_ACTION(e.weapon, WR_INIT);
                                break;
                        }
                }
@@ -1659,13 +1438,6 @@ void spawnfunc_item_jetpack(void)
        StartItem ("models/items/g_jetpack.md3", "misc/itempickup.wav", g_pickup_respawntime_powerup, g_pickup_respawntimejitter_powerup, "Jet pack", IT_JETPACK, 0, FL_POWERUP, commodity_pickupevalfunc, BOT_PICKUP_RATING_LOW);
 }
 
-
-#define OP_SET 0
-#define OP_MIN 1
-#define OP_MAX 2
-#define OP_PLUS 3
-#define OP_MINUS 4
-
 float GiveWeapon(entity e, float wpn, float op, float val)
 {
        WepSet v0, v1;
@@ -1775,14 +1547,6 @@ void GiveRot(entity e, float v0, float v1, .float rotfield, float rottime, .floa
        else if(v0 > v1)
                e.regenfield = max(e.regenfield, time + regentime);
 }
-
-#define PREGIVE_WEAPONS(e) WepSet save_weapons; save_weapons = e.weapons
-#define PREGIVE(e,f) float save_##f; save_##f = (e).f
-#define POSTGIVE_WEAPON(e,b,snd_incr,snd_decr) GiveSound((e), !!(save_weapons & WepSet_FromWeapon(b)), !!(e.weapons & WepSet_FromWeapon(b)), 0, snd_incr, snd_decr)
-#define POSTGIVE_BIT(e,f,b,snd_incr,snd_decr) GiveSound((e), save_##f & (b), (e).f & (b), 0, snd_incr, snd_decr)
-#define POSTGIVE_VALUE(e,f,t,snd_incr,snd_decr) GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr)
-#define POSTGIVE_VALUE_ROT(e,f,t,rotfield,rottime,regenfield,regentime,snd_incr,snd_decr) GiveRot((e), save_##f, (e).f, rotfield, rottime, regenfield, regentime); GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr)
-
 float GiveItems(entity e, float beginarg, float endarg)
 {
        float got, i, j, val, op;
@@ -1945,7 +1709,7 @@ float GiveItems(entity e, float beginarg, float endarg)
                        POSTGIVE_WEAPON(e, j, "weapons/weaponpickup.wav", string_null);
                        if (!(save_weapons & WepSet_FromWeapon(j)))
                                if(e.weapons & WepSet_FromWeapon(j))
-                                       weapon_action(wi.weapon, WR_PRECACHE);
+                                       WEP_ACTION(wi.weapon, WR_INIT);
                }
        }
        POSTGIVE_VALUE(e, strength_finished, 1, "misc/powerup.wav", "misc/poweroff.wav");
diff --git a/qcsrc/server/t_items.qh b/qcsrc/server/t_items.qh
new file mode 100644 (file)
index 0000000..a64c839
--- /dev/null
@@ -0,0 +1,125 @@
+#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 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 bb1128bd60e8c532c217de9d8f9d99f9c0404d27..d55c8cbd636878105d0a4cfffc4fe4d40d8f9049 100644 (file)
@@ -14,8 +14,8 @@ void spawnfunc_ammo_bullets()        { spawnfunc_item_bullets();        }
 // GL -> Mortar
 void spawnfunc_ammo_grenades()       { spawnfunc_item_rockets();        }
 
-// LG -> Electro
-void spawnfunc_weapon_lightning()    { spawnfunc_weapon_electro();      }
+// LG -> Lightning
+//void spawnfunc_weapon_lightning()    { spawnfunc_weapon_electro();      }
 void spawnfunc_ammo_lightning()      { spawnfunc_item_cells();          }
 
 // Plasma -> Hagar
@@ -72,11 +72,11 @@ void target_give_init()
        entity targ;
        for (targ = world; (targ = find(targ, targetname, self.target)); ) {
                if (targ.classname == "weapon_rocketlauncher") {
-                       self.ammo_rockets += targ.count * autocvar_g_balance_rocketlauncher_ammo;
+                       self.ammo_rockets += targ.count * WEP_CVAR(devastator, ammo);
                        self.netname = "rocketlauncher";
                }
                else if (targ.classname == "weapon_plasmagun") {
-                       self.ammo_rockets += targ.count * autocvar_g_balance_hagar_primary_ammo;
+                       self.ammo_rockets += targ.count * WEP_CVAR_PRI(hagar, ammo); // WEAPONTODO
                        if(self.netname == "")
                                self.netname = "hagar";
                        else
@@ -90,7 +90,7 @@ void target_give_init()
                                self.netname = strcat(self.netname, " crylink");
                }
                else if (targ.classname == "weapon_grenadelauncher") {
-                       self.ammo_rockets += targ.count * autocvar_g_balance_grenadelauncher_primary_ammo;
+                       self.ammo_rockets += targ.count * autocvar_g_balance_mortar_primary_ammo; // WEAPONTODO
                        if(self.netname == "")
                                self.netname = "grenadelauncher";
                        else
index b4ad709b4d0b547278fd74c4f20fff29a0d64b62..831ed49cb9cfef8888a6919874b8118897611458 100644 (file)
@@ -148,11 +148,11 @@ void turret_projectile_explode()
     self.event_damage = func_null;
 #ifdef TURRET_DEBUG
     float d;
-    d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, self.owner.shot_force, self.totalfrags, world);
+    d = RadiusDamage (self, self.owner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world);
     self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d;
     self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg;
 #else
-    RadiusDamage (self, self.realowner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, self.owner.shot_force, self.totalfrags, world);
+    RadiusDamage (self, self.realowner, self.owner.shot_dmg, 0, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world);
 #endif
     remove(self);
 }
index 8a01a9752a9f8f4b0cc1d1aad6d7c33a4a29986f..3c9e55863f7698d423ede49d0664115bc7fd660c 100644 (file)
@@ -10,11 +10,11 @@ void turret_flac_projectile_think_explode()
 
 #ifdef TURRET_DEBUG
     float d;
-    d = RadiusDamage (self, self.owner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, self.owner.shot_force, self.totalfrags, world);
+    d = RadiusDamage (self, self.owner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world);
     self.owner.tur_dbg_dmg_t_h = self.owner.tur_dbg_dmg_t_h + d;
     self.owner.tur_dbg_dmg_t_f = self.owner.tur_dbg_dmg_t_f + self.owner.shot_dmg;
 #else
-    RadiusDamage (self, self.realowner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, self.owner.shot_force, self.totalfrags, world);
+    RadiusDamage (self, self.realowner, self.owner.shot_dmg, self.owner.shot_dmg, self.owner.shot_radius, self, world, self.owner.shot_force, self.totalfrags, world);
 #endif
     remove(self);
 }
index 46c46d7f8f04068e7dba48f4b5f1e59320388385..04d9e186ef3b266ab601da93b7910a5e794037d4 100644 (file)
@@ -5,7 +5,7 @@ void turret_machinegun_attack();
 //.float bulletcounter;
 void turret_machinegun_attack()
 {
-    fireBallisticBullet (self.tur_shotorg, self.tur_shotdir_updated,self.shot_spread, self.shot_speed, 5, self.shot_dmg, self.shot_force, DEATH_TURRET_MACHINEGUN, 0, 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_MACHINEGUN, 0, WEP_CVAR(uzi, bulletconstant)); // WEAPONTODO
     endFireBallisticBullet();
 
     UziFlash();
index 1ce59dd278bfb4e3bc2c5d84a9584bde94b212b2..2da91aad05c207fe9481455877461f9ec868329d 100644 (file)
@@ -51,7 +51,8 @@ void walker_setnoanim()
 }
 void walker_rocket_explode()
 {
-    RadiusDamage (self, self.owner, autocvar_g_turrets_unit_walker_std_rocket_dmg, 0, autocvar_g_turrets_unit_walker_std_rocket_radius, self, autocvar_g_turrets_unit_walker_std_rocket_force, DEATH_TURRET_WALK_ROCKET, world);
+    RadiusDamage (self, self.owner, autocvar_g_turrets_unit_walker_std_rocket_dmg, 0, autocvar_g_turrets_unit_walker_std_rocket_radius, self, world, autocvar_g_turrets_unit_walker_std_rocket_force, DEATH_TURRET_WALK_ROCKET, world);
+
     remove (self);
 }
 
@@ -515,7 +516,7 @@ void walker_postthink()
 void walker_attack()
 {
     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, 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, WEP_CVAR(uzi, bulletconstant)); // WEAPONTODO
     endFireBallisticBullet();
     pointparticles(particleeffectnum("laser_muzzleflash"), self.tur_shotorg, self.tur_shotdir_updated * 1000, 1);
 }
index bc5f03d8ae52f53d01bcdf33f51f6243974b98bd..986397c8965fd8b4bc23a810a7bd70f7dbf0cbc8 100644 (file)
@@ -702,7 +702,7 @@ void bumb_blowup()
 {
        RadiusDamage(self, self.enemy, autocvar_g_vehicle_bumblebee_blowup_coredamage,
                                 autocvar_g_vehicle_bumblebee_blowup_edgedamage,
-                                autocvar_g_vehicle_bumblebee_blowup_radius, self,
+                                autocvar_g_vehicle_bumblebee_blowup_radius, self, world,
                                 autocvar_g_vehicle_bumblebee_blowup_forceintensity,
                                 DEATH_VH_BUMB_DEATH, world);
 
@@ -787,7 +787,6 @@ void bumb_die()
        self.nextthink          = 0;
 
        setorigin(self, self.pos1);
-
 }
 
 void bumb_impact()
index ff54539027eaa8cebb27d1f45caa6101a11e6046..70d660c42a13d28f0d45eafe6a0517ecdc6a69bd 100644 (file)
@@ -540,7 +540,7 @@ void racer_blowup()
 
     RadiusDamage (self, self.enemy, autocvar_g_vehicle_racer_blowup_coredamage,
                                        autocvar_g_vehicle_racer_blowup_edgedamage,
-                                       autocvar_g_vehicle_racer_blowup_radius, world,
+                                       autocvar_g_vehicle_racer_blowup_radius, world, world,
                                        autocvar_g_vehicle_racer_blowup_forceintensity,
                                        DEATH_VH_WAKI_DEATH, world);
 
index 58a640e9cf8672afe3d0861b7c572f4ad4617bd9..50dc32bc688ccf4e0306186b6105d9288c188879 100644 (file)
@@ -92,7 +92,7 @@ void raptor_bomblet_boom()
 {
     RadiusDamage (self, self.realowner, autocvar_g_vehicle_raptor_bomblet_damage,
                                     autocvar_g_vehicle_raptor_bomblet_edgedamage,
-                                    autocvar_g_vehicle_raptor_bomblet_radius, world,
+                                    autocvar_g_vehicle_raptor_bomblet_radius, world, world,
                                     autocvar_g_vehicle_raptor_bomblet_force, DEATH_VH_RAPT_BOMB, world);
     remove(self);
 }
@@ -690,7 +690,8 @@ void raptor_blowup()
 {
     self.deadflag    = DEAD_DEAD;
     self.vehicle_exit(VHEF_NORMAL);
-    RadiusDamage (self, self.enemy, 250, 15, 250, world, 250, DEATH_VH_RAPT_DEATH, world);
+
+    RadiusDamage (self, self.enemy, 250, 15, 250, world, world, 250, DEATH_VH_RAPT_DEATH, world);
 
     self.alpha          = -1;
     self.movetype       = MOVETYPE_NONE;
index bdbcb828870829577254ea87fe569ad792acd51a..e268f7f8309df477d9f0ffcb7da597a09c64af94 100644 (file)
@@ -723,7 +723,7 @@ void spiderbot_blowup()
     SUB_SetFade(g1, time, min(autocvar_g_vehicle_spiderbot_respawntime, 10));
     SUB_SetFade(g2, time, min(autocvar_g_vehicle_spiderbot_respawntime, 10));
 
-    RadiusDamage (self, self.enemy, 250, 15, 250, world, 250, DEATH_VH_SPID_DEATH, world);
+    RadiusDamage (self, self.enemy, 250, 15, 250, world, world, 250, DEATH_VH_SPID_DEATH, world);
 
     self.alpha = self.tur_head.alpha = self.gun1.alpha = self.gun2.alpha = -1;
     self.movetype   = MOVETYPE_NONE;
index 1e5537a259934cd2faab7750716d1b9d57ea22de..829c0ce89f21c247f22073ecb26e3e2ed772ed6c 100644 (file)
@@ -397,7 +397,7 @@ void vehicles_projectile_explode()
        PROJECTILE_TOUCH;
 
        self.event_damage = func_null;
-    RadiusDamage (self, self.realowner, self.shot_dmg, 0, self.shot_radius, self, self.shot_force, self.totalfrags, other);
+    RadiusDamage (self, self.realowner, self.shot_dmg, 0, self.shot_radius, self, world, self.shot_force, self.totalfrags, other);
 
     remove (self);
 }
diff --git a/qcsrc/server/w_all.qc b/qcsrc/server/w_all.qc
deleted file mode 100644 (file)
index ac4ef47..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-// ONLY EVER ADD NEW WEAPONS AT THE END. IF YOU REMOVE ONE, PUT THE LAST ONE ON
-// ITS PLACE. THIS IS TO AVOID UNNECESSARY RENUMBERING OF WEAPON IMPULSES.
-// IF YOU DISREGARD THIS NOTICE, I'LL KILL YOU WITH THE @!#%'N TUBA
-#include "w_laser.qc"
-#include "w_shotgun.qc"
-#include "w_uzi.qc"
-#include "w_grenadelauncher.qc"
-#include "w_minelayer.qc"
-#include "w_electro.qc"
-#include "w_crylink.qc"
-#include "w_nex.qc"
-#include "w_hagar.qc"
-#include "w_rocketlauncher.qc"
-#include "w_porto.qc"
-#include "w_minstanex.qc"
-#include "w_hook.qc"
-#include "w_hlac.qc"
-#include "w_tuba.qc"
-#include "w_rifle.qc"
-#include "w_fireball.qc"
-#include "w_seeker.qc"
diff --git a/qcsrc/server/w_common.qc b/qcsrc/server/w_common.qc
deleted file mode 100644 (file)
index 72cac79..0000000
+++ /dev/null
@@ -1,610 +0,0 @@
-
-void W_GiveWeapon (entity e, float wep)
-{
-       entity oldself;
-
-       if (!wep)
-               return;
-
-       e.weapons |= WepSet_FromWeapon(wep);
-
-       oldself = self;
-       self = e;
-
-       if(IS_PLAYER(other))
-               { Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_WEAPON_GOT, wep); }
-
-       self = oldself;
-}
-
-.float railgundistance;
-.vector railgunforce;
-void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, float deathtype)
-{
-       vector hitloc, force, endpoint, dir;
-       entity ent, endent;
-       float endq3surfaceflags;
-       float totaldmg;
-       entity o;
-
-       float length;
-       vector beampos;
-       string snd;
-       entity pseudoprojectile;
-       float f, ffs;
-
-       pseudoprojectile = world;
-
-       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(!(IS_SPEC(msg_entity) && msg_entity.enemy == self)) // we use realclient, so spectators can hear the whoosh too
-               {
-                       // nearest point on the beam
-                       beampos = start + dir * bound(0, (msg_entity.origin - start) * dir, length);
-
-                       f = bound(0, 1 - vlen(beampos - msg_entity.origin) / 512, 1);
-                       if(f <= 0)
-                               continue;
-
-                       snd = strcat("weapons/nexwhoosh", ftos(floor(random() * 3) + 1), ".wav");
-
-                       if(!pseudoprojectile)
-                               pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume
-                       soundtoat(MSG_ONE, pseudoprojectile, beampos, CH_SHOTS, snd, VOL_BASE * f, ATTEN_NONE);
-               }
-
-               if(pseudoprojectile)
-                       remove(pseudoprojectile);
-       }
-
-       // find all the entities the railgun hit and hurt them
-       ent = findfloat(world, railgunhit, TRUE);
-       while (ent)
-       {
-               // get the details we need to call the damage function
-               hitloc = ent.railgunhitloc;
-
-               f = ExponentialFalloff(mindist, maxdist, halflifedist, ent.railgundistance);
-               ffs = ExponentialFalloff(mindist, maxdist, forcehalflifedist, ent.railgundistance);
-
-               if(accuracy_isgooddamage(self.realowner, ent))
-                       totaldmg += bdamage * f;
-
-               // apply the damage
-               if (ent.takedamage)
-                       Damage (ent, self, self, bdamage * f, deathtype, hitloc, ent.railgunforce * ffs);
-
-               // create a small explosion to throw gibs around (if applicable)
-               //setorigin (explosion, hitloc);
-               //RadiusDamage (explosion, self, 10, 0, 50, world, 300, deathtype);
-
-               ent.railgunhitloc = '0 0 0';
-               ent.railgunhitsolidbackup = SOLID_NOT;
-               ent.railgunhit = FALSE;
-               ent.railgundistance = 0;
-
-               // advance to the next entity
-               ent = findfloat(ent, railgunhit, TRUE);
-       }
-
-       // calculate hits and fired shots for hitscan
-       accuracy_add(self, self.weapon, 0, min(bdamage, totaldmg));
-
-       trace_endpos = endpoint;
-       trace_ent = endent;
-       trace_dphitq3surfaceflags = endq3surfaceflags;
-}
-
-.float dmg_force;
-.float dmg_radius;
-.float dmg_total;
-//.float last_yoda;
-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(void) W_BallisticBullet_LeaveSolid_think_save;
-.float W_BallisticBullet_LeaveSolid_nextthink_save;
-.vector W_BallisticBullet_LeaveSolid_origin;
-.vector W_BallisticBullet_LeaveSolid_velocity;
-
-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 (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID))
-       if (!(trace_dphitcontents & DPCONTENTS_OPAQUE))
-       {
-               remove(self);
-               return;
-       }
-
-       // go through solid!
-       if(!W_BallisticBullet_LeaveSolid(-1))
-       {
-               remove(self);
-               return;
-       }
-
-       self.projectiledeathtype |= HITTYPE_BOUNCE;
-}
-
-void endFireBallisticBullet()
-{
-       endzcurveparticles();
-}
-
-entity fireBallisticBullet_trace_callback_ent;
-float fireBallisticBullet_trace_callback_eff;
-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 bulletconstant)
-{
-       float lag, dt, savetime; //, density;
-       entity pl, oldself;
-
-       entity proj;
-       proj = spawn();
-       proj.classname = "bullet";
-       proj.owner = proj.realowner = self;
-       PROJECTILE_MAKETRIGGER(proj);
-       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, TRUE);
-       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);
-
-       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 (!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;
-               v0 = self.velocity;
-               self.velocity = self.velocity * 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;
-
-               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 (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID))
-               if (!(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;
-}
-
-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 (!(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;
-}
-
-float W_CheckProjectileDamage(entity inflictor, entity projowner, float deathtype, float exception)
-{
-       float is_from_contents = (deathtype == DEATH_SLIME || deathtype == DEATH_LAVA);
-       float is_from_owner = (inflictor == projowner);
-       float is_from_exception = (exception != -1);
-
-       //dprint(strcat("W_CheckProjectileDamage: from_contents ", ftos(is_from_contents), " : from_owner ", ftos(is_from_owner), " : exception ", strcat(ftos(is_from_exception), " (", ftos(exception), "). \n")));
-
-       if(autocvar_g_projectiles_damage <= -2)
-       {
-               return FALSE; // no damage to projectiles at all, not even with the exceptions
-       }
-       else if(autocvar_g_projectiles_damage == -1)
-       {
-               if(is_from_exception)
-                       return (exception); // if exception is detected, allow it to override
-               else
-                       return FALSE; // otherwise, no other damage is allowed
-       }
-       else if(autocvar_g_projectiles_damage == 0)
-       {
-               if(is_from_exception)
-                       return (exception); // if exception is detected, allow it to override
-               else if (!is_from_contents)
-                       return FALSE; // otherwise, only allow damage from contents
-       }
-       else if(autocvar_g_projectiles_damage == 1)
-       {
-               if(is_from_exception)
-                       return (exception); // if exception is detected, allow it to override
-               else if (!(is_from_contents || is_from_owner))
-                       return FALSE; // otherwise, only allow self damage and damage from contents
-       }
-       else if(autocvar_g_projectiles_damage == 2) // allow any damage, but override for exceptions
-       {
-               if(is_from_exception)
-                       return (exception); // if exception is detected, allow it to override
-       }
-
-       return TRUE; // if none of these return, then allow damage anyway.
-}
-
-void W_PrepareExplosionByDamage(entity attacker, void() explode)
-{
-       self.takedamage = DAMAGE_NO;
-       self.event_damage = func_null;
-
-       if(IS_CLIENT(attacker) && !autocvar_g_projectiles_keep_owner)
-       {
-               self.owner = attacker;
-               self.realowner = attacker;
-       }
-
-       // do not explode NOW but in the NEXT FRAME!
-       // because recursive calls to RadiusDamage are not allowed
-       self.nextthink = time;
-       self.think = explode;
-}
diff --git a/qcsrc/server/w_electro.qc b/qcsrc/server/w_electro.qc
deleted file mode 100644 (file)
index 0ad23a1..0000000
+++ /dev/null
@@ -1,618 +0,0 @@
-#ifdef REGISTER_WEAPON
-REGISTER_WEAPON(
-/* WEP_##id  */ ELECTRO,
-/* function  */ w_electro,
-/* ammotype  */ IT_CELLS,
-/* impulse   */ 5,
-/* flags     */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH,
-/* rating    */ BOT_PICKUP_RATING_MID,
-/* model     */ "electro",
-/* shortname */ "electro",
-/* fullname  */ _("Electro")
-);
-#else
-#ifdef SVQC
-.float electro_count;
-.float electro_secondarytime;
-
-void W_Plasma_Explode_Combo (void);
-
-void W_Plasma_TriggerCombo(vector org, float rad, entity own)
-{
-       entity e;
-       e = WarpZone_FindRadius(org, rad, TRUE);
-       while (e)
-       {
-               if (e.classname == "plasma")
-               {
-                       // change owner to whoever caused the combo explosion
-                       e.realowner = own;
-                       e.takedamage = DAMAGE_NO;
-                       e.classname = "plasma_chain";
-                       e.think = W_Plasma_Explode_Combo;
-                       e.nextthink = time + vlen(e.WarpZone_findradius_dist) / autocvar_g_balance_electro_combo_speed; // delay combo chains, looks cooler
-               }
-               e = e.chain;
-       }
-}
-
-void W_Plasma_Explode (void)
-{
-       if(other.takedamage == DAMAGE_AIM)
-               if(IS_PLAYER(other))
-                       if(DIFF_TEAM(self.realowner, other))
-                               if(other.deadflag == DEAD_NO)
-                                       if(IsFlying(other))
-                                               Send_Notification(NOTIF_ONE, self.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_ELECTROBITCH);
-
-       self.event_damage = func_null;
-       self.takedamage = DAMAGE_NO;
-       if (self.movetype == MOVETYPE_BOUNCE)
-       {
-               RadiusDamage (self, self.realowner, autocvar_g_balance_electro_secondary_damage, autocvar_g_balance_electro_secondary_edgedamage, autocvar_g_balance_electro_secondary_radius, world, autocvar_g_balance_electro_secondary_force, self.projectiledeathtype, other);
-       }
-       else
-       {
-               W_Plasma_TriggerCombo(self.origin, autocvar_g_balance_electro_primary_comboradius, self.realowner);
-               RadiusDamage (self, self.realowner, autocvar_g_balance_electro_primary_damage, autocvar_g_balance_electro_primary_edgedamage, autocvar_g_balance_electro_primary_radius, world, autocvar_g_balance_electro_primary_force, self.projectiledeathtype, other);
-       }
-
-       remove (self);
-}
-
-void W_Plasma_Explode_Combo (void)
-{
-       W_Plasma_TriggerCombo(self.origin, autocvar_g_balance_electro_combo_comboradius, self.realowner);
-
-       self.event_damage = func_null;
-       RadiusDamage (self, self.realowner, autocvar_g_balance_electro_combo_damage, autocvar_g_balance_electro_combo_edgedamage, autocvar_g_balance_electro_combo_radius, world, autocvar_g_balance_electro_combo_force, WEP_ELECTRO | HITTYPE_BOUNCE, world); // use THIS type for a combo because primary can't bounce
-       remove (self);
-}
-
-void W_Plasma_Touch (void)
-{
-       //self.velocity = self.velocity  * 0.1;
-
-       PROJECTILE_TOUCH;
-       if (other.takedamage == DAMAGE_AIM) {
-               W_Plasma_Explode ();
-       } else {
-               //UpdateCSQCProjectile(self);
-               spamsound (self, CH_SHOTS, "weapons/electro_bounce.wav", VOL_BASE, ATTEN_NORM);
-               self.projectiledeathtype |= HITTYPE_BOUNCE;
-       }
-}
-
-void W_Plasma_TouchExplode (void)
-{
-       PROJECTILE_TOUCH;
-       W_Plasma_Explode ();
-}
-
-void W_Plasma_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-{
-       if(self.health <= 0)
-               return;
-
-       // note: combos are usually triggered by W_Plasma_TriggerCombo, not damage
-       float is_combo = (inflictor.classname == "plasma_chain" || inflictor.classname == "plasma_prim");
-
-       if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, (is_combo ? 1 : -1)))
-               return; // g_projectiles_damage says to halt
-
-       self.health = self.health - damage;
-       if (self.health <= 0)
-       {
-               self.takedamage = DAMAGE_NO;
-               self.nextthink = time;
-               if (is_combo)
-               {
-                       // change owner to whoever caused the combo explosion
-                       self.realowner = inflictor.realowner;
-                       self.classname = "plasma_chain";
-                       self.think = W_Plasma_Explode_Combo;
-                       self.nextthink = time + min(autocvar_g_balance_electro_combo_radius, vlen(self.origin - inflictor.origin)) / autocvar_g_balance_electro_combo_speed; // delay combo chains, looks cooler
-                               //                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ bounding the length, because inflictor may be in a galaxy far far away (warpzones)
-               }
-               else
-               {
-                       self.use = W_Plasma_Explode;
-                       self.think = adaptor_think2use; // not _hittype_splash, as this runs "immediately"
-               }
-       }
-}
-
-void W_Electro_Attack()
-{
-       entity proj;
-
-       W_DecreaseAmmo(ammo_cells, autocvar_g_balance_electro_primary_ammo, autocvar_g_balance_electro_reload_ammo);
-
-       W_SetupShot_ProjectileSize (self, '0 0 -3', '0 0 -3', FALSE, 2, "weapons/electro_fire.wav", CH_WEAPON_A, autocvar_g_balance_electro_primary_damage);
-
-       pointparticles(particleeffectnum("electro_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
-       proj = spawn ();
-       proj.classname = "plasma_prim";
-       proj.owner = proj.realowner = self;
-       proj.bot_dodge = TRUE;
-       proj.bot_dodgerating = autocvar_g_balance_electro_primary_damage;
-       proj.use = W_Plasma_Explode;
-       proj.think = adaptor_think2use_hittype_splash;
-       proj.nextthink = time + autocvar_g_balance_electro_primary_lifetime;
-       PROJECTILE_MAKETRIGGER(proj);
-       proj.projectiledeathtype = WEP_ELECTRO;
-       setorigin(proj, w_shotorg);
-
-       proj.movetype = MOVETYPE_FLY;
-       W_SETUPPROJECTILEVELOCITY(proj, g_balance_electro_primary);
-       proj.angles = vectoangles(proj.velocity);
-       proj.touch = W_Plasma_TouchExplode;
-       setsize(proj, '0 0 -3', '0 0 -3');
-       proj.flags = FL_PROJECTILE;
-       proj.missile_flags = MIF_SPLASH;
-
-       CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO_BEAM, TRUE);
-
-       other = proj; MUTATOR_CALLHOOK(EditProjectile);
-}
-
-void W_Electro_Attack2()
-{
-       entity proj;
-
-       W_DecreaseAmmo(ammo_cells, autocvar_g_balance_electro_secondary_ammo, autocvar_g_balance_electro_reload_ammo);
-
-       W_SetupShot_ProjectileSize (self, '0 0 -4', '0 0 -4', FALSE, 2, "weapons/electro_fire2.wav", CH_WEAPON_A, autocvar_g_balance_electro_secondary_damage);
-
-       w_shotdir = v_forward; // no TrueAim for grenades please
-
-       pointparticles(particleeffectnum("electro_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
-       proj = spawn ();
-       proj.classname = "plasma";
-       proj.owner = proj.realowner = self;
-       proj.use = W_Plasma_Explode;
-       proj.think = adaptor_think2use_hittype_splash;
-       proj.bot_dodge = TRUE;
-       proj.bot_dodgerating = autocvar_g_balance_electro_secondary_damage;
-       proj.nextthink = time + autocvar_g_balance_electro_secondary_lifetime;
-       PROJECTILE_MAKETRIGGER(proj);
-       proj.projectiledeathtype = WEP_ELECTRO | HITTYPE_SECONDARY;
-       setorigin(proj, w_shotorg);
-
-       //proj.glow_size = 50;
-       //proj.glow_color = 45;
-       proj.movetype = MOVETYPE_BOUNCE;
-       W_SETUPPROJECTILEVELOCITY_UP(proj, g_balance_electro_secondary);
-       proj.touch = W_Plasma_Touch;
-       setsize(proj, '0 0 -4', '0 0 -4');
-       proj.takedamage = DAMAGE_YES;
-       proj.damageforcescale = autocvar_g_balance_electro_secondary_damageforcescale;
-       proj.health = autocvar_g_balance_electro_secondary_health;
-       proj.event_damage = W_Plasma_Damage;
-       proj.flags = FL_PROJECTILE;
-       proj.damagedbycontents = (autocvar_g_balance_electro_secondary_damagedbycontents);
-
-       proj.bouncefactor = autocvar_g_balance_electro_secondary_bouncefactor;
-       proj.bouncestop = autocvar_g_balance_electro_secondary_bouncestop;
-       proj.missile_flags = MIF_SPLASH | MIF_ARC;
-
-#if 0
-       entity p2;
-       p2 = spawn();
-       copyentity(proj, p2);
-       setmodel(p2, "models/ebomb.mdl");
-       setsize(p2, proj.mins, proj.maxs);
-#endif
-
-       CSQCProjectile(proj, TRUE, PROJECTILE_ELECTRO, FALSE); // no culling, it has sound
-
-       other = proj; MUTATOR_CALLHOOK(EditProjectile);
-}
-
-.vector hook_start, hook_end;
-float lgbeam_send(entity to, float sf)
-{
-       WriteByte(MSG_ENTITY, ENT_CLIENT_LGBEAM);
-       sf = sf & 0x7F;
-       if(sound_allowed(MSG_BROADCAST, self.realowner))
-               sf |= 0x80;
-       WriteByte(MSG_ENTITY, sf);
-       if(sf & 1)
-       {
-               WriteByte(MSG_ENTITY, num_for_edict(self.realowner));
-               WriteCoord(MSG_ENTITY, autocvar_g_balance_electro_primary_range);
-       }
-       if(sf & 2)
-       {
-               WriteCoord(MSG_ENTITY, self.hook_start_x);
-               WriteCoord(MSG_ENTITY, self.hook_start_y);
-               WriteCoord(MSG_ENTITY, self.hook_start_z);
-       }
-       if(sf & 4)
-       {
-               WriteCoord(MSG_ENTITY, self.hook_end_x);
-               WriteCoord(MSG_ENTITY, self.hook_end_y);
-               WriteCoord(MSG_ENTITY, self.hook_end_z);
-       }
-       return TRUE;
-}
-.entity lgbeam;
-.float prevlgfire;
-float lgbeam_checkammo()
-{
-       if(self.realowner.items & IT_UNLIMITED_WEAPON_AMMO)
-               return TRUE;
-       else if(autocvar_g_balance_electro_reload_ammo)
-               return self.realowner.clip_load > 0;
-       else
-               return self.realowner.ammo_cells > 0;
-}
-
-entity lgbeam_owner_ent;
-void lgbeam_think()
-{
-       entity owner_player;
-       owner_player = self.realowner;
-
-       owner_player.prevlgfire = time;
-       if (self != owner_player.lgbeam)
-       {
-               remove(self);
-               return;
-       }
-
-       if (owner_player.weaponentity.state != WS_INUSE || !lgbeam_checkammo() || owner_player.deadflag != DEAD_NO || !owner_player.BUTTON_ATCK || owner_player.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 (!(owner_player.items & IT_UNLIMITED_WEAPON_AMMO))
-       {
-               if(autocvar_g_balance_electro_primary_ammo)
-               {
-                       if(autocvar_g_balance_electro_reload_ammo)
-                       {
-                               dt = min(dt, owner_player.clip_load / autocvar_g_balance_electro_primary_ammo);
-                               owner_player.clip_load = max(0, owner_player.clip_load - autocvar_g_balance_electro_primary_ammo * frametime);
-                               owner_player.(weapon_load[WEP_ELECTRO]) = owner_player.clip_load;
-                       }
-                       else
-                       {
-                               dt = min(dt, owner_player.ammo_cells / autocvar_g_balance_electro_primary_ammo);
-                               owner_player.ammo_cells = max(0, owner_player.ammo_cells - autocvar_g_balance_electro_primary_ammo * frametime);
-                       }
-               }
-       }
-
-       W_SetupShot_Range(owner_player, TRUE, 0, "", 0, autocvar_g_balance_electro_primary_damage * dt, autocvar_g_balance_electro_primary_range);
-       if(!lgbeam_owner_ent)
-       {
-               lgbeam_owner_ent = spawn();
-               lgbeam_owner_ent.classname = "lgbeam_owner_ent";
-       }
-       WarpZone_traceline_antilag(lgbeam_owner_ent, w_shotorg, w_shotend, MOVE_NORMAL, lgbeam_owner_ent, ANTILAG_LATENCY(owner_player));
-
-       // apply the damage
-       if(trace_ent)
-       {
-               vector force;
-               force = w_shotdir * autocvar_g_balance_electro_primary_force + '0 0 1' * autocvar_g_balance_electro_primary_force_up;
-
-               f = ExponentialFalloff(autocvar_g_balance_electro_primary_falloff_mindist, autocvar_g_balance_electro_primary_falloff_maxdist, autocvar_g_balance_electro_primary_falloff_halflifedist, vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos) - w_shotorg));
-
-               if(accuracy_isgooddamage(owner_player, trace_ent))
-                       accuracy_add(owner_player, WEP_ELECTRO, 0, autocvar_g_balance_electro_primary_damage * dt * f);
-               Damage (trace_ent, owner_player, owner_player, autocvar_g_balance_electro_primary_damage * dt * f, WEP_ELECTRO, trace_endpos, force * dt);
-       }
-       W_Plasma_TriggerCombo(trace_endpos, autocvar_g_balance_electro_primary_comboradius, owner_player);
-
-       // draw effect
-       if(w_shotorg != self.hook_start)
-       {
-               self.SendFlags |= 2;
-               self.hook_start = w_shotorg;
-       }
-       if(w_shotend != self.hook_end)
-       {
-               self.SendFlags |= 4;
-               self.hook_end = w_shotend;
-       }
-}
-
-// experimental lightning gun
-void W_Electro_Attack3 (void)
-{
-       // only play fire sound if 0.5 sec has passed since player let go the fire button
-       if(time - self.prevlgfire > 0.5)
-               sound (self, CH_WEAPON_A, "weapons/lgbeam_fire.wav", VOL_BASE, ATTEN_NORM);
-
-       entity beam, oldself;
-
-       self.lgbeam = beam = spawn();
-       beam.classname = "lgbeam";
-       beam.solid = SOLID_NOT;
-       beam.think = lgbeam_think;
-       beam.owner = beam.realowner = self;
-       beam.movetype = MOVETYPE_NONE;
-       beam.shot_spread = 0;
-       beam.bot_dodge = TRUE;
-       beam.bot_dodgerating = autocvar_g_balance_electro_primary_damage;
-       Net_LinkEntity(beam, FALSE, 0, lgbeam_send);
-
-       oldself = self;
-       self = beam;
-       self.think();
-       self = oldself;
-}
-
-void ElectroInit()
-{
-       weapon_action(WEP_ELECTRO, WR_PRECACHE);
-       electro_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ELECTRO), FALSE, FALSE, 1);
-       electro_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ELECTRO), FALSE, FALSE, 2);
-       electro_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ELECTRO), FALSE, FALSE, 3);
-       electro_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_ELECTRO), FALSE, FALSE, 4);
-}
-
-void spawnfunc_weapon_electro (void)
-{
-       weapon_defaultspawnfunc(WEP_ELECTRO);
-}
-
-void w_electro_checkattack()
-{
-       if(self.electro_count > 1)
-       if(self.BUTTON_ATCK2)
-       if(weapon_prepareattack(1, -1))
-       {
-               W_Electro_Attack2();
-               self.electro_count -= 1;
-               weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_electro_secondary_animtime, w_electro_checkattack);
-               return;
-       }
-
-       w_ready();
-}
-
-.float bot_secondary_electromooth;
-.float BUTTON_ATCK_prev;
-float w_electro(float req)
-{
-       float ammo_amount;
-       if (req == WR_AIM)
-       {
-               self.BUTTON_ATCK=FALSE;
-               self.BUTTON_ATCK2=FALSE;
-               if(vlen(self.origin-self.enemy.origin) > 1000)
-                       self.bot_secondary_electromooth = 0;
-               if(self.bot_secondary_electromooth == 0)
-               {
-                       float shoot;
-
-                       if(autocvar_g_balance_electro_primary_speed)
-                               shoot = bot_aim(autocvar_g_balance_electro_primary_speed, 0, autocvar_g_balance_electro_primary_lifetime, FALSE);
-                       else
-                               shoot = bot_aim(1000000, 0, 0.001, FALSE);
-
-                       if(shoot)
-                       {
-                               self.BUTTON_ATCK = TRUE;
-                               if(random() < 0.01) self.bot_secondary_electromooth = 1;
-                       }
-               }
-               else
-               {
-                       if(bot_aim(autocvar_g_balance_electro_secondary_speed, autocvar_g_balance_grenadelauncher_secondary_speed_up, autocvar_g_balance_electro_secondary_lifetime, TRUE))
-                       {
-                               self.BUTTON_ATCK2 = TRUE;
-                               if(random() < 0.03) self.bot_secondary_electromooth = 0;
-                       }
-               }
-       }
-       else if (req == WR_THINK)
-       {
-               if(autocvar_g_balance_electro_reload_ammo) // forced reload
-               {
-                       ammo_amount = 0;
-                       if(autocvar_g_balance_electro_lightning)
-                       {
-                               if(self.clip_load > 0)
-                                       ammo_amount = 1;
-                       }
-                       else if(self.clip_load >= autocvar_g_balance_electro_primary_ammo)
-                               ammo_amount = 1;
-                       if(self.clip_load >= autocvar_g_balance_electro_secondary_ammo)
-                               ammo_amount += 1;
-
-                       if(!ammo_amount)
-                       {
-                               weapon_action(self.weapon, WR_RELOAD);
-                               return FALSE;
-                       }
-               }
-               if (self.BUTTON_ATCK)
-               {
-                       if(autocvar_g_balance_electro_lightning)
-                               if(self.BUTTON_ATCK_prev)
-                                       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_electro_primary_animtime, w_ready);
-
-                       if (weapon_prepareattack(0, (autocvar_g_balance_electro_lightning ? 0 : autocvar_g_balance_electro_primary_refire)))
-                       {
-                               if(autocvar_g_balance_electro_lightning)
-                               {
-                                       if ((!self.lgbeam) || wasfreed(self.lgbeam))
-                                       {
-                                               W_Electro_Attack3();
-                                       }
-                                       if(!self.BUTTON_ATCK_prev)
-                                       {
-                                               weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_electro_primary_animtime, w_ready);
-                                               self.BUTTON_ATCK_prev = 1;
-                                       }
-                               }
-                               else
-                               {
-                                       W_Electro_Attack();
-                                       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_electro_primary_animtime, w_ready);
-                               }
-                       }
-               } else {
-                       if(autocvar_g_balance_electro_lightning)
-                       {
-                               if (self.BUTTON_ATCK_prev != 0)
-                               {
-                                       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_electro_primary_animtime, w_ready);
-                                       ATTACK_FINISHED(self) = time + autocvar_g_balance_electro_primary_refire * W_WeaponRateFactor();
-                               }
-                               self.BUTTON_ATCK_prev = 0;
-                       }
-
-                       if (self.BUTTON_ATCK2)
-                       {
-                               if (time >= self.electro_secondarytime)
-                               if (weapon_prepareattack(1, autocvar_g_balance_electro_secondary_refire))
-                               {
-                                       W_Electro_Attack2();
-                                       self.electro_count = autocvar_g_balance_electro_secondary_count;
-                                       weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_electro_secondary_animtime, w_electro_checkattack);
-                                       self.electro_secondarytime = time + autocvar_g_balance_electro_secondary_refire2 * W_WeaponRateFactor();
-                               }
-                       }
-               }
-       }
-       else if (req == WR_PRECACHE)
-       {
-               precache_model ("models/weapons/g_electro.md3");
-               precache_model ("models/weapons/v_electro.md3");
-               precache_model ("models/weapons/h_electro.iqm");
-               precache_sound ("weapons/electro_bounce.wav");
-               precache_sound ("weapons/electro_fire.wav");
-               precache_sound ("weapons/electro_fire2.wav");
-               precache_sound ("weapons/electro_impact.wav");
-               precache_sound ("weapons/electro_impact_combo.wav");
-               //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else
-               if(autocvar_g_balance_electro_lightning)
-               {
-                       precache_sound ("weapons/lgbeam_fire.wav");
-               }
-       }
-       else if (req == WR_SETUP)
-       {
-               weapon_setup(WEP_ELECTRO);
-               self.current_ammo = ammo_cells;
-       }
-       else if (req == WR_CHECKAMMO1)
-       {
-               if(autocvar_g_balance_electro_lightning)
-               {
-                       if(!autocvar_g_balance_electro_primary_ammo)
-                               ammo_amount = 1;
-                       else
-                               ammo_amount = self.ammo_cells > 0;
-                       ammo_amount += self.(weapon_load[WEP_ELECTRO]) > 0;
-               }
-               else
-               {
-                       ammo_amount = self.ammo_cells >= autocvar_g_balance_electro_primary_ammo;
-                       ammo_amount += self.(weapon_load[WEP_ELECTRO]) >= autocvar_g_balance_electro_primary_ammo;
-               }
-               return ammo_amount;
-       }
-       else if (req == WR_CHECKAMMO2)
-       {
-               if(autocvar_g_balance_electro_combo_safeammocheck) // true if you can fire at least one secondary blob AND one primary shot after it, otherwise false.
-               {
-                       ammo_amount = self.ammo_cells >= autocvar_g_balance_electro_secondary_ammo + autocvar_g_balance_electro_primary_ammo;
-                       ammo_amount += self.(weapon_load[WEP_ELECTRO]) >= autocvar_g_balance_electro_secondary_ammo + autocvar_g_balance_electro_primary_ammo;
-               }
-               else
-               {
-                       ammo_amount = self.ammo_cells >= autocvar_g_balance_electro_secondary_ammo;
-                       ammo_amount += self.(weapon_load[WEP_ELECTRO]) >= autocvar_g_balance_electro_secondary_ammo;
-               }
-               return ammo_amount;
-       }
-       else if (req == WR_RESETPLAYER)
-       {
-               self.electro_secondarytime = time;
-       }
-       else if (req == WR_RELOAD)
-       {
-               W_Reload(min(autocvar_g_balance_electro_primary_ammo, autocvar_g_balance_electro_secondary_ammo), autocvar_g_balance_electro_reload_ammo, autocvar_g_balance_electro_reload_time, "weapons/reload.wav");
-       }
-       else if (req == WR_SUICIDEMESSAGE)
-       {
-               if(w_deathtype & HITTYPE_SECONDARY)
-                       return WEAPON_ELECTRO_SUICIDE_ORBS;
-               else
-                       return WEAPON_ELECTRO_SUICIDE_BOLT;
-       }
-       else if (req == WR_KILLMESSAGE)
-       {
-               if(w_deathtype & HITTYPE_SECONDARY)
-               {
-                       return WEAPON_ELECTRO_MURDER_ORBS;
-               }
-               else
-               {
-                       if(w_deathtype & HITTYPE_BOUNCE)
-                               return WEAPON_ELECTRO_MURDER_COMBO;
-                       else
-                               return WEAPON_ELECTRO_MURDER_BOLT;
-               }
-       }
-       return TRUE;
-}
-#endif
-#ifdef CSQC
-float w_electro(float req)
-{
-       if(req == WR_IMPACTEFFECT)
-       {
-               vector org2;
-               org2 = w_org + w_backoff * 6;
-               if(w_deathtype & HITTYPE_SECONDARY)
-               {
-                       pointparticles(particleeffectnum("electro_ballexplode"), org2, '0 0 0', 1);
-                       if(!w_issilent)
-                               sound(self, CH_SHOTS, "weapons/electro_impact.wav", VOL_BASE, ATTEN_NORM);
-               }
-               else
-               {
-                       if(w_deathtype & HITTYPE_BOUNCE)
-                       {
-                               // this is sent as "primary (w_deathtype & HITTYPE_BOUNCE)" to distinguish it from (w_deathtype & HITTYPE_SECONDARY) bounced balls
-                               pointparticles(particleeffectnum("electro_combo"), org2, '0 0 0', 1);
-                               if(!w_issilent)
-                                       sound(self, CH_SHOTS, "weapons/electro_impact_combo.wav", VOL_BASE, ATTEN_NORM);
-                       }
-                       else
-                       {
-                               pointparticles(particleeffectnum("electro_impact"), org2, '0 0 0', 1);
-                               if(!w_issilent)
-                                       sound(self, CH_SHOTS, "weapons/electro_impact.wav", VOL_BASE, ATTEN_NORM);
-                       }
-               }
-       }
-       else if(req == WR_PRECACHE)
-       {
-               precache_sound("weapons/electro_impact.wav");
-               precache_sound("weapons/electro_impact_combo.wav");
-       }
-       return TRUE;
-}
-#endif
-#endif
diff --git a/qcsrc/server/w_electro.qh b/qcsrc/server/w_electro.qh
deleted file mode 100644 (file)
index 98c0be1..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-void ElectroInit();
-vector electro_shotorigin[4];
diff --git a/qcsrc/server/w_fireball.qc b/qcsrc/server/w_fireball.qc
deleted file mode 100644 (file)
index 88e9d77..0000000
+++ /dev/null
@@ -1,434 +0,0 @@
-#ifdef REGISTER_WEAPON
-REGISTER_WEAPON(
-/* WEP_##id  */ FIREBALL,
-/* function  */ w_fireball,
-/* ammotype  */ 0,
-/* impulse   */ 9,
-/* flags     */ WEP_FLAG_SUPERWEAPON | WEP_TYPE_SPLASH,
-/* rating    */ BOT_PICKUP_RATING_MID,
-/* model     */ "fireball",
-/* shortname */ "fireball",
-/* fullname  */ _("Fireball")
-);
-#else
-#ifdef SVQC
-.float bot_primary_fireballmooth; // whatever a mooth is
-.vector fireball_impactvec;
-.float fireball_primarytime;
-
-void W_Fireball_Explode (void)
-{
-       entity e;
-       float dist;
-       float points;
-       vector dir;
-       float d;
-
-       self.event_damage = func_null;
-       self.takedamage = DAMAGE_NO;
-
-       // 1. dist damage
-       d = (self.realowner.health + self.realowner.armorvalue);
-       RadiusDamage (self, self.realowner, autocvar_g_balance_fireball_primary_damage, autocvar_g_balance_fireball_primary_edgedamage, autocvar_g_balance_fireball_primary_radius, world, autocvar_g_balance_fireball_primary_force, self.projectiledeathtype, other);
-       if(self.realowner.health + self.realowner.armorvalue >= d)
-       if(!self.cnt)
-       {
-               modeleffect_spawn("models/sphere/sphere.md3", 0, 0, self.origin, '0 0 0', '0 0 0', '0 0 0', 0, autocvar_g_balance_fireball_primary_bfgradius, 0.2, 0.05, 0.25);
-
-               // 2. bfg effect
-               // NOTE: this cannot be made warpzone aware by design. So, better intentionally ignore warpzones here.
-               for(e = findradius(self.origin, autocvar_g_balance_fireball_primary_bfgradius); e; e = e.chain)
-               if(e != self.realowner) if(e.takedamage == DAMAGE_AIM) if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self))
-               {
-                       // can we see fireball?
-                       traceline(e.origin + e.view_ofs, self.origin, MOVE_NORMAL, e);
-                       if(/* trace_startsolid || */ trace_fraction != 1) // startsolid should be never happening anyway
-                               continue;
-                       // can we see player who shot fireball?
-                       traceline(e.origin + e.view_ofs, self.realowner.origin + self.realowner.view_ofs, MOVE_NORMAL, e);
-                       if(trace_ent != self.realowner)
-                       if(/* trace_startsolid || */ trace_fraction != 1)
-                               continue;
-                       dist = vlen(self.origin - e.origin - e.view_ofs);
-                       points = (1 - sqrt(dist / autocvar_g_balance_fireball_primary_bfgradius));
-                       if(points <= 0)
-                               continue;
-                       dir = normalize(e.origin + e.view_ofs - self.origin);
-
-                       if(accuracy_isgooddamage(self.realowner, e))
-                               accuracy_add(self.realowner, WEP_FIREBALL, 0, autocvar_g_balance_fireball_primary_bfgdamage * points);
-
-                       Damage(e, self, self.realowner, autocvar_g_balance_fireball_primary_bfgdamage * points, self.projectiledeathtype | HITTYPE_BOUNCE | HITTYPE_SPLASH, e.origin + e.view_ofs, autocvar_g_balance_fireball_primary_bfgforce * dir);
-                       pointparticles(particleeffectnum("fireball_bfgdamage"), e.origin, -1 * dir, 1);
-               }
-       }
-
-       remove (self);
-}
-
-void W_Fireball_TouchExplode (void)
-{
-       PROJECTILE_TOUCH;
-       W_Fireball_Explode ();
-}
-
-void W_Fireball_LaserPlay(float dt, float dist, float damage, float edgedamage, float burntime)
-{
-       entity e;
-       float d;
-       vector p;
-
-       if(damage <= 0)
-               return;
-
-       RandomSelection_Init();
-       for(e = WarpZone_FindRadius(self.origin, dist, TRUE); e; e = e.chain)
-       if(e != self.realowner) if(e.takedamage == DAMAGE_AIM) if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self))
-       {
-               p = e.origin;
-               p_x += e.mins_x + random() * (e.maxs_x - e.mins_x);
-               p_y += e.mins_y + random() * (e.maxs_y - e.mins_y);
-               p_z += e.mins_z + random() * (e.maxs_z - e.mins_z);
-               d = vlen(WarpZone_UnTransformOrigin(e, self.origin) - p);
-               if(d < dist)
-               {
-                       e.fireball_impactvec = p;
-                       RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e));
-               }
-       }
-       if(RandomSelection_chosen_ent)
-       {
-               d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec);
-               d = damage + (edgedamage - damage) * (d / dist);
-               Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE);
-               //trailparticles(self, particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec);
-               pointparticles(particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1);
-       }
-}
-
-void W_Fireball_Think()
-{
-       if(time > self.pushltime)
-       {
-               self.cnt = 1;
-               self.projectiledeathtype |= HITTYPE_SPLASH;
-               W_Fireball_Explode();
-               return;
-       }
-
-       W_Fireball_LaserPlay(0.1, autocvar_g_balance_fireball_primary_laserradius, autocvar_g_balance_fireball_primary_laserdamage, autocvar_g_balance_fireball_primary_laseredgedamage, autocvar_g_balance_fireball_primary_laserburntime);
-
-       self.nextthink = time + 0.1;
-}
-
-void W_Fireball_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-{
-       if(self.health <= 0)
-               return;
-
-       if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
-               return; // g_projectiles_damage says to halt
-
-       self.health = self.health - damage;
-       if (self.health <= 0)
-       {
-               self.cnt = 1;
-               W_PrepareExplosionByDamage(attacker, W_Fireball_Explode);
-       }
-}
-
-void W_Fireball_Attack1()
-{
-       entity proj;
-
-       W_SetupShot_ProjectileSize (self, '-16 -16 -16', '16 16 16', FALSE, 2, "weapons/fireball_fire2.wav", CH_WEAPON_A, autocvar_g_balance_fireball_primary_damage + autocvar_g_balance_fireball_primary_bfgdamage);
-
-       pointparticles(particleeffectnum("fireball_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
-       proj = spawn ();
-       proj.classname = "plasma_prim";
-       proj.owner = proj.realowner = self;
-       proj.bot_dodge = TRUE;
-       proj.bot_dodgerating = autocvar_g_balance_fireball_primary_damage;
-       proj.pushltime = time + autocvar_g_balance_fireball_primary_lifetime;
-       proj.use = W_Fireball_Explode;
-       proj.think = W_Fireball_Think;
-       proj.nextthink = time;
-       proj.health = autocvar_g_balance_fireball_primary_health;
-       proj.team = self.team;
-       proj.event_damage = W_Fireball_Damage;
-       proj.takedamage = DAMAGE_YES;
-       proj.damageforcescale = autocvar_g_balance_fireball_primary_damageforcescale;
-       PROJECTILE_MAKETRIGGER(proj);
-       proj.projectiledeathtype = WEP_FIREBALL;
-       setorigin(proj, w_shotorg);
-
-       proj.movetype = MOVETYPE_FLY;
-       W_SETUPPROJECTILEVELOCITY(proj, g_balance_fireball_primary);
-       proj.angles = vectoangles(proj.velocity);
-       proj.touch = W_Fireball_TouchExplode;
-       setsize(proj, '-16 -16 -16', '16 16 16');
-       proj.flags = FL_PROJECTILE;
-    proj.missile_flags = MIF_SPLASH | MIF_PROXY;
-
-       CSQCProjectile(proj, TRUE, PROJECTILE_FIREBALL, TRUE);
-
-       other = proj; MUTATOR_CALLHOOK(EditProjectile);
-}
-
-void W_Fireball_AttackEffect(float i, vector f_diff)
-{
-       W_SetupShot_ProjectileSize (self, '-16 -16 -16', '16 16 16', FALSE, 0, "", 0, 0);
-       w_shotorg += f_diff_x * v_up + f_diff_y * v_right;
-       pointparticles(particleeffectnum("fireball_preattack_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-}
-
-void W_Fireball_Attack1_Frame4()
-{
-       W_Fireball_Attack1();
-       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_fireball_primary_animtime, w_ready);
-}
-
-void W_Fireball_Attack1_Frame3()
-{
-       W_Fireball_AttackEffect(0, '+1.25 +3.75 0');
-       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_fireball_primary_animtime, W_Fireball_Attack1_Frame4);
-}
-
-void W_Fireball_Attack1_Frame2()
-{
-       W_Fireball_AttackEffect(0, '-1.25 +3.75 0');
-       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_fireball_primary_animtime, W_Fireball_Attack1_Frame3);
-}
-
-void W_Fireball_Attack1_Frame1()
-{
-       W_Fireball_AttackEffect(1, '+1.25 -3.75 0');
-       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_fireball_primary_animtime, W_Fireball_Attack1_Frame2);
-}
-
-void W_Fireball_Attack1_Frame0()
-{
-       W_Fireball_AttackEffect(0, '-1.25 -3.75 0');
-       sound (self, CH_WEAPON_SINGLE, "weapons/fireball_prefire2.wav", VOL_BASE, ATTEN_NORM);
-       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_fireball_primary_animtime, W_Fireball_Attack1_Frame1);
-}
-
-void W_Firemine_Think()
-{
-       if(time > self.pushltime)
-       {
-               remove(self);
-               return;
-       }
-
-       // make it "hot" once it leaves its owner
-       if(self.owner)
-       {
-               if(vlen(self.origin - self.owner.origin - self.owner.view_ofs) > autocvar_g_balance_fireball_secondary_laserradius)
-               {
-                       self.cnt += 1;
-                       if(self.cnt == 3)
-                               self.owner = world;
-               }
-               else
-                       self.cnt = 0;
-       }
-
-       W_Fireball_LaserPlay(0.1, autocvar_g_balance_fireball_secondary_laserradius, autocvar_g_balance_fireball_secondary_laserdamage, autocvar_g_balance_fireball_secondary_laseredgedamage, autocvar_g_balance_fireball_secondary_laserburntime);
-
-       self.nextthink = time + 0.1;
-}
-
-void W_Firemine_Touch (void)
-{
-       PROJECTILE_TOUCH;
-       if (other.takedamage == DAMAGE_AIM)
-       if(Fire_AddDamage(other, self.realowner, autocvar_g_balance_fireball_secondary_damage, autocvar_g_balance_fireball_secondary_damagetime, self.projectiledeathtype) >= 0)
-       {
-               remove(self);
-               return;
-       }
-       self.projectiledeathtype |= HITTYPE_BOUNCE;
-}
-
-void W_Fireball_Attack2()
-{
-       entity proj;
-       vector f_diff;
-       float c;
-
-       c = mod(self.bulletcounter, 4);
-       switch(c)
-       {
-               case 0:
-                       f_diff = '-1.25 -3.75 0';
-                       break;
-               case 1:
-                       f_diff = '+1.25 -3.75 0';
-                       break;
-               case 2:
-                       f_diff = '-1.25 +3.75 0';
-                       break;
-               case 3:
-               default:
-                       f_diff = '+1.25 +3.75 0';
-                       break;
-       }
-       W_SetupShot_ProjectileSize(self, '-4 -4 -4', '4 4 4', FALSE, 2, "weapons/fireball_fire.wav", CH_WEAPON_A, autocvar_g_balance_fireball_secondary_damage);
-       traceline(w_shotorg, w_shotorg + f_diff_x * v_up + f_diff_y * v_right, MOVE_NORMAL, self);
-       w_shotorg = trace_endpos;
-
-       pointparticles(particleeffectnum("fireball_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
-       proj = spawn ();
-       proj.owner = proj.realowner = self;
-       proj.classname = "grenade";
-       proj.bot_dodge = TRUE;
-       proj.bot_dodgerating = autocvar_g_balance_fireball_secondary_damage;
-       proj.movetype = MOVETYPE_BOUNCE;
-       proj.projectiledeathtype = WEP_FIREBALL | HITTYPE_SECONDARY;
-       proj.touch = W_Firemine_Touch;
-       PROJECTILE_MAKETRIGGER(proj);
-       setsize(proj, '-4 -4 -4', '4 4 4');
-       setorigin(proj, w_shotorg);
-       proj.think = W_Firemine_Think;
-       proj.nextthink = time;
-       proj.damageforcescale = autocvar_g_balance_fireball_secondary_damageforcescale;
-       proj.pushltime = time + autocvar_g_balance_fireball_secondary_lifetime;
-       W_SETUPPROJECTILEVELOCITY_UP(proj, g_balance_fireball_secondary);
-
-       proj.angles = vectoangles(proj.velocity);
-       proj.flags = FL_PROJECTILE;
-    proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC;
-
-       CSQCProjectile(proj, TRUE, PROJECTILE_FIREMINE, TRUE);
-
-       other = proj; MUTATOR_CALLHOOK(EditProjectile);
-}
-
-void spawnfunc_weapon_fireball (void)
-{
-       weapon_defaultspawnfunc(WEP_FIREBALL);
-}
-
-float w_fireball(float req)
-{
-       //float ammo_amount;
-       if (req == WR_AIM)
-       {
-               self.BUTTON_ATCK = FALSE;
-               self.BUTTON_ATCK2 = FALSE;
-               if (self.bot_primary_fireballmooth == 0)
-               {
-                       if(bot_aim(autocvar_g_balance_fireball_primary_speed, 0, autocvar_g_balance_fireball_primary_lifetime, FALSE))
-                       {
-                               self.BUTTON_ATCK = TRUE;
-                               if(random() < 0.02) self.bot_primary_fireballmooth = 0;
-                       }
-               }
-               else
-               {
-                       if(bot_aim(autocvar_g_balance_fireball_secondary_speed, autocvar_g_balance_fireball_secondary_speed_up, autocvar_g_balance_fireball_secondary_lifetime, TRUE))
-                       {
-                               self.BUTTON_ATCK2 = TRUE;
-                               if(random() < 0.01) self.bot_primary_fireballmooth = 1;
-                       }
-               }
-       }
-       else if (req == WR_THINK)
-       {
-               if (self.BUTTON_ATCK)
-               {
-                       if (time >= self.fireball_primarytime)
-                       if (weapon_prepareattack(0, autocvar_g_balance_fireball_primary_refire))
-                       {
-                               W_Fireball_Attack1_Frame0();
-                               self.fireball_primarytime = time + autocvar_g_balance_fireball_primary_refire2 * W_WeaponRateFactor();
-                       }
-               }
-               else if (self.BUTTON_ATCK2)
-               {
-                       if (weapon_prepareattack(1, autocvar_g_balance_fireball_secondary_refire))
-                       {
-                               W_Fireball_Attack2();
-                               weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_fireball_secondary_animtime, w_ready);
-                       }
-               }
-       }
-       else if (req == WR_PRECACHE)
-       {
-               precache_model ("models/weapons/g_fireball.md3");
-               precache_model ("models/weapons/v_fireball.md3");
-               precache_model ("models/weapons/h_fireball.iqm");
-               precache_model ("models/sphere/sphere.md3");
-               precache_sound ("weapons/fireball_fire.wav");
-               precache_sound ("weapons/fireball_fire2.wav");
-               precache_sound ("weapons/fireball_prefire2.wav");
-               //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else
-       }
-       else if (req == WR_SETUP)
-       {
-               weapon_setup(WEP_FIREBALL);
-               self.current_ammo = ammo_none;
-       }
-       else if (req == WR_CHECKAMMO1)
-       {
-               return 1;
-       }
-       else if (req == WR_CHECKAMMO2)
-       {
-               return 1;
-       }
-       else if (req == WR_RESETPLAYER)
-       {
-               self.fireball_primarytime = time;
-       }
-       else if (req == WR_SUICIDEMESSAGE)
-       {
-               if(w_deathtype & HITTYPE_SECONDARY)
-                       return WEAPON_FIREBALL_SUICIDE_FIREMINE;
-               else
-                       return WEAPON_FIREBALL_SUICIDE_BLAST;
-       }
-       else if (req == WR_KILLMESSAGE)
-       {
-               if(w_deathtype & HITTYPE_SECONDARY)
-               {
-                       return WEAPON_FIREBALL_MURDER_FIREMINE;
-               }
-               else
-               {
-                       return WEAPON_FIREBALL_MURDER_BLAST;
-               }
-       }
-       return TRUE;
-}
-#endif
-#ifdef CSQC
-float w_fireball(float req)
-{
-       if(req == WR_IMPACTEFFECT)
-       {
-               vector org2;
-               if(w_deathtype & HITTYPE_SECONDARY)
-               {
-                       // firemine goes out silently
-               }
-               else
-               {
-                       org2 = w_org + w_backoff * 16;
-                       pointparticles(particleeffectnum("fireball_explode"), org2, '0 0 0', 1);
-                       if(!w_issilent)
-                               sound(self, CH_SHOTS, "weapons/fireball_impact2.wav", VOL_BASE, ATTEN_NORM * 0.25); // long range boom
-               }
-       }
-       else if(req == WR_PRECACHE)
-       {
-               precache_sound("weapons/fireball_impact2.wav");
-       }
-
-       return TRUE;
-}
-#endif
-#endif
diff --git a/qcsrc/server/w_hagar.qc b/qcsrc/server/w_hagar.qc
deleted file mode 100644 (file)
index 05768fc..0000000
+++ /dev/null
@@ -1,489 +0,0 @@
-#ifdef REGISTER_WEAPON
-REGISTER_WEAPON(
-/* WEP_##id  */ HAGAR,
-/* function  */ w_hagar,
-/* ammotype  */ IT_ROCKETS,
-/* impulse   */ 8,
-/* flags     */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
-/* rating    */ BOT_PICKUP_RATING_MID,
-/* model     */ "hagar",
-/* shortname */ "hagar",
-/* fullname  */ _("Hagar")
-);
-#else
-#ifdef SVQC
-// NO bounce protection, as bounces are limited!
-
-void W_Hagar_Explode (void)
-{
-       self.event_damage = func_null;
-       RadiusDamage (self, self.realowner, autocvar_g_balance_hagar_primary_damage, autocvar_g_balance_hagar_primary_edgedamage, autocvar_g_balance_hagar_primary_radius, world, autocvar_g_balance_hagar_primary_force, self.projectiledeathtype, other);
-
-       remove (self);
-}
-
-void W_Hagar_Explode2 (void)
-{
-       self.event_damage = func_null;
-       RadiusDamage (self, self.realowner, autocvar_g_balance_hagar_secondary_damage, autocvar_g_balance_hagar_secondary_edgedamage, autocvar_g_balance_hagar_secondary_radius, world, autocvar_g_balance_hagar_secondary_force, self.projectiledeathtype, other);
-
-       remove (self);
-}
-
-void W_Hagar_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-{
-       if (self.health <= 0)
-               return;
-
-       float is_linkexplode = ( ((inflictor.owner != world) ? (inflictor.owner == self.owner) : TRUE)
-               && (inflictor.projectiledeathtype & HITTYPE_SECONDARY)
-               && (self.projectiledeathtype & HITTYPE_SECONDARY));
-
-       if(is_linkexplode)
-               is_linkexplode = (is_linkexplode && autocvar_g_balance_hagar_secondary_load_linkexplode);
-       else
-               is_linkexplode = -1; // not secondary load, so continue as normal without exception.
-
-       if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, is_linkexplode))
-               return; // g_projectiles_damage says to halt
-
-       self.health = self.health - damage;
-       self.angles = vectoangles(self.velocity);
-
-       if (self.health <= 0)
-               W_PrepareExplosionByDamage(attacker, self.think);
-}
-
-void W_Hagar_Touch (void)
-{
-       PROJECTILE_TOUCH;
-       self.use ();
-}
-
-void W_Hagar_Touch2 (void)
-{
-       PROJECTILE_TOUCH;
-
-       if(self.cnt > 0 || other.takedamage == DAMAGE_AIM) {
-               self.use();
-       } else {
-               self.cnt++;
-               pointparticles(particleeffectnum("hagar_bounce"), self.origin, self.velocity, 1);
-               self.angles = vectoangles (self.velocity);
-               self.owner = world;
-               self.projectiledeathtype |= HITTYPE_BOUNCE;
-       }
-}
-
-void W_Hagar_Attack (void)
-{
-       entity missile;
-
-       W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_hagar_primary_ammo, autocvar_g_balance_hagar_reload_ammo);
-
-       W_SetupShot (self, FALSE, 2, "weapons/hagar_fire.wav", CH_WEAPON_A, autocvar_g_balance_hagar_primary_damage);
-
-       pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
-       missile = spawn ();
-       missile.owner = missile.realowner = self;
-       missile.classname = "missile";
-       missile.bot_dodge = TRUE;
-       missile.bot_dodgerating = autocvar_g_balance_hagar_primary_damage;
-
-       missile.takedamage = DAMAGE_YES;
-       missile.health = autocvar_g_balance_hagar_primary_health;
-       missile.damageforcescale = autocvar_g_balance_hagar_primary_damageforcescale;
-       missile.event_damage = W_Hagar_Damage;
-       missile.damagedbycontents = TRUE;
-
-       missile.touch = W_Hagar_Touch;
-       missile.use = W_Hagar_Explode;
-       missile.think = adaptor_think2use_hittype_splash;
-       missile.nextthink = time + autocvar_g_balance_hagar_primary_lifetime;
-       PROJECTILE_MAKETRIGGER(missile);
-       missile.projectiledeathtype = WEP_HAGAR;
-       setorigin (missile, w_shotorg);
-       setsize(missile, '0 0 0', '0 0 0');
-
-       missile.movetype = MOVETYPE_FLY;
-       W_SETUPPROJECTILEVELOCITY(missile, g_balance_hagar_primary);
-
-       missile.angles = vectoangles (missile.velocity);
-       missile.flags = FL_PROJECTILE;
-       missile.missile_flags = MIF_SPLASH;
-
-       CSQCProjectile(missile, TRUE, PROJECTILE_HAGAR, TRUE);
-
-       other = missile; MUTATOR_CALLHOOK(EditProjectile);
-}
-
-void W_Hagar_Attack2 (void)
-{
-       entity missile;
-
-       W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_hagar_secondary_ammo, autocvar_g_balance_hagar_reload_ammo);
-
-       W_SetupShot (self, FALSE, 2, "weapons/hagar_fire.wav", CH_WEAPON_A, autocvar_g_balance_hagar_secondary_damage);
-
-       pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
-       missile = spawn ();
-       missile.owner = missile.realowner = self;
-       missile.classname = "missile";
-       missile.bot_dodge = TRUE;
-       missile.bot_dodgerating = autocvar_g_balance_hagar_secondary_damage;
-
-       missile.takedamage = DAMAGE_YES;
-       missile.health = autocvar_g_balance_hagar_secondary_health;
-       missile.damageforcescale = autocvar_g_balance_hagar_secondary_damageforcescale;
-       missile.event_damage = W_Hagar_Damage;
-       missile.damagedbycontents = TRUE;
-
-       missile.touch = W_Hagar_Touch2;
-       missile.cnt = 0;
-       missile.use = W_Hagar_Explode2;
-       missile.think = adaptor_think2use_hittype_splash;
-       missile.nextthink = time + autocvar_g_balance_hagar_secondary_lifetime_min + random() * autocvar_g_balance_hagar_secondary_lifetime_rand;
-       PROJECTILE_MAKETRIGGER(missile);
-       missile.projectiledeathtype = WEP_HAGAR | HITTYPE_SECONDARY;
-       setorigin (missile, w_shotorg);
-       setsize(missile, '0 0 0', '0 0 0');
-
-       missile.movetype = MOVETYPE_BOUNCEMISSILE;
-       W_SETUPPROJECTILEVELOCITY(missile, g_balance_hagar_secondary);
-
-       missile.angles = vectoangles (missile.velocity);
-       missile.flags = FL_PROJECTILE;
-       missile.missile_flags = MIF_SPLASH;
-
-       CSQCProjectile(missile, TRUE, PROJECTILE_HAGAR_BOUNCING, TRUE);
-
-       other = missile; MUTATOR_CALLHOOK(EditProjectile);
-}
-
-.float hagar_loadstep, hagar_loadblock, hagar_loadbeep, hagar_warning;
-void W_Hagar_Attack2_Load_Release (void)
-{
-       // time to release the rockets we've loaded
-
-       entity missile;
-       float counter, shots, spread_pershot;
-       vector s;
-       vector forward, right, up;
-
-       if(!self.hagar_load)
-               return;
-
-       weapon_prepareattack_do(1, autocvar_g_balance_hagar_secondary_refire);
-
-       W_SetupShot (self, FALSE, 2, "weapons/hagar_fire.wav", CH_WEAPON_A, autocvar_g_balance_hagar_secondary_damage);
-       pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
-       forward = v_forward;
-       right = v_right;
-       up = v_up;
-
-       shots = self.hagar_load;
-       missile = world;
-       for(counter = 0; counter < shots; ++counter)
-       {
-               missile = spawn ();
-               missile.owner = missile.realowner = self;
-               missile.classname = "missile";
-               missile.bot_dodge = TRUE;
-               missile.bot_dodgerating = autocvar_g_balance_hagar_secondary_damage;
-
-               missile.takedamage = DAMAGE_YES;
-               missile.health = autocvar_g_balance_hagar_secondary_health;
-               missile.damageforcescale = autocvar_g_balance_hagar_secondary_damageforcescale;
-               missile.event_damage = W_Hagar_Damage;
-               missile.damagedbycontents = TRUE;
-
-               missile.touch = W_Hagar_Touch; // not bouncy
-               missile.use = W_Hagar_Explode2;
-               missile.think = adaptor_think2use_hittype_splash;
-               missile.nextthink = time + autocvar_g_balance_hagar_secondary_lifetime_min + random() * autocvar_g_balance_hagar_secondary_lifetime_rand;
-               PROJECTILE_MAKETRIGGER(missile);
-               missile.projectiledeathtype = WEP_HAGAR | HITTYPE_SECONDARY;
-               setorigin (missile, w_shotorg);
-               setsize(missile, '0 0 0', '0 0 0');
-               missile.movetype = MOVETYPE_FLY;
-               missile.missile_flags = MIF_SPLASH;
-
-               // per-shot spread calculation: the more shots there are, the less spread is applied (based on the bias cvar)
-               spread_pershot = ((shots - 1) / (autocvar_g_balance_hagar_secondary_load_max - 1));
-               spread_pershot = (1 - (spread_pershot * autocvar_g_balance_hagar_secondary_load_spread_bias));
-               spread_pershot = (autocvar_g_balance_hagar_secondary_spread * spread_pershot * g_weaponspreadfactor);
-
-               // pattern spread calculation
-               s = '0 0 0';
-               if (counter == 0)
-                       s = '0 0 0';
-               else
-               {
-                       makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1)));
-                       s_y = v_forward_x;
-                       s_z = v_forward_y;
-               }
-               s = s * autocvar_g_balance_hagar_secondary_load_spread * g_weaponspreadfactor;
-
-               W_SetupProjectileVelocityEx(missile, w_shotdir + right * s_y + up * s_z, v_up, autocvar_g_balance_hagar_secondary_speed, 0, 0, spread_pershot, FALSE);
-
-               missile.angles = vectoangles (missile.velocity);
-               missile.flags = FL_PROJECTILE;
-
-               CSQCProjectile(missile, TRUE, PROJECTILE_HAGAR, TRUE);
-
-               other = missile; MUTATOR_CALLHOOK(EditProjectile);
-       }
-
-       weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_hagar_secondary_load_animtime, w_ready);
-       self.hagar_loadstep = time + autocvar_g_balance_hagar_secondary_refire * W_WeaponRateFactor();
-       self.hagar_load = 0;
-}
-
-void W_Hagar_Attack2_Load (void)
-{
-       // loadable hagar secondary attack, must always run each frame
-
-       if(time < game_starttime)
-               return;
-
-       float loaded, enough_ammo;
-       loaded = self.hagar_load >= autocvar_g_balance_hagar_secondary_load_max;
-
-       // this is different than WR_CHECKAMMO when it comes to reloading
-       if(autocvar_g_balance_hagar_reload_ammo)
-               enough_ammo = self.(weapon_load[WEP_HAGAR]) >= autocvar_g_balance_hagar_secondary_ammo;
-       else
-               enough_ammo = self.ammo_rockets >= autocvar_g_balance_hagar_secondary_ammo;
-
-       if(self.BUTTON_ATCK2)
-       {
-               if(self.BUTTON_ATCK && autocvar_g_balance_hagar_secondary_load_abort)
-               {
-                       if(self.hagar_load)
-                       {
-                               // if we pressed primary fire while loading, unload all rockets and abort
-                               self.weaponentity.state = WS_READY;
-                               W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_hagar_secondary_ammo * self.hagar_load * -1, autocvar_g_balance_hagar_reload_ammo); // give back ammo
-                               self.hagar_load = 0;
-                               sound(self, CH_WEAPON_A, "weapons/hagar_beep.wav", VOL_BASE, ATTEN_NORM);
-
-                               // pause until we can load rockets again, once we re-press the alt fire button
-                               self.hagar_loadstep = time + autocvar_g_balance_hagar_secondary_load_speed * W_WeaponRateFactor();
-
-                               // require letting go of the alt fire button before we can load again
-                               self.hagar_loadblock = TRUE;
-                       }
-               }
-               else
-               {
-                       // check if we can attempt to load another rocket
-                       if(!loaded && enough_ammo)
-                       {
-                               if(!self.hagar_loadblock && self.hagar_loadstep < time)
-                               {
-                                       W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_hagar_secondary_ammo, autocvar_g_balance_hagar_reload_ammo);
-                                       self.weaponentity.state = WS_INUSE;
-                                       self.hagar_load += 1;
-                                       sound(self, CH_WEAPON_B, "weapons/hagar_load.wav", VOL_BASE * 0.8, ATTEN_NORM); // sound is too loud according to most
-
-                                       if (self.hagar_load >= autocvar_g_balance_hagar_secondary_load_max)
-                                               self.hagar_loadstep = time + autocvar_g_balance_hagar_secondary_load_hold * W_WeaponRateFactor();
-                                       else
-                                               self.hagar_loadstep = time + autocvar_g_balance_hagar_secondary_load_speed * W_WeaponRateFactor();
-                               }
-                       }
-                       else if(!self.hagar_loadbeep && self.hagar_load) // prevents the beep from playing each frame
-                       {
-                               // if this is the last rocket we can load, play a beep sound to notify the player
-                               sound(self, CH_WEAPON_A, "weapons/hagar_beep.wav", VOL_BASE, ATTEN_NORM);
-                               self.hagar_loadbeep = TRUE;
-                       }
-               }
-       }
-       else if(self.hagar_loadblock)
-       {
-               // the alt fire button has been released, so re-enable loading if blocked
-               self.hagar_loadblock = FALSE;
-       }
-
-       if(self.hagar_load)
-       {
-               // play warning sound if we're about to release
-               if((loaded || !enough_ammo) && self.hagar_loadstep - 0.5 < time && autocvar_g_balance_hagar_secondary_load_hold >= 0)
-               {
-                       if(!self.hagar_warning && self.hagar_load) // prevents the beep from playing each frame
-                       {
-                               // we're about to automatically release after holding time, play a beep sound to notify the player
-                               sound(self, CH_WEAPON_A, "weapons/hagar_beep.wav", VOL_BASE, ATTEN_NORM);
-                               self.hagar_warning = TRUE;
-                       }
-               }
-
-               // release if player let go of button or if they've held it in too long
-               if(!self.BUTTON_ATCK2 || ((loaded || !enough_ammo) && self.hagar_loadstep < time && autocvar_g_balance_hagar_secondary_load_hold >= 0))
-               {
-                       self.weaponentity.state = WS_READY;
-                       W_Hagar_Attack2_Load_Release();
-               }
-       }
-       else
-       {
-               self.hagar_loadbeep = FALSE;
-               self.hagar_warning = FALSE;
-       }
-
-       // we aren't checking ammo during an attack, so we must do it here
-       if (!(weapon_action(self.weapon, WR_CHECKAMMO1) + weapon_action(self.weapon, WR_CHECKAMMO2)))
-       {
-               // note: this doesn't force the switch
-               W_SwitchToOtherWeapon(self);
-               return;
-       }
-}
-
-void spawnfunc_weapon_hagar (void)
-{
-       weapon_defaultspawnfunc(WEP_HAGAR);
-}
-
-float w_hagar(float req)
-{
-       float ammo_amount;
-       if (req == WR_AIM)
-               if (random()>0.15)
-                       self.BUTTON_ATCK = bot_aim(autocvar_g_balance_hagar_primary_speed, 0, autocvar_g_balance_hagar_primary_lifetime, FALSE);
-               else
-               {
-                       // not using secondary_speed since these are only 15% and should cause some ricochets without re-aiming
-                       self.BUTTON_ATCK2 = bot_aim(autocvar_g_balance_hagar_primary_speed, 0, autocvar_g_balance_hagar_primary_lifetime, FALSE);
-               }
-       else if (req == WR_THINK)
-       {
-               float loadable_secondary;
-               loadable_secondary = (autocvar_g_balance_hagar_secondary_load && autocvar_g_balance_hagar_secondary);
-
-               if (loadable_secondary)
-                       W_Hagar_Attack2_Load(); // must always run each frame
-               if(autocvar_g_balance_hagar_reload_ammo && self.clip_load < min(autocvar_g_balance_hagar_primary_ammo, autocvar_g_balance_hagar_secondary_ammo)) // forced reload
-                       weapon_action(self.weapon, WR_RELOAD);
-               else if (self.BUTTON_ATCK && !self.hagar_load && !self.hagar_loadblock) // not while secondary is loaded or awaiting reset
-               {
-                       if (weapon_prepareattack(0, autocvar_g_balance_hagar_primary_refire))
-                       {
-                               W_Hagar_Attack();
-                               weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_hagar_primary_refire, w_ready);
-                       }
-               }
-               else if (self.BUTTON_ATCK2 && !loadable_secondary && autocvar_g_balance_hagar_secondary)
-               {
-                       if (weapon_prepareattack(1, autocvar_g_balance_hagar_secondary_refire))
-                       {
-                               W_Hagar_Attack2();
-                               weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_hagar_secondary_refire, w_ready);
-                       }
-               }
-       }
-       else if (req == WR_GONETHINK)
-       {
-               // we lost the weapon and want to prepare switching away
-               if(self.hagar_load)
-               {
-                       self.weaponentity.state = WS_READY;
-                       W_Hagar_Attack2_Load_Release();
-               }
-       }
-       else if (req == WR_PRECACHE)
-       {
-               precache_model ("models/weapons/g_hagar.md3");
-               precache_model ("models/weapons/v_hagar.md3");
-               precache_model ("models/weapons/h_hagar.iqm");
-               precache_sound ("weapons/hagar_fire.wav");
-               precache_sound ("weapons/hagar_load.wav");
-               precache_sound ("weapons/hagar_beep.wav");
-               //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else
-       }
-       else if (req == WR_SETUP)
-       {
-               weapon_setup(WEP_HAGAR);
-               self.current_ammo = ammo_rockets;
-               self.hagar_loadblock = FALSE;
-
-               if(self.hagar_load)
-               {
-                       W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_hagar_secondary_ammo * self.hagar_load * -1, autocvar_g_balance_hagar_reload_ammo); // give back ammo if necessary
-                       self.hagar_load = 0;
-               }
-       }
-       else if (req == WR_CHECKAMMO1)
-       {
-               ammo_amount = self.ammo_rockets >= autocvar_g_balance_hagar_primary_ammo;
-               ammo_amount += self.(weapon_load[WEP_HAGAR]) >= autocvar_g_balance_hagar_primary_ammo;
-               return ammo_amount;
-       }
-       else if (req == WR_CHECKAMMO2)
-       {
-               ammo_amount = self.ammo_rockets >= autocvar_g_balance_hagar_secondary_ammo;
-               ammo_amount += self.(weapon_load[WEP_HAGAR]) >= autocvar_g_balance_hagar_secondary_ammo;
-               return ammo_amount;
-       }
-       else if (req == WR_RESETPLAYER)
-       {
-               self.hagar_load = 0;
-       }
-       else if (req == WR_PLAYERDEATH)
-       {
-               // if we have any rockets loaded when we die, release them
-               if(self.hagar_load && autocvar_g_balance_hagar_secondary_load_releasedeath)
-                       W_Hagar_Attack2_Load_Release();
-       }
-       else if (req == WR_RELOAD)
-       {
-               if (!self.hagar_load) // require releasing loaded rockets first
-                       W_Reload(min(autocvar_g_balance_hagar_primary_ammo, autocvar_g_balance_hagar_secondary_ammo), autocvar_g_balance_hagar_reload_ammo, autocvar_g_balance_hagar_reload_time, "weapons/reload.wav");
-       }
-       else if (req == WR_SUICIDEMESSAGE)
-       {
-               return WEAPON_HAGAR_SUICIDE;
-       }
-       else if (req == WR_KILLMESSAGE)
-       {
-               if(w_deathtype & HITTYPE_SECONDARY)
-                       return WEAPON_HAGAR_MURDER_BURST;
-               else
-                       return WEAPON_HAGAR_MURDER_SPRAY;
-       }
-       return TRUE;
-}
-#endif
-#ifdef CSQC
-float w_hagar(float req)
-{
-       if(req == WR_IMPACTEFFECT)
-       {
-               vector org2;
-               org2 = w_org + w_backoff * 6;
-               pointparticles(particleeffectnum("hagar_explode"), org2, '0 0 0', 1);
-               if(!w_issilent)
-               {
-                       if (w_random<0.15)
-                               sound(self, CH_SHOTS, "weapons/hagexp1.wav", VOL_BASE, ATTEN_NORM);
-                       else if (w_random<0.7)
-                               sound(self, CH_SHOTS, "weapons/hagexp2.wav", VOL_BASE, ATTEN_NORM);
-                       else
-                               sound(self, CH_SHOTS, "weapons/hagexp3.wav", VOL_BASE, ATTEN_NORM);
-               }
-       }
-       else if(req == WR_PRECACHE)
-       {
-               precache_sound("weapons/hagexp1.wav");
-               precache_sound("weapons/hagexp2.wav");
-               precache_sound("weapons/hagexp3.wav");
-       }
-       return TRUE;
-}
-#endif
-#endif
diff --git a/qcsrc/server/w_hlac.qc b/qcsrc/server/w_hlac.qc
deleted file mode 100644 (file)
index 5642ef0..0000000
+++ /dev/null
@@ -1,261 +0,0 @@
-#ifdef REGISTER_WEAPON
-REGISTER_WEAPON(
-/* WEP_##id  */ HLAC,
-/* function  */ w_hlac,
-/* ammotype  */ IT_CELLS,
-/* impulse   */ 6,
-/* flags     */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH,
-/* rating    */ BOT_PICKUP_RATING_MID,
-/* model     */ "hlac",
-/* shortname */ "hlac",
-/* fullname  */ _("Heavy Laser Assault Cannon")
-);
-#else
-#ifdef SVQC
-
-void W_HLAC_Touch (void)
-{
-       PROJECTILE_TOUCH;
-
-       self.event_damage = func_null;
-
-       if(self.projectiledeathtype & HITTYPE_SECONDARY)
-               RadiusDamage (self, self.realowner, autocvar_g_balance_hlac_secondary_damage, autocvar_g_balance_hlac_secondary_edgedamage, autocvar_g_balance_hlac_secondary_radius, world, autocvar_g_balance_hlac_secondary_force, self.projectiledeathtype, other);
-       else
-               RadiusDamage (self, self.realowner, autocvar_g_balance_hlac_primary_damage, autocvar_g_balance_hlac_primary_edgedamage, autocvar_g_balance_hlac_primary_radius, world, autocvar_g_balance_hlac_primary_force, self.projectiledeathtype, other);
-
-       remove (self);
-}
-
-void W_HLAC_Attack (void)
-{
-       entity missile;
-    float spread;
-
-       W_DecreaseAmmo(ammo_cells, autocvar_g_balance_hlac_primary_ammo, autocvar_g_balance_hlac_reload_ammo);
-
-    spread = autocvar_g_balance_hlac_primary_spread_min + (autocvar_g_balance_hlac_primary_spread_add * self.misc_bulletcounter);
-    spread = min(spread,autocvar_g_balance_hlac_primary_spread_max);
-    if(self.crouch)
-        spread = spread * autocvar_g_balance_hlac_primary_spread_crouchmod;
-
-       W_SetupShot (self, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_A, autocvar_g_balance_hlac_primary_damage);
-       pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-       if (!autocvar_g_norecoil)
-       {
-               self.punchangle_x = random () - 0.5;
-               self.punchangle_y = random () - 0.5;
-       }
-
-       missile = spawn ();
-       missile.owner = missile.realowner = self;
-       missile.classname = "hlacbolt";
-       missile.bot_dodge = TRUE;
-
-    missile.bot_dodgerating = autocvar_g_balance_hlac_primary_damage;
-
-       missile.movetype = MOVETYPE_FLY;
-       PROJECTILE_MAKETRIGGER(missile);
-
-       setorigin (missile, w_shotorg);
-       setsize(missile, '0 0 0', '0 0 0');
-
-       W_SetupProjectileVelocity(missile, autocvar_g_balance_hlac_primary_speed, spread);
-       //missile.angles = vectoangles (missile.velocity); // csqc
-
-       missile.touch = W_HLAC_Touch;
-       missile.think = SUB_Remove;
-
-    missile.nextthink = time + autocvar_g_balance_hlac_primary_lifetime;
-
-       missile.flags = FL_PROJECTILE;
-       missile.projectiledeathtype = WEP_HLAC;
-
-       CSQCProjectile(missile, TRUE, PROJECTILE_HLAC, TRUE);
-
-       other = missile; MUTATOR_CALLHOOK(EditProjectile);
-}
-
-void W_HLAC_Attack2f (void)
-{
-       entity missile;
-    float spread;
-
-    spread = autocvar_g_balance_hlac_secondary_spread;
-
-
-    if(self.crouch)
-        spread = spread * autocvar_g_balance_hlac_secondary_spread_crouchmod;
-
-       W_SetupShot (self, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_A, autocvar_g_balance_hlac_secondary_damage);
-       pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
-       missile = spawn ();
-       missile.owner = missile.realowner = self;
-       missile.classname = "hlacbolt";
-       missile.bot_dodge = TRUE;
-
-    missile.bot_dodgerating = autocvar_g_balance_hlac_secondary_damage;
-
-       missile.movetype = MOVETYPE_FLY;
-       PROJECTILE_MAKETRIGGER(missile);
-
-       setorigin (missile, w_shotorg);
-       setsize(missile, '0 0 0', '0 0 0');
-
-       W_SetupProjectileVelocity(missile, autocvar_g_balance_hlac_secondary_speed, spread);
-       //missile.angles = vectoangles (missile.velocity); // csqc
-
-       missile.touch = W_HLAC_Touch;
-       missile.think = SUB_Remove;
-
-    missile.nextthink = time + autocvar_g_balance_hlac_secondary_lifetime;
-
-       missile.flags = FL_PROJECTILE;
-       missile.missile_flags = MIF_SPLASH;
-       missile.projectiledeathtype = WEP_HLAC | HITTYPE_SECONDARY;
-
-       CSQCProjectile(missile, TRUE, PROJECTILE_HLAC, TRUE);
-
-       other = missile; MUTATOR_CALLHOOK(EditProjectile);
-}
-
-void W_HLAC_Attack2 (void)
-{
-    float i;
-
-       W_DecreaseAmmo(ammo_cells, autocvar_g_balance_hlac_secondary_ammo, autocvar_g_balance_hlac_reload_ammo);
-
-    for(i=autocvar_g_balance_hlac_secondary_shots;i>0;--i)
-        W_HLAC_Attack2f();
-
-       if (!autocvar_g_norecoil)
-       {
-               self.punchangle_x = random () - 0.5;
-               self.punchangle_y = random () - 0.5;
-       }
-}
-
-// weapon frames
-void HLAC_fire1_02()
-{
-       if(self.weapon != self.switchweapon) // abort immediately if switching
-       {
-               w_ready();
-               return;
-       }
-
-       if (self.BUTTON_ATCK)
-       {
-               if (!weapon_action(self.weapon, WR_CHECKAMMO1))
-               if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
-               {
-                       W_SwitchWeapon_Force(self, w_getbestweapon(self));
-                       w_ready();
-                       return;
-               }
-
-               ATTACK_FINISHED(self) = time + autocvar_g_balance_hlac_primary_refire * W_WeaponRateFactor();
-               W_HLAC_Attack();
-               self.misc_bulletcounter = self.misc_bulletcounter + 1;
-        weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_hlac_primary_refire, HLAC_fire1_02);
-       }
-       else
-       {
-               weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_hlac_primary_animtime, w_ready);
-       }
-}
-
-void spawnfunc_weapon_hlac (void)
-{
-       weapon_defaultspawnfunc(WEP_HLAC);
-}
-
-float w_hlac(float req)
-{
-       float ammo_amount;
-       if (req == WR_AIM)
-        self.BUTTON_ATCK = bot_aim(autocvar_g_balance_hlac_primary_speed, 0, autocvar_g_balance_hlac_primary_lifetime, FALSE);
-       else if (req == WR_THINK)
-       {
-               if(autocvar_g_balance_hlac_reload_ammo && self.clip_load < min(autocvar_g_balance_hlac_primary_ammo, autocvar_g_balance_hlac_secondary_ammo)) // forced reload
-                       weapon_action(self.weapon, WR_RELOAD);
-               else if (self.BUTTON_ATCK)
-               {
-                       if (weapon_prepareattack(0, autocvar_g_balance_hlac_primary_refire))
-                       {
-                               self.misc_bulletcounter = 0;
-                               W_HLAC_Attack();
-                               weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_hlac_primary_refire, HLAC_fire1_02);
-                       }
-               }
-
-               else if (self.BUTTON_ATCK2 && autocvar_g_balance_hlac_secondary)
-               {
-                       if (weapon_prepareattack(1, autocvar_g_balance_hlac_secondary_refire))
-                       {
-                               W_HLAC_Attack2();
-                               weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_hlac_secondary_animtime, w_ready);
-                       }
-               }
-       }
-       else if (req == WR_PRECACHE)
-       {
-        precache_model ("models/weapons/g_hlac.md3");
-               precache_model ("models/weapons/v_hlac.md3");
-               precache_model ("models/weapons/h_hlac.iqm");
-               precache_sound ("weapons/lasergun_fire.wav");
-               //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else
-
-       }
-       else if (req == WR_SETUP)
-       {
-               weapon_setup(WEP_HLAC);
-               self.current_ammo = ammo_cells;
-       }
-       else if (req == WR_CHECKAMMO1)
-       {
-               ammo_amount = self.ammo_cells >= autocvar_g_balance_hlac_primary_ammo;
-               ammo_amount += self.(weapon_load[WEP_HLAC]) >= autocvar_g_balance_hlac_primary_ammo;
-               return ammo_amount;
-       }
-       else if (req == WR_CHECKAMMO2)
-       {
-               ammo_amount = self.ammo_cells >= autocvar_g_balance_hlac_secondary_ammo;
-               ammo_amount += self.(weapon_load[WEP_HLAC]) >= autocvar_g_balance_hlac_secondary_ammo;
-               return ammo_amount;
-       }
-       else if (req == WR_RELOAD)
-       {
-               W_Reload(min(autocvar_g_balance_hlac_primary_ammo, autocvar_g_balance_hlac_secondary_ammo), autocvar_g_balance_hlac_reload_ammo, autocvar_g_balance_hlac_reload_time, "weapons/reload.wav");
-       }
-       else if (req == WR_SUICIDEMESSAGE)
-       {
-               return WEAPON_HLAC_SUICIDE;
-       }
-       else if (req == WR_KILLMESSAGE)
-       {
-               return WEAPON_HLAC_MURDER;
-       }
-       return TRUE;
-}
-#endif
-#ifdef CSQC
-float w_hlac(float req)
-{
-       if(req == WR_IMPACTEFFECT)
-       {
-               vector org2;
-               org2 = w_org + w_backoff * 6;
-               pointparticles(particleeffectnum("laser_impact"), org2, w_backoff * 1000, 1);
-               if(!w_issilent)
-                       sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTEN_NORM);
-       }
-       else if(req == WR_PRECACHE)
-       {
-               precache_sound("weapons/laserimpact.wav");
-       }
-       return TRUE;
-}
-#endif
-#endif
diff --git a/qcsrc/server/w_hook.qc b/qcsrc/server/w_hook.qc
deleted file mode 100644 (file)
index f0310c7..0000000
+++ /dev/null
@@ -1,307 +0,0 @@
-#ifdef REGISTER_WEAPON
-REGISTER_WEAPON(
-/* WEP_##id  */ HOOK,
-/* function  */ w_hook,
-/* ammotype  */ IT_CELLS|IT_FUEL,
-/* impulse   */ 0,
-/* flags     */ WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
-/* rating    */ 0,
-/* model     */ "hookgun",
-/* shortname */ "hook",
-/* fullname  */ _("Grappling Hook")
-);
-#else
-#ifdef SVQC
-.float dmg;
-.float dmg_edge;
-.float dmg_radius;
-.float dmg_force;
-.float dmg_power;
-.float dmg_duration;
-.float dmg_last;
-.float hook_refire;
-.float hook_time_hooked;
-.float hook_time_fueldecrease;
-
-void W_Hook_ExplodeThink (void)
-{
-       float dt, dmg_remaining_next, f;
-
-       dt = time - self.teleport_time;
-       dmg_remaining_next = pow(bound(0, 1 - dt / self.dmg_duration, 1), self.dmg_power);
-
-       f = self.dmg_last - dmg_remaining_next;
-       self.dmg_last = dmg_remaining_next;
-
-       RadiusDamage (self, self.realowner, self.dmg * f, self.dmg_edge * f, self.dmg_radius, self.realowner, self.dmg_force * f, self.projectiledeathtype, world);
-       self.projectiledeathtype |= HITTYPE_BOUNCE;
-       //RadiusDamage (self, world, self.dmg * f, self.dmg_edge * f, self.dmg_radius, world, self.dmg_force * f, self.projectiledeathtype, world);
-
-       if(dt < self.dmg_duration)
-               self.nextthink = time + 0.05; // soon
-       else
-               remove(self);
-}
-
-void W_Hook_Explode2 (void)
-{
-       self.event_damage = func_null;
-       self.touch = func_null;
-       self.effects |= EF_NODRAW;
-
-       self.think = W_Hook_ExplodeThink;
-       self.nextthink = time;
-       self.dmg = autocvar_g_balance_hook_secondary_damage;
-       self.dmg_edge = autocvar_g_balance_hook_secondary_edgedamage;
-       self.dmg_radius = autocvar_g_balance_hook_secondary_radius;
-       self.dmg_force = autocvar_g_balance_hook_secondary_force;
-       self.dmg_power = autocvar_g_balance_hook_secondary_power;
-       self.dmg_duration = autocvar_g_balance_hook_secondary_duration;
-       self.teleport_time = time;
-       self.dmg_last = 1;
-       self.movetype = MOVETYPE_NONE;
-}
-
-void W_Hook_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-{
-       if (self.health <= 0)
-               return;
-
-       if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
-               return; // g_projectiles_damage says to halt
-
-       self.health = self.health - damage;
-
-       if (self.health <= 0)
-               W_PrepareExplosionByDamage(self.realowner, W_Hook_Explode2);
-}
-
-void W_Hook_Touch2 (void)
-{
-       PROJECTILE_TOUCH;
-       self.use();
-}
-
-void W_Hook_Attack2()
-{
-       entity gren;
-
-       W_DecreaseAmmo(ammo_cells, autocvar_g_balance_hook_secondary_ammo, FALSE);
-       W_SetupShot (self, FALSE, 4, "weapons/hookbomb_fire.wav", CH_WEAPON_A, autocvar_g_balance_hook_secondary_damage);
-
-       gren = spawn ();
-       gren.owner = gren.realowner = self;
-       gren.classname = "hookbomb";
-       gren.bot_dodge = TRUE;
-       gren.bot_dodgerating = autocvar_g_balance_hook_secondary_damage;
-       gren.movetype = MOVETYPE_TOSS;
-       PROJECTILE_MAKETRIGGER(gren);
-       gren.projectiledeathtype = WEP_HOOK | HITTYPE_SECONDARY;
-       setorigin(gren, w_shotorg);
-       setsize(gren, '0 0 0', '0 0 0');
-
-       gren.nextthink = time + autocvar_g_balance_hook_secondary_lifetime;
-       gren.think = adaptor_think2use_hittype_splash;
-       gren.use = W_Hook_Explode2;
-       gren.touch = W_Hook_Touch2;
-
-       gren.takedamage = DAMAGE_YES;
-       gren.health = autocvar_g_balance_hook_secondary_health;
-       gren.damageforcescale = autocvar_g_balance_hook_secondary_damageforcescale;
-       gren.event_damage = W_Hook_Damage;
-       gren.damagedbycontents = TRUE;
-       gren.missile_flags = MIF_SPLASH | MIF_ARC;
-
-       gren.velocity = '0 0 1' * autocvar_g_balance_hook_secondary_speed;
-       if(autocvar_g_projectiles_newton_style)
-               gren.velocity = gren.velocity + self.velocity;
-
-       gren.gravity = autocvar_g_balance_hook_secondary_gravity;
-       //W_SetupProjectileVelocity(gren); // just falling down!
-
-       gren.angles = '0 0 0';
-       gren.flags = FL_PROJECTILE;
-
-       CSQCProjectile(gren, TRUE, PROJECTILE_HOOKBOMB, TRUE);
-
-       other = gren; MUTATOR_CALLHOOK(EditProjectile);
-}
-
-void spawnfunc_weapon_hook (void)
-{
-       if(g_grappling_hook) // offhand hook
-       {
-               startitem_failed = TRUE;
-               remove(self);
-               return;
-       }
-       weapon_defaultspawnfunc(WEP_HOOK);
-}
-
-float w_hook(float req)
-{
-       float hooked_time_max, hooked_fuel;
-
-       if (req == WR_AIM)
-       {
-               // ... sorry ...
-       }
-       else if (req == WR_THINK)
-       {
-               if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
-               {
-                       if(!self.hook)
-                       if (!(self.hook_state & HOOK_WAITING_FOR_RELEASE))
-                       if (!(self.hook_state & HOOK_FIRING))
-                       if (time > self.hook_refire)
-                       if (weapon_prepareattack(0, -1))
-                       {
-                               W_DecreaseAmmo(ammo_fuel, autocvar_g_balance_hook_primary_fuel, FALSE);
-                               self.hook_state |= HOOK_FIRING;
-                               weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_hook_primary_animtime, w_ready);
-                       }
-               }
-
-               if (self.BUTTON_ATCK2)
-               {
-                       if (weapon_prepareattack(1, autocvar_g_balance_hook_secondary_refire))
-                       {
-                               W_Hook_Attack2();
-                               weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_hook_secondary_animtime, w_ready);
-                       }
-               }
-
-               if(self.hook)
-               {
-                       // if hooked, no bombs, and increase the timer
-                       self.hook_refire = max(self.hook_refire, time + autocvar_g_balance_hook_primary_refire * W_WeaponRateFactor());
-
-                       // hook also inhibits health regeneration, but only for 1 second
-                       if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
-                               self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen);
-               }
-
-               if(self.hook && self.hook.state == 1)
-               {
-                       hooked_time_max = autocvar_g_balance_hook_primary_hooked_time_max;
-                       if (hooked_time_max > 0)
-                       {
-                               if ( time > self.hook_time_hooked + hooked_time_max )
-                                       self.hook_state |= HOOK_REMOVING;
-                       }
-
-                       hooked_fuel = autocvar_g_balance_hook_primary_hooked_fuel;
-                       if (hooked_fuel > 0)
-                       {
-                               if ( time > self.hook_time_fueldecrease )
-                               {
-                                       if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
-                                       {
-                                               if ( self.ammo_fuel >= (time - self.hook_time_fueldecrease) * hooked_fuel )
-                                               {
-                                                       W_DecreaseAmmo(ammo_fuel, (time - self.hook_time_fueldecrease) * hooked_fuel, FALSE);
-                                                       self.hook_time_fueldecrease = time;
-                                                       // decrease next frame again
-                                               }
-                                               else
-                                               {
-                                                       self.ammo_fuel = 0;
-                                                       self.hook_state |= HOOK_REMOVING;
-                                                       W_SwitchWeapon_Force(self, w_getbestweapon(self));
-                                               }
-                                       }
-                               }
-                       }
-               }
-               else
-               {
-                       self.hook_time_hooked = time;
-                       self.hook_time_fueldecrease = time + autocvar_g_balance_hook_primary_hooked_time_free;
-               }
-
-               if (self.BUTTON_CROUCH)
-               {
-                       self.hook_state &= ~HOOK_PULLING;
-                       if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
-                               self.hook_state &= ~HOOK_RELEASING;
-                       else
-                               self.hook_state |= HOOK_RELEASING;
-               }
-               else
-               {
-                       self.hook_state |= HOOK_PULLING;
-                       self.hook_state &= ~HOOK_RELEASING;
-
-                       if (self.BUTTON_ATCK || (!(self.items & IT_JETPACK) && self.BUTTON_HOOK))
-                       {
-                               // already fired
-                               if(self.hook)
-                                       self.hook_state |= HOOK_WAITING_FOR_RELEASE;
-                       }
-                       else
-                       {
-                               self.hook_state |= HOOK_REMOVING;
-                               self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
-                       }
-               }
-       }
-       else if (req == WR_PRECACHE)
-       {
-               precache_model ("models/weapons/g_hookgun.md3");
-               precache_model ("models/weapons/v_hookgun.md3");
-               precache_model ("models/weapons/h_hookgun.iqm");
-               precache_sound ("weapons/hook_impact.wav"); // done by g_hook.qc
-               precache_sound ("weapons/hook_fire.wav");
-               precache_sound ("weapons/hookbomb_fire.wav");
-       }
-       else if (req == WR_SETUP)
-       {
-               weapon_setup(WEP_HOOK);
-               self.current_ammo = ammo_fuel;
-               self.hook_state &= ~HOOK_WAITING_FOR_RELEASE;
-       }
-       else if (req == WR_CHECKAMMO1)
-       {
-               if(self.hook)
-                       return self.ammo_fuel > 0;
-               else
-                       return self.ammo_fuel >= autocvar_g_balance_hook_primary_fuel;
-       }
-       else if (req == WR_CHECKAMMO2)
-       {
-               return self.ammo_cells >= autocvar_g_balance_hook_secondary_ammo;
-       }
-       else if (req == WR_RESETPLAYER)
-       {
-               self.hook_refire = time;
-       }
-       else if (req == WR_SUICIDEMESSAGE)
-       {
-               return FALSE;
-       }
-       else if (req == WR_KILLMESSAGE)
-       {
-               return WEAPON_HOOK_MURDER;
-       }
-       return TRUE;
-}
-#endif
-#ifdef CSQC
-float w_hook(float req)
-{
-       if(req == WR_IMPACTEFFECT)
-       {
-               vector org2;
-               org2 = w_org + w_backoff * 2;
-               pointparticles(particleeffectnum("hookbomb_explode"), org2, '0 0 0', 1);
-               if(!w_issilent)
-                       sound(self, CH_SHOTS, "weapons/hookbomb_impact.wav", VOL_BASE, ATTEN_NORM);
-       }
-       else if(req == WR_PRECACHE)
-       {
-               precache_sound("weapons/hookbomb_impact.wav");
-       }
-       return TRUE;
-}
-#endif
-#endif
diff --git a/qcsrc/server/w_laser.qc b/qcsrc/server/w_laser.qc
deleted file mode 100644 (file)
index 0a07046..0000000
+++ /dev/null
@@ -1,322 +0,0 @@
-#ifdef REGISTER_WEAPON
-REGISTER_WEAPON(
-/* WEP_##id  */ LASER,
-/* function  */ w_laser,
-/* ammotype  */ 0,
-/* impulse   */ 1,
-/* flags     */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH,
-/* rating    */ 0,
-/* model     */ "laser",
-/* shortname */ "laser",
-/* fullname  */ _("Laser")
-);
-#else
-#ifdef SVQC
-void(float imp) W_SwitchWeapon;
-void() W_LastWeapon;
-
-void W_Laser_Touch (void)
-{
-       PROJECTILE_TOUCH;
-
-       self.event_damage = func_null;
-       if (self.dmg)
-               RadiusDamage (self, self.realowner, autocvar_g_balance_laser_secondary_damage, autocvar_g_balance_laser_secondary_edgedamage, autocvar_g_balance_laser_secondary_radius, world, autocvar_g_balance_laser_secondary_force, self.projectiledeathtype, other);
-       else
-               RadiusDamage (self, self.realowner, autocvar_g_balance_laser_primary_damage, autocvar_g_balance_laser_primary_edgedamage, autocvar_g_balance_laser_primary_radius, world, autocvar_g_balance_laser_primary_force, self.projectiledeathtype, other);
-
-       remove (self);
-}
-
-void W_Laser_Think()
-{
-       self.movetype = MOVETYPE_FLY;
-       self.think = SUB_Remove;
-       if (self.dmg)
-               self.nextthink = time + autocvar_g_balance_laser_secondary_lifetime;
-       else
-               self.nextthink = time + autocvar_g_balance_laser_primary_lifetime;
-       CSQCProjectile(self, TRUE, PROJECTILE_LASER, TRUE);
-}
-
-void W_Laser_Attack (float issecondary)
-{
-       entity missile;
-       vector s_forward;
-       float a;
-
-       a = autocvar_g_balance_laser_primary_shotangle;
-       s_forward = v_forward * cos(a * DEG2RAD) + v_up * sin(a * DEG2RAD);
-
-       if(issecondary == 1)
-               W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_secondary_damage);
-       else
-               W_SetupShot_Dir (self, s_forward, FALSE, 3, "weapons/lasergun_fire.wav", CH_WEAPON_B, autocvar_g_balance_laser_primary_damage);
-       pointparticles(particleeffectnum("laser_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
-       missile = spawn ();
-       missile.owner = missile.realowner = self;
-       missile.classname = "laserbolt";
-       missile.dmg = 0;
-       missile.bot_dodge = TRUE;
-       missile.bot_dodgerating = autocvar_g_balance_laser_primary_damage;
-
-       PROJECTILE_MAKETRIGGER(missile);
-       missile.projectiledeathtype = WEP_LASER;
-
-       setorigin (missile, w_shotorg);
-       setsize(missile, '0 0 0', '0 0 0');
-
-       W_SETUPPROJECTILEVELOCITY(missile, g_balance_laser_primary);
-       missile.angles = vectoangles (missile.velocity);
-       //missile.glow_color = 250; // 244, 250
-       //missile.glow_size = 120;
-       missile.touch = W_Laser_Touch;
-
-       missile.flags = FL_PROJECTILE;
-       missile.missile_flags = MIF_SPLASH;
-
-       missile.think = W_Laser_Think;
-       missile.nextthink = time + autocvar_g_balance_laser_primary_delay;
-
-       other = missile; MUTATOR_CALLHOOK(EditProjectile);
-
-       if(time >= missile.nextthink)
-       {
-               entity oldself;
-               oldself = self;
-               self = missile;
-               self.think();
-               self = oldself;
-       }
-}
-
-.vector hook_start, hook_end;
-float gauntletbeam_send(entity to, float sf)
-{
-       WriteByte(MSG_ENTITY, ENT_CLIENT_GAUNTLET);
-       sf = sf & 0x7F;
-       if(sound_allowed(MSG_BROADCAST, self.realowner))
-               sf |= 0x80;
-       WriteByte(MSG_ENTITY, sf);
-       if(sf & 1)
-       {
-               WriteByte(MSG_ENTITY, num_for_edict(self.realowner));
-       }
-       if(sf & 2)
-       {
-               WriteCoord(MSG_ENTITY, self.hook_start_x);
-               WriteCoord(MSG_ENTITY, self.hook_start_y);
-               WriteCoord(MSG_ENTITY, self.hook_start_z);
-       }
-       if(sf & 4)
-       {
-               WriteCoord(MSG_ENTITY, self.hook_end_x);
-               WriteCoord(MSG_ENTITY, self.hook_end_y);
-               WriteCoord(MSG_ENTITY, self.hook_end_z);
-       }
-       return TRUE;
-}
-.entity gauntletbeam;
-.float prevgauntletfire;
-entity lgbeam_owner_ent;
-void gauntletbeam_think()
-{
-       float damage, myforce, myradius;
-       damage = autocvar_g_balance_laser_secondary_damage;
-       myforce = autocvar_g_balance_laser_secondary_force;
-       myradius = autocvar_g_balance_laser_secondary_radius;
-
-       self.realowner.prevgauntletfire = time;
-       if (self.realowner.weaponentity.state != WS_INUSE || self != self.realowner.gauntletbeam || self.realowner.deadflag != DEAD_NO || !self.realowner.BUTTON_ATCK2)
-       {
-               remove(self);
-               return;
-       }
-
-       self.nextthink = time;
-
-       makevectors(self.realowner.v_angle);
-
-       float dt;
-       dt = frametime;
-
-       W_SetupShot_Range(self.realowner, TRUE, 0, "", 0, damage * dt, myradius);
-       if(!lgbeam_owner_ent)
-       {
-               lgbeam_owner_ent = spawn();
-               lgbeam_owner_ent.classname = "lgbeam_owner_ent";
-       }
-       WarpZone_traceline_antilag(lgbeam_owner_ent, w_shotorg, w_shotend, MOVE_NORMAL, lgbeam_owner_ent, ANTILAG_LATENCY(self.owner));
-
-       // apply the damage
-       if(trace_ent)
-       {
-               vector force;
-               force = w_shotdir * myforce;
-               if(accuracy_isgooddamage(self.owner, trace_ent))
-                       accuracy_add(self.owner, WEP_LASER, 0, damage * dt);
-               Damage (trace_ent, self.owner, self.owner, damage * dt, WEP_LASER | HITTYPE_SECONDARY, trace_endpos, force * dt);
-       }
-
-       // draw effect
-       if(w_shotorg != self.hook_start)
-       {
-               self.SendFlags |= 2;
-               self.hook_start = w_shotorg;
-       }
-       if(w_shotend != self.hook_end)
-       {
-               self.SendFlags |= 4;
-               self.hook_end = w_shotend;
-       }
-}
-
-// experimental gauntlet
-void W_Laser_Attack2 ()
-{
-       // only play fire sound if 0.5 sec has passed since player let go the fire button
-       if(time - self.prevgauntletfire > 0.5)
-       {
-               sound (self, CH_WEAPON_A, "weapons/gauntlet_fire.wav", VOL_BASE, ATTEN_NORM);
-       }
-
-       entity beam, oldself;
-
-       self.gauntletbeam = beam = spawn();
-       beam.solid = SOLID_NOT;
-       beam.think = gauntletbeam_think;
-       beam.owner = self;
-       beam.movetype = MOVETYPE_NONE;
-       beam.shot_spread = 0;
-       beam.bot_dodge = TRUE;
-       beam.bot_dodgerating = autocvar_g_balance_laser_primary_damage;
-       Net_LinkEntity(beam, FALSE, 0, gauntletbeam_send);
-
-       oldself = self;
-       self = beam;
-       self.think();
-       self = oldself;
-}
-
-void LaserInit()
-{
-       weapon_action(WEP_LASER, WR_PRECACHE);
-       gauntlet_shotorigin[0] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 1);
-       gauntlet_shotorigin[1] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 2);
-       gauntlet_shotorigin[2] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 3);
-       gauntlet_shotorigin[3] = shotorg_adjust_values(CL_Weapon_GetShotOrg(WEP_LASER), FALSE, FALSE, 4);
-}
-
-void spawnfunc_weapon_laser (void)
-{
-       weapon_defaultspawnfunc(WEP_LASER);
-}
-
-float w_laser(float req)
-{
-       float r1;
-       float r2;
-       if (req == WR_AIM)
-       {
-               if(autocvar_g_balance_laser_secondary)
-               {
-                       r1 = autocvar_g_balance_laser_primary_damage;
-                       r2 = autocvar_g_balance_laser_secondary_damage;
-                       if (random() * (r2 + r1) > r1)
-                               self.BUTTON_ATCK2 = bot_aim(autocvar_g_balance_laser_secondary_speed, 0, autocvar_g_balance_laser_secondary_lifetime, FALSE);
-                       else
-                               self.BUTTON_ATCK = bot_aim(autocvar_g_balance_laser_primary_speed, 0, autocvar_g_balance_laser_primary_lifetime, FALSE);
-               }
-               else
-                       self.BUTTON_ATCK = bot_aim(autocvar_g_balance_laser_primary_speed, 0, autocvar_g_balance_laser_primary_lifetime, FALSE);
-       }
-       else if (req == WR_THINK)
-       {
-               if(autocvar_g_balance_laser_reload_ammo && self.clip_load < 1) // forced reload
-                       weapon_action(self.weapon, WR_RELOAD);
-               else if (self.BUTTON_ATCK)
-               {
-                       if (weapon_prepareattack(0, autocvar_g_balance_laser_primary_refire))
-                       {
-                               W_DecreaseAmmo(ammo_none, 1, TRUE);
-
-                               W_Laser_Attack(0);
-                               weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_laser_primary_animtime, w_ready);
-                       }
-               }
-               else if (self.BUTTON_ATCK2)
-               {
-                       if(autocvar_g_balance_laser_secondary)
-                       {
-                               W_DecreaseAmmo(ammo_none, 1, TRUE);
-
-                               if (weapon_prepareattack(0, 0))
-                               {
-                                       W_Laser_Attack2();
-                                       weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_laser_secondary_animtime, w_ready);
-                               }
-                       }
-                       else
-                       {
-                               if(self.switchweapon == WEP_LASER) // don't do this if already switching
-                                       W_LastWeapon();
-                       }
-               }
-       }
-       else if (req == WR_PRECACHE)
-       {
-               precache_model ("models/weapons/g_laser.md3");
-               precache_model ("models/weapons/v_laser.md3");
-               precache_model ("models/weapons/h_laser.iqm");
-               precache_sound ("weapons/lasergun_fire.wav");
-               precache_sound ("weapons/gauntlet_fire.wav");
-               //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else
-       }
-       else if (req == WR_SETUP)
-       {
-               weapon_setup(WEP_LASER);
-               self.current_ammo = ammo_none;
-       }
-       else if (req == WR_CHECKAMMO1)
-       {
-               return TRUE;
-       }
-       else if (req == WR_CHECKAMMO2)
-       {
-               return TRUE;
-       }
-       else if (req == WR_RELOAD)
-       {
-               W_Reload(0, autocvar_g_balance_laser_reload_ammo, autocvar_g_balance_laser_reload_time, "weapons/reload.wav");
-       }
-       else if (req == WR_SUICIDEMESSAGE)
-       {
-               return WEAPON_LASER_SUICIDE;
-       }
-       else if (req == WR_KILLMESSAGE)
-       {
-               return WEAPON_LASER_MURDER;
-       }
-       return TRUE;
-}
-#endif
-#ifdef CSQC
-float w_laser(float req)
-{
-       if(req == WR_IMPACTEFFECT)
-       {
-               vector org2;
-               org2 = w_org + w_backoff * 6;
-               pointparticles(particleeffectnum("laser_impact"), org2, w_backoff * 1000, 1);
-               if(!w_issilent)
-                       sound(self, CH_SHOTS, "weapons/laserimpact.wav", VOL_BASE, ATTEN_NORM);
-       }
-       else if(req == WR_PRECACHE)
-       {
-               precache_sound("weapons/laserimpact.wav");
-       }
-       return TRUE;
-}
-#endif
-#endif
diff --git a/qcsrc/server/w_laser.qh b/qcsrc/server/w_laser.qh
deleted file mode 100644 (file)
index 0f2c137..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-void LaserInit();
-vector gauntlet_shotorigin[4];
diff --git a/qcsrc/server/w_minstanex.qc b/qcsrc/server/w_minstanex.qc
deleted file mode 100644 (file)
index 9fb80c6..0000000
+++ /dev/null
@@ -1,213 +0,0 @@
-#ifdef REGISTER_WEAPON
-REGISTER_WEAPON(
-/* WEP_##id  */ MINSTANEX,
-/* function  */ w_minstanex,
-/* ammotype  */ IT_CELLS,
-/* impulse   */ 7,
-/* flags     */ WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_FLAG_SUPERWEAPON | WEP_TYPE_HITSCAN,
-/* rating    */ BOT_PICKUP_RATING_HIGH,
-/* model     */ "minstanex",
-/* shortname */ "minstanex",
-/* fullname  */ _("MinstaNex")
-);
-#else
-#ifdef SVQC
-.float minstanex_lasthit;
-.float jump_interval;
-
-void W_MinstaNex_Attack (void)
-{
-       float flying;
-       flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last
-
-       W_SetupShot (self, TRUE, 0, "weapons/minstanexfire.wav", CH_WEAPON_A, 10000);
-
-       yoda = 0;
-       damage_goodhits = 0;
-       FireRailgunBullet (w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, 10000, 800, 0, 0, 0, 0, WEP_MINSTANEX);
-
-       if(yoda && flying)
-               Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA);
-       if(damage_goodhits && self.minstanex_lasthit)
-       {
-               Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_IMPRESSIVE);
-               damage_goodhits = 0; // only every second time
-       }
-
-       self.minstanex_lasthit = damage_goodhits;
-
-       pointparticles(particleeffectnum("nex_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
-       // teamcolor / hit beam effect
-       vector v;
-       v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
-       switch(self.team)
-       {
-               case NUM_TEAM_1:   // Red
-                       if(damage_goodhits)
-                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED_HIT"), w_shotorg, v);
-                       else
-                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3RED"), w_shotorg, v);
-                       break;
-               case NUM_TEAM_2:   // Blue
-                       if(damage_goodhits)
-                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE_HIT"), w_shotorg, v);
-                       else
-                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3BLUE"), w_shotorg, v);
-                       break;
-               case NUM_TEAM_3:   // Yellow
-                       if(damage_goodhits)
-                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW_HIT"), w_shotorg, v);
-                       else
-                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3YELLOW"), w_shotorg, v);
-                       break;
-               case NUM_TEAM_4:   // Pink
-                       if(damage_goodhits)
-                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK_HIT"), w_shotorg, v);
-                       else
-                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3PINK"), w_shotorg, v);
-                       break;
-               default:
-                       if(damage_goodhits)
-                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3_HIT"), w_shotorg, v);
-                       else
-                               WarpZone_TrailParticles(world, particleeffectnum("TE_TEI_G3"), w_shotorg, v);
-                       break;
-       }
-
-       W_DecreaseAmmo(ammo_cells, ((g_minstagib) ? 1 : autocvar_g_balance_minstanex_ammo), autocvar_g_balance_minstanex_reload_ammo);
-}
-
-void spawnfunc_weapon_minstanex (void); // defined in t_items.qc
-
-float w_minstanex(float req)
-{
-       float ammo_amount;
-       float minstanex_ammo;
-
-       // now multiple WR_s use this
-       minstanex_ammo = ((g_minstagib) ? 1 : autocvar_g_balance_minstanex_ammo);
-
-       if (req == WR_AIM)
-       {
-               if(self.ammo_cells > 0)
-                       self.BUTTON_ATCK = bot_aim(1000000, 0, 1, FALSE);
-               else
-                       self.BUTTON_ATCK2 = bot_aim(autocvar_g_balance_laser_primary_speed, 0, autocvar_g_balance_laser_primary_lifetime, FALSE);
-       }
-       else if (req == WR_THINK)
-       {
-               // if the laser uses load, we also consider its ammo for reloading
-               if(autocvar_g_balance_minstanex_reload_ammo && autocvar_g_balance_minstanex_laser_ammo && self.clip_load < min(minstanex_ammo, autocvar_g_balance_minstanex_laser_ammo)) // forced reload
-                       weapon_action(self.weapon, WR_RELOAD);
-               else if(autocvar_g_balance_minstanex_reload_ammo && self.clip_load < minstanex_ammo) // forced reload
-                       weapon_action(self.weapon, WR_RELOAD);
-               else if (self.BUTTON_ATCK)
-               {
-                       if (weapon_prepareattack(0, autocvar_g_balance_minstanex_refire))
-                       {
-                               W_MinstaNex_Attack();
-                               weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_minstanex_animtime, w_ready);
-                       }
-               }
-               else if (self.BUTTON_ATCK2)
-               {
-                       if (self.jump_interval <= time)
-                       if (weapon_prepareattack(1, -1))
-                       {
-                               // handle refire manually, so that primary and secondary can be fired without conflictions (important for minstagib)
-                               self.jump_interval = time + autocvar_g_balance_minstanex_laser_refire * W_WeaponRateFactor();
-
-                               // decrease ammo for the laser?
-                               if(autocvar_g_balance_minstanex_laser_ammo)
-                                       W_DecreaseAmmo(ammo_cells, autocvar_g_balance_minstanex_laser_ammo, autocvar_g_balance_minstanex_reload_ammo);
-
-                               // ugly minstagib hack to reuse the fire mode of the laser
-                               float w;
-                               w = self.weapon;
-                               self.weapon = WEP_LASER;
-                               W_Laser_Attack(2);
-                               self.weapon = w;
-
-                               // now do normal refire
-                               weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_minstanex_laser_animtime, w_ready);
-                       }
-               }
-       }
-       else if (req == WR_PRECACHE)
-       {
-               precache_model ("models/nexflash.md3");
-               precache_model ("models/weapons/g_minstanex.md3");
-               precache_model ("models/weapons/v_minstanex.md3");
-               precache_model ("models/weapons/h_minstanex.iqm");
-               precache_sound ("weapons/minstanexfire.wav");
-               precache_sound ("weapons/nexwhoosh1.wav");
-               precache_sound ("weapons/nexwhoosh2.wav");
-               precache_sound ("weapons/nexwhoosh3.wav");
-               //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else
-               w_laser(WR_PRECACHE);
-       }
-       else if (req == WR_SETUP)
-       {
-               weapon_setup(WEP_MINSTANEX);
-               self.current_ammo = ammo_cells;
-               self.minstanex_lasthit = 0;
-       }
-       else if (req == WR_CHECKAMMO1)
-       {
-               ammo_amount = self.ammo_cells >= minstanex_ammo;
-               ammo_amount += self.(weapon_load[WEP_MINSTANEX]) >= minstanex_ammo;
-               return ammo_amount;
-       }
-       else if (req == WR_CHECKAMMO2)
-       {
-               if(!autocvar_g_balance_minstanex_laser_ammo)
-                       return TRUE;
-               ammo_amount = self.ammo_cells >= autocvar_g_balance_minstanex_laser_ammo;
-               ammo_amount += self.(weapon_load[WEP_MINSTANEX]) >= autocvar_g_balance_minstanex_laser_ammo;
-               return ammo_amount;
-       }
-       else if (req == WR_RESETPLAYER)
-       {
-               self.minstanex_lasthit = 0;
-       }
-       else if (req == WR_RELOAD)
-       {
-               float used_ammo;
-               if(autocvar_g_balance_minstanex_laser_ammo)
-                       used_ammo = min(minstanex_ammo, autocvar_g_balance_minstanex_laser_ammo);
-               else
-                       used_ammo = minstanex_ammo;
-
-               W_Reload(used_ammo, autocvar_g_balance_minstanex_reload_ammo, autocvar_g_balance_minstanex_reload_time, "weapons/reload.wav");
-       }
-       else if (req == WR_SUICIDEMESSAGE)
-       {
-               return WEAPON_THINKING_WITH_PORTALS;
-       }
-       else if (req == WR_KILLMESSAGE)
-       {
-               return WEAPON_MINSTANEX_MURDER;
-       }
-       return TRUE;
-}
-#endif
-#ifdef CSQC
-float w_minstanex(float req)
-{
-       if(req == WR_IMPACTEFFECT)
-       {
-               vector org2;
-               org2 = w_org + w_backoff * 6;
-               pointparticles(particleeffectnum("nex_impact"), org2, '0 0 0', 1);
-               if(!w_issilent)
-                       sound(self, CH_SHOTS, "weapons/neximpact.wav", VOL_BASE, ATTEN_NORM);
-       }
-       else if(req == WR_PRECACHE)
-       {
-               precache_sound("weapons/neximpact.wav");
-       }
-       return TRUE;
-}
-#endif
-#endif
diff --git a/qcsrc/server/w_nex.qc b/qcsrc/server/w_nex.qc
deleted file mode 100644 (file)
index 919e27e..0000000
+++ /dev/null
@@ -1,275 +0,0 @@
-#ifdef REGISTER_WEAPON
-REGISTER_WEAPON(
-/* WEP_##id  */ NEX,
-/* function  */ w_nex,
-/* ammotype  */ IT_CELLS,
-/* impulse   */ 7,
-/* flags     */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN,
-/* rating    */ BOT_PICKUP_RATING_HIGH,
-/* model     */ "nex",
-/* shortname */ "nex",
-/* fullname  */ _("Nex")
-);
-#else
-#ifdef SVQC
-
-void SendCSQCNexBeamParticle(float charge) {
-       vector v;
-       v = WarpZone_UnTransformOrigin(WarpZone_trace_transform, trace_endpos);
-       WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
-       WriteByte(MSG_BROADCAST, TE_CSQC_NEXGUNBEAMPARTICLE);
-       WriteCoord(MSG_BROADCAST, w_shotorg_x);
-       WriteCoord(MSG_BROADCAST, w_shotorg_y);
-       WriteCoord(MSG_BROADCAST, w_shotorg_z);
-       WriteCoord(MSG_BROADCAST, v_x);
-       WriteCoord(MSG_BROADCAST, v_y);
-       WriteCoord(MSG_BROADCAST, v_z);
-       WriteByte(MSG_BROADCAST, bound(0, 255 * charge, 255));
-}
-
-void W_Nex_Attack (float issecondary)
-{
-       float mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, myammo, charge;
-       if(issecondary)
-       {
-               mydmg = autocvar_g_balance_nex_secondary_damage;
-               myforce = autocvar_g_balance_nex_secondary_force;
-               mymindist = autocvar_g_balance_nex_secondary_damagefalloff_mindist;
-               mymaxdist = autocvar_g_balance_nex_secondary_damagefalloff_maxdist;
-               myhalflife = autocvar_g_balance_nex_secondary_damagefalloff_halflife;
-               myforcehalflife = autocvar_g_balance_nex_secondary_damagefalloff_forcehalflife;
-               myammo = autocvar_g_balance_nex_secondary_ammo;
-       }
-       else
-       {
-               mydmg = autocvar_g_balance_nex_primary_damage;
-               myforce = autocvar_g_balance_nex_primary_force;
-               mymindist = autocvar_g_balance_nex_primary_damagefalloff_mindist;
-               mymaxdist = autocvar_g_balance_nex_primary_damagefalloff_maxdist;
-               myhalflife = autocvar_g_balance_nex_primary_damagefalloff_halflife;
-               myforcehalflife = autocvar_g_balance_nex_primary_damagefalloff_forcehalflife;
-               myammo = autocvar_g_balance_nex_primary_ammo;
-       }
-
-       float flying;
-       flying = IsFlying(self); // do this BEFORE to make the trace values from FireRailgunBullet last
-
-       if(autocvar_g_balance_nex_charge)
-       {
-               charge = autocvar_g_balance_nex_charge_mindmg / mydmg + (1 - autocvar_g_balance_nex_charge_mindmg / mydmg) * self.nex_charge;
-               self.nex_charge *= autocvar_g_balance_nex_charge_shot_multiplier; // do this AFTER setting mydmg/myforce
-               // O RLY? -- divVerent
-               // YA RLY -- FruitieX
-       }
-       else
-               charge = 1;
-       mydmg *= charge;
-       myforce *= charge;
-
-       W_SetupShot (self, TRUE, 5, "weapons/nexfire.wav", CH_WEAPON_A, mydmg);
-       if(charge > autocvar_g_balance_nex_charge_animlimit && autocvar_g_balance_nex_charge_animlimit) // if the Nex is overcharged, we play an extra sound
-       {
-               sound (self, CH_WEAPON_B, "weapons/nexcharge.wav", VOL_BASE * (charge - 0.5 * autocvar_g_balance_nex_charge_animlimit) / (1 - 0.5 * autocvar_g_balance_nex_charge_animlimit), ATTEN_NORM);
-       }
-
-       yoda = 0;
-       FireRailgunBullet (w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, WEP_NEX);
-
-       if(yoda && flying)
-               Send_Notification(NOTIF_ONE, self, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA);
-
-       //beam and muzzle flash done on client
-       SendCSQCNexBeamParticle(charge);
-
-       W_DecreaseAmmo(ammo_cells, myammo, autocvar_g_balance_nex_reload_ammo);
-}
-
-void spawnfunc_weapon_nex (void); // defined in t_items.qc
-
-.float nex_chargepool_pauseregen_finished;
-float w_nex(float req)
-{
-       float dt;
-       float ammo_amount;
-       if (req == WR_AIM)
-       {
-               if(bot_aim(1000000, 0, 1, FALSE))
-                       self.BUTTON_ATCK = TRUE;
-               else
-               {
-                       if(autocvar_g_balance_nex_charge)
-                               self.BUTTON_ATCK2 = TRUE;
-               }
-       }
-       else if (req == WR_THINK)
-       {
-               if(autocvar_g_balance_nex_charge && self.nex_charge < autocvar_g_balance_nex_charge_limit)
-                       self.nex_charge = min(1, self.nex_charge + autocvar_g_balance_nex_charge_rate * frametime / W_TICSPERFRAME);
-
-               if(autocvar_g_balance_nex_secondary_chargepool)
-                       if(self.nex_chargepool_ammo < 1)
-                       {
-                               if(self.nex_chargepool_pauseregen_finished < time)
-                                       self.nex_chargepool_ammo = min(1, self.nex_chargepool_ammo + autocvar_g_balance_nex_secondary_chargepool_regen * frametime / W_TICSPERFRAME);
-                               self.pauseregen_finished = max(self.pauseregen_finished, time + autocvar_g_balance_nex_secondary_chargepool_pause_health_regen);
-                       }
-
-               if(autocvar_g_balance_nex_reload_ammo && self.clip_load < min(autocvar_g_balance_nex_primary_ammo, autocvar_g_balance_nex_secondary_ammo)) // forced reload
-                       weapon_action(self.weapon, WR_RELOAD);
-               else
-               {
-                       if (self.BUTTON_ATCK)
-                       {
-                               if (weapon_prepareattack(0, autocvar_g_balance_nex_primary_refire))
-                               {
-                                       W_Nex_Attack(0);
-                                       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_nex_primary_animtime, w_ready);
-                               }
-                       }
-                       if ((autocvar_g_balance_nex_secondary_charge && !autocvar_g_balance_nex_secondary) ? (self.BUTTON_ZOOM | self.BUTTON_ZOOMSCRIPT) : self.BUTTON_ATCK2)
-                       {
-                               if(autocvar_g_balance_nex_secondary_charge)
-                               {
-                                       self.nex_charge_rottime = time + autocvar_g_balance_nex_charge_rot_pause;
-                                       dt = frametime / W_TICSPERFRAME;
-
-                                       if(self.nex_charge < 1)
-                                       {
-                                               if(autocvar_g_balance_nex_secondary_chargepool)
-                                               {
-                                                       if(autocvar_g_balance_nex_secondary_ammo)
-                                                       {
-                                                               // always deplete if secondary is held
-                                                               self.nex_chargepool_ammo = max(0, self.nex_chargepool_ammo - autocvar_g_balance_nex_secondary_ammo * dt);
-
-                                                               dt = min(dt, (1 - self.nex_charge) / autocvar_g_balance_nex_secondary_charge_rate);
-                                                               self.nex_chargepool_pauseregen_finished = time + autocvar_g_balance_nex_secondary_chargepool_pause_regen;
-                                                               dt = min(dt, self.nex_chargepool_ammo);
-                                                               dt = max(0, dt);
-
-                                                               self.nex_charge += dt * autocvar_g_balance_nex_secondary_charge_rate;
-                                                       }
-                                               }
-
-                                               else if(autocvar_g_balance_nex_secondary_ammo)
-                                               {
-                                                       if(self.BUTTON_ATCK2) // only eat ammo when the button is pressed
-                                                       {
-                                                               dt = min(dt, (1 - self.nex_charge) / autocvar_g_balance_nex_secondary_charge_rate);
-                                                               if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
-                                                               {
-                                                                       // if this weapon is reloadable, decrease its load. Else decrease the player's ammo
-                                                                       if(autocvar_g_balance_nex_reload_ammo)
-                                                                       {
-                                                                               dt = min(dt, (self.clip_load - autocvar_g_balance_nex_primary_ammo) / autocvar_g_balance_nex_secondary_ammo);
-                                                                               dt = max(0, dt);
-                                                                               if(dt > 0)
-                                                                               {
-                                                                                       self.clip_load = max(autocvar_g_balance_nex_secondary_ammo, self.clip_load - autocvar_g_balance_nex_secondary_ammo * dt);
-                                                                               }
-                                                                               self.(weapon_load[WEP_NEX]) = self.clip_load;
-                                                                       }
-                                                                       else
-                                                                       {
-                                                                               dt = min(dt, (self.ammo_cells - autocvar_g_balance_nex_primary_ammo) / autocvar_g_balance_nex_secondary_ammo);
-                                                                               dt = max(0, dt);
-                                                                               if(dt > 0)
-                                                                               {
-                                                                                       self.ammo_cells = max(autocvar_g_balance_nex_secondary_ammo, self.ammo_cells - autocvar_g_balance_nex_secondary_ammo * dt);
-                                                                               }
-                                                                       }
-                                                               }
-                                                               self.nex_charge += dt * autocvar_g_balance_nex_secondary_charge_rate;
-                                                       }
-                                               }
-
-                                               else
-                                               {
-                                                       dt = min(dt, (1 - self.nex_charge) / autocvar_g_balance_nex_secondary_charge_rate);
-                                                       self.nex_charge += dt * autocvar_g_balance_nex_secondary_charge_rate;
-                                               }
-                                       }
-                               }
-                               else if(autocvar_g_balance_nex_secondary)
-                               {
-                                       if (weapon_prepareattack(0, autocvar_g_balance_nex_secondary_refire))
-                                       {
-                                               W_Nex_Attack(1);
-                                               weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_nex_secondary_animtime, w_ready);
-                                       }
-                               }
-                       }
-               }
-       }
-       else if (req == WR_PRECACHE)
-       {
-               precache_model ("models/nexflash.md3");
-               precache_model ("models/weapons/g_nex.md3");
-               precache_model ("models/weapons/v_nex.md3");
-               precache_model ("models/weapons/h_nex.iqm");
-               precache_sound ("weapons/nexfire.wav");
-               precache_sound ("weapons/nexcharge.wav");
-               precache_sound ("weapons/nexwhoosh1.wav");
-               precache_sound ("weapons/nexwhoosh2.wav");
-               precache_sound ("weapons/nexwhoosh3.wav");
-               //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else
-       }
-       else if (req == WR_SETUP)
-       {
-               weapon_setup(WEP_NEX);
-               self.current_ammo = ammo_cells;
-       }
-       else if (req == WR_CHECKAMMO1)
-       {
-               ammo_amount = self.ammo_cells >= autocvar_g_balance_nex_primary_ammo;
-               ammo_amount += (autocvar_g_balance_nex_reload_ammo && self.(weapon_load[WEP_NEX]) >= autocvar_g_balance_nex_primary_ammo);
-               return ammo_amount;
-       }
-       else if (req == WR_CHECKAMMO2)
-       {
-               if(autocvar_g_balance_nex_secondary)
-               {
-                       // don't allow charging if we don't have enough ammo
-                       ammo_amount = self.ammo_cells >= autocvar_g_balance_nex_secondary_ammo;
-                       ammo_amount += self.(weapon_load[WEP_NEX]) >= autocvar_g_balance_nex_secondary_ammo;
-                       return ammo_amount;
-               }
-               else
-               {
-                       return FALSE; // zoom is not a fire mode
-               }
-       }
-       else if (req == WR_RELOAD)
-       {
-               W_Reload(min(autocvar_g_balance_nex_primary_ammo, autocvar_g_balance_nex_secondary_ammo), autocvar_g_balance_nex_reload_ammo, autocvar_g_balance_nex_reload_time, "weapons/reload.wav");
-       }
-       else if (req == WR_SUICIDEMESSAGE)
-       {
-               return WEAPON_THINKING_WITH_PORTALS;
-       }
-       else if (req == WR_KILLMESSAGE)
-       {
-               return WEAPON_NEX_MURDER;
-       }
-       return TRUE;
-}
-#endif
-#ifdef CSQC
-float w_nex(float req)
-{
-       if(req == WR_IMPACTEFFECT)
-       {
-               vector org2;
-               org2 = w_org + w_backoff * 6;
-               pointparticles(particleeffectnum("nex_impact"), org2, '0 0 0', 1);
-               if(!w_issilent)
-                       sound(self, CH_SHOTS, "weapons/neximpact.wav", VOL_BASE, ATTEN_NORM);
-       }
-       else if(req == WR_PRECACHE)
-       {
-               precache_sound("weapons/neximpact.wav");
-       }
-       return TRUE;
-}
-#endif
-#endif
diff --git a/qcsrc/server/w_porto.qc b/qcsrc/server/w_porto.qc
deleted file mode 100644 (file)
index 1eb0f4b..0000000
+++ /dev/null
@@ -1,391 +0,0 @@
-#ifdef REGISTER_WEAPON
-REGISTER_WEAPON(
-/* WEP_##id  */ PORTO,
-/* function  */ w_porto,
-/* ammotype  */ 0,
-/* impulse   */ 0,
-/* flags     */ WEP_TYPE_OTHER | WEP_FLAG_SUPERWEAPON,
-/* rating    */ 0,
-/* model     */ "porto" ,
-/* shortname */ "porto",
-/* fullname  */ _("Port-O-Launch")
-);
-#else
-#ifdef SVQC
-.entity porto_current;
-.vector porto_v_angle; // holds "held" view angles
-.float porto_v_angle_held;
-.vector right_vector;
-
-void W_Porto_Success (void)
-{
-       if(self.realowner == world)
-       {
-               objerror("Cannot succeed successfully: no owner\n");
-               return;
-       }
-
-       self.realowner.porto_current = world;
-       remove(self);
-}
-
-string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo);
-void W_Porto_Fail (float failhard)
-{
-       if(self.realowner == world)
-       {
-               objerror("Cannot fail successfully: no owner\n");
-               return;
-       }
-
-       // no portals here!
-       if(self.cnt < 0)
-       {
-               Portal_ClearWithID(self.realowner, self.portal_id);
-       }
-
-       self.realowner.porto_current = world;
-
-       if(self.cnt < 0 && !failhard && self.realowner.playerid == self.playerid && self.realowner.deadflag == DEAD_NO && !(self.realowner.weapons & WEPSET_PORTO))
-       {
-               setsize (self, '-16 -16 0', '16 16 32');
-               setorigin(self, self.origin + trace_plane_normal);
-               if(move_out_of_solid(self))
-               {
-                       self.flags = FL_ITEM;
-                       self.velocity = trigger_push_calculatevelocity(self.origin, self.realowner, 128);
-                       tracetoss(self, self);
-                       if(vlen(trace_endpos - self.realowner.origin) < 128)
-                       {
-                               W_ThrowNewWeapon(self.realowner, WEP_PORTO, 0, self.origin, self.velocity);
-                               centerprint(self.realowner, "^1Portal deployment failed.\n\n^2Catch it to try again!");
-                       }
-               }
-       }
-       remove(self);
-}
-
-void W_Porto_Remove (entity p)
-{
-       if(p.porto_current.realowner == p && p.porto_current.classname == "porto")
-       {
-               entity oldself;
-               oldself = self;
-               self = p.porto_current;
-               W_Porto_Fail(1);
-               self = oldself;
-       }
-}
-
-void W_Porto_Think (void)
-{
-       trace_plane_normal = '0 0 0';
-       if(self.realowner.playerid != self.playerid)
-               remove(self);
-       else
-               W_Porto_Fail(0);
-}
-
-void W_Porto_Touch (void)
-{
-       vector norm;
-
-       // do not use PROJECTILE_TOUCH here
-       // FIXME but DO handle warpzones!
-
-       if(other.classname == "portal")
-               return; // handled by the portal
-
-       norm = trace_plane_normal;
-       if(trace_ent.iscreature)
-       {
-               traceline(trace_ent.origin, trace_ent.origin + '0 0 2' * PL_MIN_z, MOVE_WORLDONLY, self);
-               if(trace_fraction >= 1)
-                       return;
-               if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
-                       return;
-               if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
-                       return;
-       }
-
-       if(self.realowner.playerid != self.playerid)
-       {
-               sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
-               remove(self);
-       }
-       else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
-       {
-               spamsound(self, CH_SHOTS, "porto/bounce.wav", VOL_BASE, ATTEN_NORM);
-               // just reflect
-               self.right_vector = self.right_vector - 2 * trace_plane_normal * (self.right_vector * trace_plane_normal);
-               self.angles = vectoangles(self.velocity - 2 * trace_plane_normal * (self.velocity * trace_plane_normal));
-       }
-       else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
-       {
-               sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
-               W_Porto_Fail(0);
-               if(self.cnt < 0)
-                       Portal_ClearAll_PortalsOnly(self.realowner);
-       }
-       else if(self.cnt == 0)
-       {
-               // in-portal only
-               if(Portal_SpawnInPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
-               {
-                       sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
-                       trace_plane_normal = norm;
-                       centerprint(self.realowner, "^1In^7-portal created.");
-                       W_Porto_Success();
-               }
-               else
-               {
-                       sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
-                       trace_plane_normal = norm;
-                       W_Porto_Fail(0);
-               }
-       }
-       else if(self.cnt == 1)
-       {
-               // out-portal only
-               if(Portal_SpawnOutPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
-               {
-                       sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
-                       trace_plane_normal = norm;
-                       centerprint(self.realowner, "^4Out^7-portal created.");
-                       W_Porto_Success();
-               }
-               else
-               {
-                       sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
-                       trace_plane_normal = norm;
-                       W_Porto_Fail(0);
-               }
-       }
-       else if(self.effects & EF_RED)
-       {
-               self.effects += EF_BLUE - EF_RED;
-               if(Portal_SpawnInPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
-               {
-                       sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
-                       trace_plane_normal = norm;
-                       centerprint(self.realowner, "^1In^7-portal created.");
-                       self.right_vector = self.right_vector - 2 * trace_plane_normal * (self.right_vector * norm);
-                       self.angles = vectoangles(self.velocity - 2 * trace_plane_normal * (self.velocity * norm));
-                       CSQCProjectile(self, TRUE, PROJECTILE_PORTO_BLUE, TRUE); // change type
-               }
-               else
-               {
-                       sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
-                       trace_plane_normal = norm;
-                       Portal_ClearAll_PortalsOnly(self.realowner);
-                       W_Porto_Fail(0);
-               }
-       }
-       else
-       {
-               if(self.realowner.portal_in.portal_id == self.portal_id)
-               {
-                       if(Portal_SpawnOutPortalAtTrace(self.realowner, self.right_vector, self.portal_id))
-                       {
-                               sound(self, CH_SHOTS, "porto/create.wav", VOL_BASE, ATTEN_NORM);
-                               trace_plane_normal = norm;
-                               centerprint(self.realowner, "^4Out^7-portal created.");
-                               W_Porto_Success();
-                       }
-                       else
-                       {
-                               sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
-                               Portal_ClearAll_PortalsOnly(self.realowner);
-                               W_Porto_Fail(0);
-                       }
-               }
-               else
-               {
-                       sound(self, CH_SHOTS, "porto/unsupported.wav", VOL_BASE, ATTEN_NORM);
-                       Portal_ClearAll_PortalsOnly(self.realowner);
-                       W_Porto_Fail(0);
-               }
-       }
-}
-
-void W_Porto_Attack (float type)
-{
-       entity gren;
-
-       W_SetupShot (self, FALSE, 4, "porto/fire.wav", CH_WEAPON_A, 0);
-       // always shoot from the eye
-       w_shotdir = v_forward;
-       w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward;
-
-       //pointparticles(particleeffectnum("grenadelauncher_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
-       gren = spawn ();
-       gren.cnt = type;
-       gren.owner = gren.realowner = self;
-       gren.playerid = self.playerid;
-       gren.classname = "porto";
-       gren.bot_dodge = TRUE;
-       gren.bot_dodgerating = 200;
-       gren.movetype = MOVETYPE_BOUNCEMISSILE;
-       PROJECTILE_MAKETRIGGER(gren);
-       gren.effects = EF_RED;
-       gren.scale = 4;
-       setorigin(gren, w_shotorg);
-       setsize(gren, '0 0 0', '0 0 0');
-
-       if(type > 0)
-               gren.nextthink = time + autocvar_g_balance_porto_secondary_lifetime;
-       else
-               gren.nextthink = time + autocvar_g_balance_porto_primary_lifetime;
-       gren.think = W_Porto_Think;
-       gren.touch = W_Porto_Touch;
-
-       if(type > 0)
-       {
-               if(self.items & IT_STRENGTH)
-                       W_SetupProjectileVelocity(gren, autocvar_g_balance_porto_secondary_speed * autocvar_g_balance_powerup_strength_force, 0);
-               else
-                       W_SetupProjectileVelocity(gren, autocvar_g_balance_porto_secondary_speed, 0);
-       }
-       else
-       {
-               if(self.items & IT_STRENGTH)
-                       W_SetupProjectileVelocity(gren, autocvar_g_balance_porto_primary_speed * autocvar_g_balance_powerup_strength_force, 0);
-               else
-                       W_SetupProjectileVelocity(gren, autocvar_g_balance_porto_primary_speed, 0);
-       }
-
-       gren.angles = vectoangles (gren.velocity);
-       gren.flags = FL_PROJECTILE;
-
-       gren.portal_id = time;
-       self.porto_current = gren;
-       gren.playerid = self.playerid;
-       fixedmakevectors(fixedvectoangles(gren.velocity));
-       gren.right_vector = v_right;
-
-       gren.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP;
-
-       if(type > 0)
-               CSQCProjectile(gren, TRUE, PROJECTILE_PORTO_BLUE, TRUE);
-       else
-               CSQCProjectile(gren, TRUE, PROJECTILE_PORTO_RED, TRUE);
-
-       other = gren; MUTATOR_CALLHOOK(EditProjectile);
-}
-
-void spawnfunc_weapon_porto (void)
-{
-       weapon_defaultspawnfunc(WEP_PORTO);
-}
-
-float w_nexball_weapon(float req);
-float w_porto(float req)
-{
-       //vector v_angle_save;
-
-       if (g_nexball) { return w_nexball_weapon(req); }
-       if (req == WR_AIM)
-       {
-               self.BUTTON_ATCK = FALSE;
-               self.BUTTON_ATCK2 = FALSE;
-               if(!autocvar_g_balance_porto_secondary)
-                       if(bot_aim(autocvar_g_balance_porto_primary_speed, 0, autocvar_g_balance_grenadelauncher_primary_lifetime, FALSE))
-                               self.BUTTON_ATCK = TRUE;
-       }
-       else if (req == WR_THINK)
-       {
-               if(autocvar_g_balance_porto_secondary)
-               {
-                       if (self.BUTTON_ATCK)
-                       if (!self.porto_current)
-                       if (!self.porto_forbidden)
-                       if (weapon_prepareattack(0, autocvar_g_balance_porto_primary_refire))
-                       {
-                               W_Porto_Attack(0);
-                               weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_porto_primary_animtime, w_ready);
-                       }
-
-                       if (self.BUTTON_ATCK2)
-                       if (!self.porto_current)
-                       if (!self.porto_forbidden)
-                       if (weapon_prepareattack(1, autocvar_g_balance_porto_secondary_refire))
-                       {
-                               W_Porto_Attack(1);
-                               weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_porto_secondary_animtime, w_ready);
-                       }
-               }
-               else
-               {
-                       if(self.porto_v_angle_held)
-                       {
-                               if(!self.BUTTON_ATCK2)
-                               {
-                                       self.porto_v_angle_held = 0;
-
-                                       ClientData_Touch(self);
-                               }
-                       }
-                       else
-                       {
-                               if(self.BUTTON_ATCK2)
-                               {
-                                       self.porto_v_angle = self.v_angle;
-                                       self.porto_v_angle_held = 1;
-
-                                       ClientData_Touch(self);
-                               }
-                       }
-                       if(self.porto_v_angle_held)
-                               makevectors(self.porto_v_angle); // override the previously set angles
-
-                       if (self.BUTTON_ATCK)
-                       if (!self.porto_current)
-                       if (!self.porto_forbidden)
-                       if (weapon_prepareattack(0, autocvar_g_balance_porto_primary_refire))
-                       {
-                               W_Porto_Attack(-1);
-                               weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_porto_primary_animtime, w_ready);
-                       }
-               }
-       }
-       else if (req == WR_PRECACHE)
-       {
-               precache_model ("models/weapons/g_porto.md3");
-               precache_model ("models/weapons/v_porto.md3");
-               precache_model ("models/weapons/h_porto.iqm");
-               precache_model ("models/portal.md3");
-               precache_sound ("porto/bounce.wav");
-               precache_sound ("porto/create.wav");
-               precache_sound ("porto/expire.wav");
-               precache_sound ("porto/explode.wav");
-               precache_sound ("porto/fire.wav");
-               precache_sound ("porto/unsupported.wav");
-       }
-       else if (req == WR_SETUP)
-       {
-               weapon_setup(WEP_PORTO);
-               self.current_ammo = ammo_none;
-       }
-       else if (req == WR_RESETPLAYER)
-       {
-               self.porto_current = world;
-       }
-       return TRUE;
-}
-#endif
-#ifdef CSQC
-float w_porto(float req)
-{
-       if(req == WR_IMPACTEFFECT)
-       {
-               print("Since when does Porto send DamageInfo?\n");
-       }
-       else if(req == WR_PRECACHE)
-       {
-               // nothing to do
-       }
-       return TRUE;
-}
-#endif
-#endif
diff --git a/qcsrc/server/w_rifle.qc b/qcsrc/server/w_rifle.qc
deleted file mode 100644 (file)
index 90db2ad..0000000
+++ /dev/null
@@ -1,265 +0,0 @@
-#ifdef REGISTER_WEAPON
-REGISTER_WEAPON(
-/* WEP_##id  */ RIFLE,
-/* function  */ w_rifle,
-/* ammotype  */ IT_NAILS,
-/* impulse   */ 7,
-/* flags     */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN,
-/* rating    */ BOT_PICKUP_RATING_MID,
-/* model     */ "campingrifle",
-/* shortname */ "rifle",
-/* fullname  */ _("Rifle")
-);
-#else
-#ifdef SVQC
-
-.float rifle_accumulator;
-
-void W_Rifle_FireBullet(float pSpread, float pDamage, float pForce, float pSpeed, float pLifetime, float pAmmo, float deathtype, float pBulletConstant, float pTracer, float pShots, string pSound)
-{
-       float i;
-
-       W_DecreaseAmmo(ammo_nails, pAmmo, autocvar_g_balance_rifle_reload_ammo);
-
-       W_SetupShot (self, TRUE, 2, pSound, CH_WEAPON_A, pDamage * pShots);
-
-       pointparticles(particleeffectnum("rifle_muzzleflash"), w_shotorg, w_shotdir * 2000, 1);
-
-       if(self.BUTTON_ZOOM | self.BUTTON_ZOOMSCRIPT) // if zoomed, shoot from the eye
-       {
-               w_shotdir = v_forward;
-               w_shotorg = self.origin + self.view_ofs + ((w_shotorg - self.origin - self.view_ofs) * v_forward) * v_forward;
-       }
-
-       for(i = 0; i < pShots; ++i)
-               fireBallisticBullet(w_shotorg, w_shotdir, pSpread, pSpeed, pLifetime, pDamage, pForce, deathtype, (pTracer ? EF_RED : EF_BLUE), pBulletConstant);
-       endFireBallisticBullet();
-
-       if (autocvar_g_casings >= 2)
-               SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, self);
-}
-
-void W_Rifle_Attack()
-{
-       W_Rifle_FireBullet(autocvar_g_balance_rifle_primary_spread, autocvar_g_balance_rifle_primary_damage, autocvar_g_balance_rifle_primary_force, autocvar_g_balance_rifle_primary_speed, autocvar_g_balance_rifle_primary_lifetime, autocvar_g_balance_rifle_primary_ammo, WEP_RIFLE, autocvar_g_balance_rifle_primary_bulletconstant, autocvar_g_balance_rifle_primary_tracer, autocvar_g_balance_rifle_primary_shots, "weapons/campingrifle_fire.wav");
-}
-
-void W_Rifle_Attack2()
-{
-       W_Rifle_FireBullet(autocvar_g_balance_rifle_secondary_spread, autocvar_g_balance_rifle_secondary_damage, autocvar_g_balance_rifle_secondary_force, autocvar_g_balance_rifle_secondary_speed, autocvar_g_balance_rifle_secondary_lifetime, autocvar_g_balance_rifle_secondary_ammo, WEP_RIFLE | HITTYPE_SECONDARY, autocvar_g_balance_rifle_secondary_bulletconstant, autocvar_g_balance_rifle_secondary_tracer, autocvar_g_balance_rifle_secondary_shots, "weapons/campingrifle_fire2.wav");
-}
-
-void spawnfunc_weapon_rifle (void)
-{
-       weapon_defaultspawnfunc(WEP_RIFLE);
-}
-
-// compatibility alias
-void spawnfunc_weapon_campingrifle (void)
-{
-       spawnfunc_weapon_rifle();
-}
-void spawnfunc_weapon_sniperrifle (void)
-{
-       spawnfunc_weapon_rifle();
-}
-
-.void(void) rifle_bullethail_attackfunc;
-.float rifle_bullethail_frame;
-.float rifle_bullethail_animtime;
-.float rifle_bullethail_refire;
-void W_Rifle_BulletHail_Continue()
-{
-       float r, sw, af;
-
-       sw = self.switchweapon; // make it not detect weapon changes as reason to abort firing
-       af = ATTACK_FINISHED(self);
-       self.switchweapon = self.weapon;
-       ATTACK_FINISHED(self) = time;
-       print(ftos(self.ammo_nails), "\n");
-       r = weapon_prepareattack(self.rifle_bullethail_frame == WFRAME_FIRE2, self.rifle_bullethail_refire);
-       if(self.switchweapon == self.weapon)
-               self.switchweapon = sw;
-       if(r)
-       {
-               self.rifle_bullethail_attackfunc();
-               weapon_thinkf(self.rifle_bullethail_frame, self.rifle_bullethail_animtime, W_Rifle_BulletHail_Continue);
-               print("thinkf set\n");
-       }
-       else
-       {
-               ATTACK_FINISHED(self) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time
-               print("out of ammo... ", ftos(self.weaponentity.state), "\n");
-       }
-}
-
-void W_Rifle_BulletHail(float mode, void(void) AttackFunc, float fr, float animtime, float refire)
-{
-       // if we get here, we have at least one bullet to fire
-       AttackFunc();
-       if(mode)
-       {
-               // continue hail
-               self.rifle_bullethail_attackfunc = AttackFunc;
-               self.rifle_bullethail_frame = fr;
-               self.rifle_bullethail_animtime = animtime;
-               self.rifle_bullethail_refire = refire;
-               weapon_thinkf(fr, animtime, W_Rifle_BulletHail_Continue);
-       }
-       else
-       {
-               // just one shot
-               weapon_thinkf(fr, animtime, w_ready);
-       }
-}
-
-.float bot_secondary_riflemooth;
-float w_rifle(float req)
-{
-       float ammo_amount;
-
-       if (req == WR_AIM)
-       {
-               self.BUTTON_ATCK=FALSE;
-               self.BUTTON_ATCK2=FALSE;
-               if(vlen(self.origin-self.enemy.origin) > 1000)
-                       self.bot_secondary_riflemooth = 0;
-               if(self.bot_secondary_riflemooth == 0)
-               {
-                       if(bot_aim(autocvar_g_balance_rifle_primary_speed, 0, autocvar_g_balance_rifle_primary_lifetime, FALSE))
-                       {
-                               self.BUTTON_ATCK = TRUE;
-                               if(random() < 0.01) self.bot_secondary_riflemooth = 1;
-                       }
-               }
-               else
-               {
-                       if(bot_aim(autocvar_g_balance_rifle_secondary_speed, 0, autocvar_g_balance_rifle_secondary_lifetime, FALSE))
-                       {
-                               self.BUTTON_ATCK2 = TRUE;
-                               if(random() < 0.03) self.bot_secondary_riflemooth = 0;
-                       }
-               }
-       }
-       else if (req == WR_THINK)
-       {
-               if(autocvar_g_balance_rifle_reload_ammo && self.clip_load < min(autocvar_g_balance_rifle_primary_ammo, autocvar_g_balance_rifle_secondary_ammo)) // forced reload
-            weapon_action(self.weapon, WR_RELOAD);
-               else
-               {
-                       self.rifle_accumulator = bound(time - autocvar_g_balance_rifle_bursttime, self.rifle_accumulator, time);
-                       if (self.BUTTON_ATCK)
-                       if (weapon_prepareattack_check(0, autocvar_g_balance_rifle_primary_refire))
-                       if (time >= self.rifle_accumulator + autocvar_g_balance_rifle_primary_burstcost)
-                       {
-                               weapon_prepareattack_do(0, autocvar_g_balance_rifle_primary_refire);
-                               W_Rifle_BulletHail(autocvar_g_balance_rifle_primary_bullethail, W_Rifle_Attack, WFRAME_FIRE1, autocvar_g_balance_rifle_primary_animtime, autocvar_g_balance_rifle_primary_refire);
-                               self.rifle_accumulator += autocvar_g_balance_rifle_primary_burstcost;
-                       }
-                       if (self.BUTTON_ATCK2)
-                       {
-                               if (autocvar_g_balance_rifle_secondary)
-                               {
-                    if(autocvar_g_balance_rifle_secondary_reload)
-                        weapon_action(self.weapon, WR_RELOAD);
-                    else
-                    {
-                        if (weapon_prepareattack_check(1, autocvar_g_balance_rifle_secondary_refire))
-                        if (time >= self.rifle_accumulator + autocvar_g_balance_rifle_secondary_burstcost)
-                        {
-                            weapon_prepareattack_do(1, autocvar_g_balance_rifle_secondary_refire);
-                            W_Rifle_BulletHail(autocvar_g_balance_rifle_secondary_bullethail, W_Rifle_Attack2, WFRAME_FIRE2, autocvar_g_balance_rifle_secondary_animtime, autocvar_g_balance_rifle_primary_refire);
-                            self.rifle_accumulator += autocvar_g_balance_rifle_secondary_burstcost;
-                        }
-                    }
-                               }
-                       }
-               }
-       }
-       else if (req == WR_PRECACHE)
-       {
-               precache_model ("models/weapons/g_campingrifle.md3");
-               precache_model ("models/weapons/v_campingrifle.md3");
-               precache_model ("models/weapons/h_campingrifle.iqm");
-               precache_sound ("weapons/campingrifle_fire.wav");
-               precache_sound ("weapons/campingrifle_fire2.wav");
-               //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else
-       }
-       else if (req == WR_SETUP)
-       {
-               weapon_setup(WEP_RIFLE);
-               self.current_ammo = ammo_nails;
-       }
-       else if (req == WR_CHECKAMMO1)
-       {
-               ammo_amount = self.ammo_nails >= autocvar_g_balance_rifle_primary_ammo;
-               ammo_amount += self.(weapon_load[WEP_RIFLE]) >= autocvar_g_balance_rifle_primary_ammo;
-               return ammo_amount;
-       }
-       else if (req == WR_CHECKAMMO2)
-       {
-               ammo_amount = self.ammo_nails >= autocvar_g_balance_rifle_secondary_ammo;
-               ammo_amount += self.(weapon_load[WEP_RIFLE]) >= autocvar_g_balance_rifle_secondary_ammo;
-               return ammo_amount;
-       }
-       else if (req == WR_RESETPLAYER)
-       {
-               self.rifle_accumulator = time - autocvar_g_balance_rifle_bursttime;
-       }
-       else if (req == WR_RELOAD)
-       {
-               W_Reload(min(autocvar_g_balance_rifle_primary_ammo, autocvar_g_balance_rifle_secondary_ammo), autocvar_g_balance_rifle_reload_ammo, autocvar_g_balance_rifle_reload_time, "weapons/reload.wav");
-       }
-       else if (req == WR_SUICIDEMESSAGE)
-       {
-               return WEAPON_THINKING_WITH_PORTALS;
-       }
-       else if (req == WR_KILLMESSAGE)
-       {
-               if(w_deathtype & HITTYPE_SECONDARY)
-               {
-                       if(w_deathtype & HITTYPE_BOUNCE)
-                               return WEAPON_RIFLE_MURDER_HAIL_PIERCING;
-                       else
-                               return WEAPON_RIFLE_MURDER_HAIL;
-               }
-               else
-               {
-                       if(w_deathtype & HITTYPE_BOUNCE)
-                               return WEAPON_RIFLE_MURDER_PIERCING;
-                       else
-                               return WEAPON_RIFLE_MURDER;
-               }
-       }
-       return TRUE;
-}
-#endif
-#ifdef CSQC
-float w_rifle(float req)
-{
-       if(req == WR_IMPACTEFFECT)
-       {
-               vector org2;
-               org2 = w_org + w_backoff * 2;
-               pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
-               if(!w_issilent)
-               {
-                       if(w_random < 0.2)
-                               sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTEN_NORM);
-                       else if(w_random < 0.4)
-                               sound(self, CH_SHOTS, "weapons/ric2.wav", VOL_BASE, ATTEN_NORM);
-                       else if(w_random < 0.5)
-                               sound(self, CH_SHOTS, "weapons/ric3.wav", VOL_BASE, ATTEN_NORM);
-               }
-       }
-       else if(req == WR_PRECACHE)
-       {
-               precache_sound("weapons/ric1.wav");
-               precache_sound("weapons/ric2.wav");
-               precache_sound("weapons/ric3.wav");
-       }
-
-       return TRUE;
-}
-#endif
-#endif
diff --git a/qcsrc/server/w_seeker.qc b/qcsrc/server/w_seeker.qc
deleted file mode 100644 (file)
index c870f08..0000000
+++ /dev/null
@@ -1,712 +0,0 @@
-#ifdef REGISTER_WEAPON
-REGISTER_WEAPON(
-/* WEP_##id  */ SEEKER,
-/* function  */ w_seeker,
-/* ammotype  */ IT_ROCKETS,
-/* impulse   */ 8,
-/* flags     */ WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH,
-/* rating    */ BOT_PICKUP_RATING_MID,
-/* model     */ "seeker",
-/* shortname */ "seeker",
-/* fullname  */ _("T.A.G. Seeker")
-);
-#else
-#ifdef SVQC
-//.float proxytime; = autoswitch
-//.float tl; = wait
-.entity tag_target, wps_tag_tracker;
-.float tag_time;
-
-// ============================
-// Begin: Missile functions, these are general functions to be manipulated by other code
-// ============================
-void Seeker_Missile_Explode ()
-{
-       self.event_damage = func_null;
-       RadiusDamage (self, self.realowner, autocvar_g_balance_seeker_missile_damage, autocvar_g_balance_seeker_missile_edgedamage, autocvar_g_balance_seeker_missile_radius, world, autocvar_g_balance_seeker_missile_force, self.projectiledeathtype, other);
-
-       remove (self);
-}
-
-void Seeker_Missile_Touch()
-{
-       PROJECTILE_TOUCH;
-
-       Seeker_Missile_Explode();
-}
-
-void Seeker_Missile_Think()
-{
-       entity e;
-       vector desireddir, olddir, newdir, eorg;
-       float turnrate;
-       float dist;
-       float spd;
-
-       if (time > self.cnt)
-       {
-               self.projectiledeathtype |= HITTYPE_SPLASH;
-               Seeker_Missile_Explode();
-       }
-
-       spd = vlen(self.velocity);
-       spd = bound(
-               spd - autocvar_g_balance_seeker_missile_decel * frametime,
-               autocvar_g_balance_seeker_missile_speed_max,
-               spd + autocvar_g_balance_seeker_missile_accel * frametime
-       );
-
-       if (self.enemy != world)
-               if (self.enemy.takedamage != DAMAGE_AIM || self.enemy.deadflag != DEAD_NO)
-                       self.enemy = world;
-
-       if (self.enemy != world)
-       {
-               e               = self.enemy;
-               eorg            = 0.5 * (e.absmin + e.absmax);
-               turnrate        = autocvar_g_balance_seeker_missile_turnrate; // how fast to turn
-               desireddir      = normalize(eorg - self.origin);
-               olddir          = normalize(self.velocity); // get my current direction
-               dist            = vlen(eorg - self.origin);
-
-               // Do evasive maneuvers for world objects? ( this should be a cpu hog. :P )
-               if (autocvar_g_balance_seeker_missile_smart && (dist > autocvar_g_balance_seeker_missile_smart_mindist))
-               {
-                       // Is it a better idea (shorter distance) to trace to the target itself?
-                       if ( vlen(self.origin + olddir * self.wait) < dist)
-                               traceline(self.origin, self.origin + olddir * self.wait, FALSE, self);
-                       else
-                               traceline(self.origin, eorg, FALSE, self);
-
-                       // Setup adaptive tracelength
-                       self.wait = bound(autocvar_g_balance_seeker_missile_smart_trace_min, vlen(self.origin - trace_endpos), self.wait = autocvar_g_balance_seeker_missile_smart_trace_max);
-
-                       // Calc how important it is that we turn and add this to the desierd (enemy) dir.
-                       desireddir  = normalize(((trace_plane_normal * (1 - trace_fraction)) + (desireddir * trace_fraction)) * 0.5);
-               }
-
-               newdir = normalize(olddir + desireddir * turnrate); // take the average of the 2 directions; not the best method but simple & easy
-               self.velocity = newdir * spd; // make me fly in the new direction at my flight speed
-       }
-       else
-               dist = 0;
-
-       // Proxy
-       if (autocvar_g_balance_seeker_missile_proxy)
-       {
-               if ( dist <= autocvar_g_balance_seeker_missile_proxy_maxrange)
-               {
-                       if (self.autoswitch == 0)
-                       {
-                               self.autoswitch = time + autocvar_g_balance_seeker_missile_proxy_delay;
-                       }
-                       else
-                       {
-                               if (self.autoswitch <= time)
-                               {
-                                       Seeker_Missile_Explode();
-                                       self.autoswitch = 0;
-                               }
-                       }
-               }
-               else
-               {
-                       if (self.autoswitch != 0)
-                               self.autoswitch = 0;
-               }
-       }
-       ///////////////
-
-       if (self.enemy.deadflag != DEAD_NO)
-       {
-               self.enemy = world;
-               self.cnt = time + 1 + (random() * 4);
-               self.nextthink = self.cnt;
-               return;
-       }
-
-       //self.angles = vectoangles(self.velocity);                     // turn model in the new flight direction
-       self.nextthink = time;// + 0.05; // csqc projectiles
-       UpdateCSQCProjectile(self);
-}
-
-
-
-void Seeker_Missile_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-{
-       if (self.health <= 0)
-               return;
-
-       if (!W_CheckProjectileDamage(inflictor.realowner, self.realowner, deathtype, -1)) // no exceptions
-               return; // g_projectiles_damage says to halt
-
-       if (self.realowner == attacker)
-               self.health = self.health - (damage * 0.25);
-       else
-               self.health = self.health - damage;
-
-       if (self.health <= 0)
-               W_PrepareExplosionByDamage(attacker, Seeker_Missile_Explode);
-}
-
-/*
-void Seeker_Missile_Animate()
-{
-       self.frame = self.frame +1;
-       self.nextthink = time + 0.05;
-
-       if (self.enemy != world)
-               if (self.enemy.takedamage != DAMAGE_AIM || self.enemy.deadflag != DEAD_NO)
-                       self.enemy = world;
-
-       if(self.frame == 5)
-       {
-               self.think           = Seeker_Missile_Think;
-               self.nextthink       = time;// + cvar("g_balance_seeker_missile_activate_delay"); // cant dealy with csqc projectiles
-
-               if (autocvar_g_balance_seeker_missile_proxy)
-                       self.movetype    = MOVETYPE_BOUNCEMISSILE;
-               else
-                       self.movetype    = MOVETYPE_FLYMISSILE;
-       }
-
-       UpdateCSQCProjectile(self);
-}
-*/
-
-void Seeker_Fire_Missile(vector f_diff, entity m_target)
-{
-       entity missile;
-
-       W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_seeker_missile_ammo, autocvar_g_balance_seeker_reload_ammo);
-
-       makevectors(self.v_angle);
-       W_SetupShot_ProjectileSize (self, '-2 -2 -2', '2 2 2', FALSE, 2, "weapons/seeker_fire.wav", CH_WEAPON_A, 0);
-       w_shotorg += f_diff;
-       pointparticles(particleeffectnum("seeker_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
-       //self.detornator         = FALSE;
-
-       missile                 = spawn();
-       missile.owner           = missile.realowner = self;
-       missile.classname       = "seeker_missile";
-       missile.bot_dodge       = TRUE;
-       missile.bot_dodgerating = autocvar_g_balance_seeker_missile_damage;
-
-       missile.think           = Seeker_Missile_Think;
-       missile.touch           = Seeker_Missile_Touch;
-       missile.event_damage    = Seeker_Missile_Damage;
-       missile.nextthink       = time;// + 0.2;// + cvar("g_balance_seeker_missile_activate_delay");
-       missile.cnt             = time + autocvar_g_balance_seeker_missile_lifetime;
-       missile.enemy           = m_target;
-       missile.solid           = SOLID_BBOX;
-       missile.scale           = 2;
-       missile.takedamage      = DAMAGE_YES;
-       missile.health          = autocvar_g_balance_seeker_missile_health;
-       missile.damageforcescale = autocvar_g_balance_seeker_missile_damageforcescale;
-       missile.damagedbycontents = TRUE;
-       //missile.think           = Seeker_Missile_Animate; // csqc projectiles.
-
-       if (missile.enemy != world)
-               missile.projectiledeathtype = WEP_SEEKER | HITTYPE_SECONDARY;
-       else
-               missile.projectiledeathtype = WEP_SEEKER;
-
-
-       setorigin (missile, w_shotorg);
-       setsize (missile, '-4 -4 -4', '4 4 4');
-       missile.movetype    = MOVETYPE_FLYMISSILE;
-       missile.flags       = FL_PROJECTILE;
-       missile.missile_flags = MIF_SPLASH | MIF_GUIDED_TAG;
-
-       W_SETUPPROJECTILEVELOCITY_UP(missile, g_balance_seeker_missile);
-
-       missile.angles = vectoangles (missile.velocity);
-
-       CSQCProjectile(missile, FALSE, PROJECTILE_SEEKER, TRUE);
-
-       other = missile; MUTATOR_CALLHOOK(EditProjectile);
-}
-
-// ============================
-// Begin: FLAC, close range attack meant for defeating rockets which are coming at you.
-// ============================
-void Seeker_Flac_Explode ()
-{
-       self.event_damage = func_null;
-
-       RadiusDamage (self, self.realowner, autocvar_g_balance_seeker_flac_damage, autocvar_g_balance_seeker_flac_edgedamage, autocvar_g_balance_seeker_flac_radius, world, autocvar_g_balance_seeker_flac_force, self.projectiledeathtype, other);
-
-       remove (self);
-}
-
-void Seeker_Flac_Touch()
-{
-       PROJECTILE_TOUCH;
-
-       Seeker_Flac_Explode();
-}
-
-void Seeker_Fire_Flac()
-{
-       entity missile;
-       vector f_diff;
-       float c;
-
-       W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_seeker_flac_ammo, autocvar_g_balance_seeker_reload_ammo);
-
-       c = mod(self.bulletcounter, 4);
-       switch(c)
-       {
-               case 0:
-                       f_diff = '-1.25 -3.75 0';
-                       break;
-               case 1:
-                       f_diff = '+1.25 -3.75 0';
-                       break;
-               case 2:
-                       f_diff = '-1.25 +3.75 0';
-                       break;
-               case 3:
-               default:
-                       f_diff = '+1.25 +3.75 0';
-                       break;
-       }
-       W_SetupShot_ProjectileSize (self, '-2 -2 -2', '2 2 2', FALSE, 2, "weapons/flac_fire.wav", CH_WEAPON_A, autocvar_g_balance_seeker_flac_damage);
-       w_shotorg += f_diff;
-
-       pointparticles(particleeffectnum("hagar_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
-       missile                                 = spawn ();
-       missile.owner                   = missile.realowner = self;
-       missile.classname               = "missile";
-       missile.bot_dodge               = TRUE;
-       missile.bot_dodgerating = autocvar_g_balance_seeker_flac_damage;
-       missile.touch                   = Seeker_Flac_Explode;
-       missile.use                     = Seeker_Flac_Explode;
-       missile.think                   = adaptor_think2use_hittype_splash;
-       missile.nextthink               = time + autocvar_g_balance_seeker_flac_lifetime + autocvar_g_balance_seeker_flac_lifetime_rand;
-       missile.solid                   = SOLID_BBOX;
-       missile.movetype                = MOVETYPE_FLY;
-       missile.projectiledeathtype = WEP_SEEKER;
-       missile.projectiledeathtype = WEP_SEEKER | HITTYPE_SECONDARY;
-       missile.flags                           = FL_PROJECTILE;
-       missile.missile_flags       = MIF_SPLASH;
-
-       // csqc projectiles
-       //missile.angles                                = vectoangles (missile.velocity);
-       //missile.scale = 0.4; // BUG: the model is too big
-
-       setorigin (missile, w_shotorg);
-       setsize (missile, '-2 -2 -2', '2 2 2');
-
-       W_SETUPPROJECTILEVELOCITY_UP(missile, g_balance_seeker_flac);
-       CSQCProjectile(missile, TRUE, PROJECTILE_FLAC, TRUE);
-
-       other = missile; MUTATOR_CALLHOOK(EditProjectile);
-}
-
-// ============================
-// Begin: Tag and rocket controllers
-// ============================
-entity Seeker_Tagged_Info(entity isowner, entity istarget)
-{
-       entity tag;
-       for(tag = world; (tag = find(tag, classname, "tag_tracker")); )
-               if ((tag.realowner == isowner) && (tag.tag_target == istarget))
-                       return tag;
-
-       return world;
-}
-
-void Seeker_Attack()
-{
-       entity tracker, closest_target;
-
-       closest_target = world;
-       for(tracker = world; (tracker = find(tracker, classname, "tag_tracker")); ) if (tracker.realowner == self)
-       {
-               if (closest_target)
-               {
-                       if (vlen(self.origin - tracker.tag_target.origin) < vlen(self.origin - closest_target.origin))
-                               closest_target = tracker.tag_target;
-               }
-               else
-                       closest_target = tracker.tag_target;
-       }
-
-       traceline(self.origin + self.view_ofs, closest_target.origin, MOVE_NOMONSTERS, self);
-       if ((!closest_target) || ((trace_fraction < 1) && (trace_ent != closest_target)))
-               closest_target = world;
-
-       Seeker_Fire_Missile('0 0 0', closest_target);
-}
-
-void Seeker_Vollycontroller_Think() // TODO: Merge this with Seeker_Attack
-{
-       float c;
-       entity oldself,oldenemy;
-       self.cnt = self.cnt - 1;
-
-       if((!(self.realowner.items & IT_UNLIMITED_AMMO) && self.realowner.ammo_rockets < autocvar_g_balance_seeker_missile_ammo) || (self.cnt <= -1) || (self.realowner.deadflag != DEAD_NO) || (self.realowner.switchweapon != WEP_SEEKER))
-       {
-               remove(self);
-               return;
-       }
-
-       self.nextthink = time + autocvar_g_balance_seeker_missile_delay * W_WeaponRateFactor();
-
-       oldself = self;
-       self = self.realowner;
-
-       oldenemy = self.enemy;
-       self.enemy = oldself.enemy;
-
-       c = mod(self.cnt, 4);
-       switch(c)
-       {
-               case 0:
-                       Seeker_Fire_Missile('-1.25 -3.75 0', self.enemy);
-                       break;
-               case 1:
-                       Seeker_Fire_Missile('+1.25 -3.75 0', self.enemy);
-                       break;
-               case 2:
-                       Seeker_Fire_Missile('-1.25 +3.75 0', self.enemy);
-                       break;
-               case 3:
-               default:
-                       Seeker_Fire_Missile('+1.25 +3.75 0', self.enemy);
-                       break;
-       }
-
-       self.enemy = oldenemy;
-       self = oldself;
-}
-
-void Seeker_Tracker_Think()
-{
-       // commit suicide if: You die OR target dies OR you switch away from the seeker OR commit suicide if lifetime is up
-       if ((self.realowner.deadflag != DEAD_NO) || (self.tag_target.deadflag != DEAD_NO) || (self.realowner.switchweapon != WEP_SEEKER)
-       || (time > self.tag_time + autocvar_g_balance_seeker_tag_tracker_lifetime))
-       {
-               if (self)
-               {
-                       WaypointSprite_Kill(self.tag_target.wps_tag_tracker);
-                       remove(self);
-               }
-               return;
-       }
-
-       // Update the think method information
-       self.nextthink = time;
-}
-
-// ============================
-// Begin: Tag projectile
-// ============================
-void Seeker_Tag_Explode ()
-{
-       //if(other==self.realowner)
-       //    return;
-       Damage_DamageInfo(self.origin, 0, 0, 0, self.velocity, WEP_SEEKER | HITTYPE_BOUNCE, other.species, self);
-
-       remove (self);
-}
-
-void Seeker_Tag_Damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-{
-       if (self.health <= 0)
-               return;
-       self.health = self.health - damage;
-       if (self.health <= 0)
-               Seeker_Tag_Explode();
-}
-
-void Seeker_Tag_Touch()
-{
-       vector dir;
-       vector org2;
-       entity e;
-
-       PROJECTILE_TOUCH;
-
-       dir     = normalize (self.realowner.origin - self.origin);
-       org2    = findbetterlocation (self.origin, 8);
-
-       te_knightspike(org2);
-
-       self.event_damage = func_null;
-       Damage_DamageInfo(self.origin, 0, 0, 0, self.velocity, WEP_SEEKER | HITTYPE_BOUNCE | HITTYPE_SECONDARY, other.species, self);
-
-       if (other.takedamage == DAMAGE_AIM && other.deadflag == DEAD_NO)
-       {
-               // check to see if this person is already tagged by me
-               entity tag = Seeker_Tagged_Info(self.realowner, other);
-
-               if (tag != world)
-               {
-                       if (other.wps_tag_tracker && (autocvar_g_balance_seeker_type == 1)) // don't attach another waypointsprite without killing the old one first
-                               WaypointSprite_Kill(other.wps_tag_tracker);
-
-                       tag.tag_time = time;
-               }
-               else
-               {
-                       //sprint(self.realowner, strcat("You just tagged ^2", other.netname, "^7 with a tracking device!\n"));
-                       e             = spawn();
-                       e.cnt         = autocvar_g_balance_seeker_missile_count;
-                       e.classname   = "tag_tracker";
-                       e.owner       = self.owner;
-                       e.realowner   = self.realowner;
-
-                       if      (autocvar_g_balance_seeker_type == 1)
-                       {
-                               e.tag_target  = other;
-                               e.tag_time    = time;
-                               e.think       = Seeker_Tracker_Think;
-                       }
-                       else
-                       {
-                               e.enemy     = other;
-                               e.think     = Seeker_Vollycontroller_Think;
-                       }
-
-                       e.nextthink   = time;
-               }
-
-               if      (autocvar_g_balance_seeker_type == 1)
-               {
-                       WaypointSprite_Spawn("tagged-target", autocvar_g_balance_seeker_tag_tracker_lifetime, 0, other, '0 0 64', self.realowner, 0, other, wps_tag_tracker, TRUE, RADARICON_TAGGED, '0.5 1 0');
-                       WaypointSprite_UpdateRule(other.wps_tag_tracker, 0, SPRITERULE_DEFAULT);
-               }
-       }
-
-       remove(self);
-       return;
-}
-
-void Seeker_Fire_Tag()
-{
-       entity missile;
-       W_DecreaseAmmo(ammo_rockets, autocvar_g_balance_seeker_tag_ammo, autocvar_g_balance_seeker_reload_ammo);
-
-       W_SetupShot_ProjectileSize (self, '-2 -2 -2', '2 2 2', FALSE, 2, "weapons/tag_fire.wav", CH_WEAPON_A, autocvar_g_balance_seeker_missile_damage * autocvar_g_balance_seeker_missile_count);
-
-       missile                 = spawn();
-       missile.owner           = missile.realowner = self;
-       missile.classname       = "seeker_tag";
-       missile.bot_dodge       = TRUE;
-       missile.bot_dodgerating = 50;
-       missile.touch           = Seeker_Tag_Touch;
-       missile.think           = SUB_Remove;
-       missile.nextthink       = time + autocvar_g_balance_seeker_tag_lifetime;
-       missile.movetype        = MOVETYPE_FLY;
-       missile.solid           = SOLID_BBOX;
-
-       missile.takedamage       = DAMAGE_YES;
-       missile.event_damage     = Seeker_Tag_Damage;
-       missile.health           = autocvar_g_balance_seeker_tag_health;
-       missile.damageforcescale = autocvar_g_balance_seeker_tag_damageforcescale;
-
-       setorigin (missile, w_shotorg);
-       setsize (missile, '-2 -2 -2', '2 2 2');
-
-       missile.flags       = FL_PROJECTILE;
-       //missile.missile_flags = MIF_..?;
-
-       missile.movetype    = MOVETYPE_FLY;
-       W_SETUPPROJECTILEVELOCITY(missile, g_balance_seeker_tag);
-       missile.angles = vectoangles (missile.velocity);
-
-       CSQCProjectile(missile, TRUE, PROJECTILE_TAG, FALSE); // has sound
-
-       other = missile; MUTATOR_CALLHOOK(EditProjectile);
-}
-
-// ============================
-// Begin: Genereal weapon functions
-// ============================
-void spawnfunc_weapon_seeker (void)
-{
-       weapon_defaultspawnfunc(WEP_SEEKER);
-}
-
-float w_seeker(float req)
-{
-       float ammo_amount;
-
-       if (req == WR_AIM)
-       {
-               if (autocvar_g_balance_seeker_type == 1)
-                       if (Seeker_Tagged_Info(self, self.enemy) != world)
-                               self.BUTTON_ATCK = bot_aim(autocvar_g_balance_seeker_missile_speed_max, 0, autocvar_g_balance_seeker_missile_lifetime, FALSE);
-                       else
-                               self.BUTTON_ATCK2 = bot_aim(autocvar_g_balance_seeker_tag_speed, 0, autocvar_g_balance_seeker_tag_lifetime, FALSE);
-               else
-                       self.BUTTON_ATCK = bot_aim(autocvar_g_balance_seeker_tag_speed, 0, autocvar_g_balance_seeker_tag_lifetime, FALSE);
-       }
-       else if (req == WR_THINK)
-       {
-               if(autocvar_g_balance_seeker_reload_ammo && self.clip_load < min(autocvar_g_balance_seeker_missile_ammo, autocvar_g_balance_seeker_tag_ammo)) // forced reload
-                       weapon_action(self.weapon, WR_RELOAD);
-
-               else if (self.BUTTON_ATCK)
-               {
-                       if (autocvar_g_balance_seeker_type == 1)
-                       {
-                               if (weapon_prepareattack(0, autocvar_g_balance_seeker_missile_refire))
-                               {
-                                       Seeker_Attack();
-                                       weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_seeker_missile_animtime, w_ready);
-                               }
-                       }
-                       else
-                       {
-                               if (weapon_prepareattack(0, autocvar_g_balance_seeker_tag_refire))
-                               {
-                                       Seeker_Fire_Tag();
-                                       weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_seeker_tag_animtime, w_ready);
-                               }
-                       }
-               }
-
-               else if (self.BUTTON_ATCK2)
-               {
-                       if (autocvar_g_balance_seeker_type == 1)
-                       {
-                               if (weapon_prepareattack(0, autocvar_g_balance_seeker_tag_refire))
-                               {
-                                       Seeker_Fire_Tag();
-                                       weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_seeker_tag_animtime, w_ready);
-                               }
-                       }
-                       else
-                       {
-                               if (weapon_prepareattack(0, autocvar_g_balance_seeker_flac_refire))
-                               {
-                                       Seeker_Fire_Flac();
-                                       weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_seeker_flac_animtime, w_ready);
-                               }
-                       }
-               }
-       }
-       else if (req == WR_PRECACHE)
-       {
-               precache_model ("models/weapons/g_seeker.md3");
-               precache_model ("models/weapons/v_seeker.md3");
-               precache_model ("models/weapons/h_seeker.iqm");
-               precache_sound ("weapons/tag_fire.wav");
-               precache_sound ("weapons/flac_fire.wav");
-               precache_sound ("weapons/seeker_fire.wav");
-               //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else
-       }
-       else if (req == WR_SETUP)
-       {
-               weapon_setup(WEP_SEEKER);
-               self.current_ammo = ammo_rockets;
-       }
-       else if (req == WR_CHECKAMMO1)
-       {
-               if (autocvar_g_balance_seeker_type == 1)
-               {
-                       ammo_amount = self.ammo_rockets >= autocvar_g_balance_seeker_missile_ammo;
-                       ammo_amount += self.(weapon_load[WEP_SEEKER]) >= autocvar_g_balance_seeker_missile_ammo;
-               }
-               else
-               {
-                       ammo_amount = self.ammo_rockets >= autocvar_g_balance_seeker_tag_ammo;
-                       ammo_amount += self.(weapon_load[WEP_SEEKER]) >= autocvar_g_balance_seeker_tag_ammo;
-               }
-
-               return ammo_amount;
-       }
-       else if (req == WR_CHECKAMMO2)
-       {
-               if (autocvar_g_balance_seeker_type == 1)
-               {
-                       ammo_amount = self.ammo_rockets >= autocvar_g_balance_seeker_tag_ammo;
-                       ammo_amount += self.(weapon_load[WEP_SEEKER]) >= autocvar_g_balance_seeker_tag_ammo;
-               }
-               else
-               {
-                       ammo_amount = self.ammo_rockets >= autocvar_g_balance_seeker_flac_ammo;
-                       ammo_amount += self.(weapon_load[WEP_SEEKER]) >= autocvar_g_balance_seeker_flac_ammo;
-               }
-
-               return ammo_amount;
-       }
-       else if (req == WR_RELOAD)
-       {
-               W_Reload(min(autocvar_g_balance_seeker_missile_ammo, autocvar_g_balance_seeker_tag_ammo), autocvar_g_balance_seeker_reload_ammo, autocvar_g_balance_seeker_reload_time, "weapons/reload.wav");
-       }
-       else if (req == WR_SUICIDEMESSAGE)
-       {
-               return WEAPON_SEEKER_SUICIDE;
-       }
-       else if (req == WR_KILLMESSAGE)
-       {
-               if(w_deathtype & HITTYPE_SECONDARY)
-                       return WEAPON_SEEKER_MURDER_TAG;
-               else
-                       return WEAPON_SEEKER_MURDER_SPRAY;
-       }
-       return TRUE;
-}
-#endif
-#ifdef CSQC
-float w_seeker(float req)
-{
-       if(req == WR_IMPACTEFFECT)
-       {
-               vector org2;
-               org2 = w_org + w_backoff * 6;
-               if(w_deathtype & HITTYPE_BOUNCE)
-               {
-                       if(w_deathtype & HITTYPE_SECONDARY)
-                       {
-                               if(!w_issilent)
-                                       sound(self, CH_SHOTS, "weapons/tag_impact.wav", 1, ATTEN_NORM);
-                       }
-                       else
-                       {
-                               pointparticles(particleeffectnum("hagar_explode"), org2, '0 0 0', 1);
-                               if(!w_issilent)
-                               {
-                                       if (w_random<0.15)
-                                               sound(self, CH_SHOTS, "weapons/tagexp1.wav", 1, ATTEN_NORM);
-                                       else if (w_random<0.7)
-                                               sound(self, CH_SHOTS, "weapons/tagexp2.wav", 1, ATTEN_NORM);
-                                       else
-                                               sound(self, CH_SHOTS, "weapons/tagexp3.wav", 1, ATTEN_NORM);
-                               }
-                       }
-               }
-               else
-               {
-                       pointparticles(particleeffectnum("hagar_explode"), org2, '0 0 0', 1);
-                       if(!w_issilent)
-                       {
-                               if (w_random<0.15)
-                                       sound(self, CH_SHOTS, "weapons/seekerexp1.wav", 1, ATTEN_NORM);
-                               else if (w_random<0.7)
-                                       sound(self, CH_SHOTS, "weapons/seekerexp2.wav", 1, ATTEN_NORM);
-                               else
-                                       sound(self, CH_SHOTS, "weapons/seekerexp3.wav", 1, ATTEN_NORM);
-                       }
-               }
-       }
-       else if(req == WR_PRECACHE)
-       {
-               precache_sound("weapons/seekerexp1.wav");
-               precache_sound("weapons/seekerexp2.wav");
-               precache_sound("weapons/seekerexp3.wav");
-               precache_sound("weapons/tagexp1.wav");
-               precache_sound("weapons/tagexp2.wav");
-               precache_sound("weapons/tagexp3.wav");
-               precache_sound("weapons/tag_impact.wav");
-       }
-       return TRUE;
-}
-#endif
-#endif
diff --git a/qcsrc/server/w_shotgun.qc b/qcsrc/server/w_shotgun.qc
deleted file mode 100644 (file)
index 2fb6214..0000000
+++ /dev/null
@@ -1,289 +0,0 @@
-#ifdef REGISTER_WEAPON
-REGISTER_WEAPON(
-/* WEP_##id  */ SHOTGUN,
-/* function  */ w_shotgun,
-/* ammotype  */ IT_SHELLS,
-/* impulse   */ 2,
-/* flags     */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN,
-/* rating    */ BOT_PICKUP_RATING_LOW,
-/* model     */ "shotgun",
-/* shortname */ "shotgun",
-/* fullname  */ _("Shotgun")
-);
-#else
-#ifdef SVQC
-
-void W_Shotgun_Attack (void)
-{
-       float   sc;
-       float   ammoamount;
-       float   bullets;
-       float   d;
-       float   f;
-       float   spread;
-       float   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, TRUE, 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, 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_WEAPON_A, "weapons/shotgun_melee.wav", VOL_BASE, ATTEN_NORM);
-       weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_shotgun_secondary_animtime, w_ready);
-
-       entity meleetemp;
-       meleetemp = spawn();
-       meleetemp.realowner = self;
-       meleetemp.think = shotgun_meleethink;
-       meleetemp.nextthink = time + autocvar_g_balance_shotgun_secondary_melee_delay * W_WeaponRateFactor();
-       W_SetupShot_Range(self, TRUE, 0, "", 0, autocvar_g_balance_shotgun_secondary_damage, autocvar_g_balance_shotgun_secondary_melee_range);
-}
-
-void spawnfunc_weapon_shotgun(); // defined in t_items.qc
-
-.float shotgun_primarytime;
-
-float w_shotgun(float req)
-{
-       float ammo_amount;
-       if (req == WR_AIM)
-               if(vlen(self.origin-self.enemy.origin) <= autocvar_g_balance_shotgun_secondary_melee_range)
-                       self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE);
-               else
-                       self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE);
-
-       else if (req == WR_THINK)
-       {
-               if(autocvar_g_balance_shotgun_reload_ammo && self.clip_load < autocvar_g_balance_shotgun_primary_ammo) // forced reload
-               {
-                       // don't force reload an empty shotgun if its melee attack is active
-                       if (!(autocvar_g_balance_shotgun_secondary && self.ammo_shells < autocvar_g_balance_shotgun_primary_ammo))
-                               weapon_action(self.weapon, WR_RELOAD);
-               }
-               else
-               {
-                       if (self.BUTTON_ATCK)
-                       {
-                               if (time >= self.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary
-                               {
-                                       if(weapon_prepareattack(0, autocvar_g_balance_shotgun_primary_animtime))
-                                       {
-                                               W_Shotgun_Attack();
-                                               self.shotgun_primarytime = time + autocvar_g_balance_shotgun_primary_refire * W_WeaponRateFactor();
-                                               weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_shotgun_primary_animtime, w_ready);
-                                       }
-                               }
-                       }
-               }
-               if (self.clip_load >= 0) // we are not currently reloading
-               if (!self.crouch) // no crouchmelee please
-               if (self.BUTTON_ATCK2 && autocvar_g_balance_shotgun_secondary)
-               if (weapon_prepareattack(1, autocvar_g_balance_shotgun_secondary_refire))
-               {
-                       // attempt forcing playback of the anim by switching to another anim (that we never play) here...
-                       weapon_thinkf(WFRAME_FIRE1, 0, W_Shotgun_Attack2);
-               }
-       }
-       else if (req == WR_PRECACHE)
-       {
-               precache_model ("models/uziflash.md3");
-               precache_model ("models/weapons/g_shotgun.md3");
-               precache_model ("models/weapons/v_shotgun.md3");
-               precache_model ("models/weapons/h_shotgun.iqm");
-               precache_sound ("misc/itempickup.wav");
-               precache_sound ("weapons/shotgun_fire.wav");
-               precache_sound ("weapons/shotgun_melee.wav");
-               //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else
-       }
-       else if (req == WR_SETUP)
-       {
-               weapon_setup(WEP_SHOTGUN);
-               self.current_ammo = ammo_shells;
-       }
-       else if (req == WR_CHECKAMMO1)
-       {
-               ammo_amount = self.ammo_shells >= autocvar_g_balance_shotgun_primary_ammo;
-               ammo_amount += self.(weapon_load[WEP_SHOTGUN]) >= autocvar_g_balance_shotgun_primary_ammo;
-               return ammo_amount;
-       }
-       else if (req == WR_CHECKAMMO2)
-       {
-               // melee attack is always available
-               return TRUE;
-       }
-       else if (req == WR_RELOAD)
-       {
-               W_Reload(autocvar_g_balance_shotgun_primary_ammo, autocvar_g_balance_shotgun_reload_ammo, autocvar_g_balance_shotgun_reload_time, "weapons/reload.wav");
-       }
-       else if (req == WR_SUICIDEMESSAGE)
-       {
-               return WEAPON_THINKING_WITH_PORTALS;
-       }
-       else if (req == WR_KILLMESSAGE)
-       {
-               if(w_deathtype & HITTYPE_SECONDARY)
-                       return WEAPON_SHOTGUN_MURDER_SLAP;
-               else
-                       return WEAPON_SHOTGUN_MURDER;
-       }
-       return TRUE;
-}
-#endif
-#ifdef CSQC
-.float prevric;
-float w_shotgun(float req)
-{
-       if(req == WR_IMPACTEFFECT)
-       {
-               vector org2;
-               org2 = w_org + w_backoff * 2;
-               pointparticles(particleeffectnum("shotgun_impact"), org2, w_backoff * 1000, 1);
-               if(!w_issilent && time - self.prevric > 0.25)
-               {
-                       if(w_random < 0.0165)
-                               sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTEN_NORM);
-                       else if(w_random < 0.033)
-                               sound(self, CH_SHOTS, "weapons/ric2.wav", VOL_BASE, ATTEN_NORM);
-                       else if(w_random < 0.05)
-                               sound(self, CH_SHOTS, "weapons/ric3.wav", VOL_BASE, ATTEN_NORM);
-                       self.prevric = time;
-               }
-       }
-       else if(req == WR_PRECACHE)
-       {
-               precache_sound("weapons/ric1.wav");
-               precache_sound("weapons/ric2.wav");
-               precache_sound("weapons/ric3.wav");
-       }
-       return TRUE;
-}
-#endif
-#endif
diff --git a/qcsrc/server/w_tuba.qc b/qcsrc/server/w_tuba.qc
deleted file mode 100644 (file)
index f70aa04..0000000
+++ /dev/null
@@ -1,466 +0,0 @@
-#ifdef REGISTER_WEAPON
-REGISTER_WEAPON(
-/* WEP_##id  */ TUBA,
-/* function  */ w_tuba,
-/* ammotype  */ 0,
-/* impulse   */ 1,
-/* flags     */ WEP_FLAG_HIDDEN | WEP_TYPE_SPLASH,
-/* rating    */ BOT_PICKUP_RATING_MID,
-/* model     */ "tuba",
-/* shortname */ "tuba",
-/* xgettext:no-c-format */
-/* fullname  */ _("@!#%'n Tuba")
-);
-#else
-#ifdef SVQC
-//#define TUBA_NOTE(n) strcat("weapons/tuba_note", ftos(n), ".wav")
-.entity tuba_note;
-.float tuba_smoketime;
-.float tuba_instrument;
-
-#define MAX_TUBANOTES 32
-.float tuba_lastnotes_last;
-.float tuba_lastnotes_cnt; // over
-.vector tuba_lastnotes[MAX_TUBANOTES];
-
-float W_Tuba_HasPlayed(entity pl, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo)
-{
-       float i, j, mmin, mmax, nolength;
-       float n = tokenize_console(melody);
-       if(n > pl.tuba_lastnotes_cnt)
-               return FALSE;
-       float pitchshift = 0;
-
-       if(instrument >= 0)
-               if(pl.tuba_instrument != instrument)
-                       return FALSE;
-
-       // verify notes...
-       nolength = FALSE;
-       for(i = 0; i < n; ++i)
-       {
-               vector v = pl.(tuba_lastnotes[mod(pl.tuba_lastnotes_last - i + MAX_TUBANOTES, MAX_TUBANOTES)]);
-               float ai = stof(argv(n - i - 1));
-               float np = floor(ai);
-               if(ai == np)
-                       nolength = TRUE;
-               // n counts the last played notes BACKWARDS
-               // _x is start
-               // _y is end
-               // _z is note pitch
-               if(ignorepitch && i == 0)
-               {
-                       pitchshift = np - v_z;
-               }
-               else
-               {
-                       if(v_z + pitchshift != np)
-                               return FALSE;
-               }
-       }
-
-       // now we know the right NOTES were played
-       if(!nolength)
-       {
-               // verify rhythm...
-               float ti = 0;
-               if(maxtempo > 0)
-                       mmin = 240 / maxtempo; // 60 = "0.25 means 1 sec", at 120 0.5 means 1 sec, at 240 1 means 1 sec
-               else
-                       mmin = 0;
-               if(mintempo > 0)
-                       mmax = 240 / mintempo; // 60 = "0.25 means 1 sec", at 120 0.5 means 1 sec, at 240 1 means 1 sec
-               else
-                       mmax = 240; // you won't try THAT hard... (tempo 1)
-               //print(sprintf("initial tempo rules: %f %f\n", mmin, mmax));
-
-               for(i = 0; i < n; ++i)
-               {
-                       vector vi = pl.(tuba_lastnotes[mod(pl.tuba_lastnotes_last - i + MAX_TUBANOTES, MAX_TUBANOTES)]);
-                       float ai = stof(argv(n - i - 1));
-                       ti -= 1 / (ai - floor(ai));
-                       float tj = ti;
-                       for(j = i+1; j < n; ++j)
-                       {
-                               vector vj = pl.(tuba_lastnotes[mod(pl.tuba_lastnotes_last - j + MAX_TUBANOTES, MAX_TUBANOTES)]);
-                               float aj = stof(argv(n - j - 1));
-                               tj -= (aj - floor(aj));
-
-                               // note i should be at m*ti+b
-                               // note j should be at m*tj+b
-                               // so:
-                               // we have a LINE l, so that
-                               // vi_x <= l(ti) <= vi_y
-                               // vj_x <= l(tj) <= vj_y
-                               // what is m?
-
-                               // vi_x <= vi_y <= vj_x <= vj_y
-                               // ti <= tj
-                               //print(sprintf("first note: %f to %f, should be %f\n", vi_x, vi_y, ti));
-                               //print(sprintf("second note: %f to %f, should be %f\n", vj_x, vj_y, tj));
-                               //print(sprintf("m1 = %f\n", (vi_x - vj_y) / (ti - tj)));
-                               //print(sprintf("m2 = %f\n", (vi_y - vj_x) / (ti - tj)));
-                               mmin = max(mmin, (vi_x - vj_y) / (ti - tj)); // lower bound
-                               mmax = min(mmax, (vi_y - vj_x) / (ti - tj)); // upper bound
-                       }
-               }
-
-               if(mmin > mmax) // rhythm fail
-                       return FALSE;
-       }
-
-       pl.tuba_lastnotes_cnt = 0;
-
-       return TRUE;
-}
-
-void W_Tuba_NoteOff()
-{
-       // we have a note:
-       //   on: self.spawnshieldtime
-       //   off: time
-       //   note: self.cnt
-       if(self.owner.tuba_note == self)
-       {
-               self.owner.tuba_lastnotes_last = mod(self.owner.tuba_lastnotes_last + 1, MAX_TUBANOTES);
-               self.owner.(tuba_lastnotes[self.owner.tuba_lastnotes_last]) = eX * self.spawnshieldtime + eY * time + eZ * self.cnt;
-               self.owner.tuba_note = world;
-               self.owner.tuba_lastnotes_cnt = bound(0, self.owner.tuba_lastnotes_cnt + 1, MAX_TUBANOTES);
-
-               string s;
-               s = trigger_magicear_processmessage_forallears(self.owner, 0, world, string_null);
-               if(s != "")
-               {
-                       // simulate a server message
-                       switch(self.tuba_instrument)
-                       {
-                               default:
-                               case 0: // Tuba
-                                       bprint(strcat("\{1}\{13}* ^3", self.owner.netname, "^3 played on the @!#%'n Tuba: ^7", s, "\n"));
-                                       break;
-                               case 1:
-                                       bprint(strcat("\{1}\{13}* ^3", self.owner.netname, "^3 played on the @!#%'n Accordeon: ^7", s, "\n"));
-                                       break;
-                               case 2:
-                                       bprint(strcat("\{1}\{13}* ^3", self.owner.netname, "^3 played on the @!#%'n Klein Bottle: ^7", s, "\n"));
-                                       break;
-                       }
-               }
-       }
-       remove(self);
-}
-
-float Tuba_GetNote(entity pl, float hittype)
-{
-       float note;
-       float movestate;
-       movestate = 5;
-       if(pl.movement_x < 0) movestate -= 3;
-       if(pl.movement_x > 0) movestate += 3;
-       if(pl.movement_y < 0) movestate -= 1;
-       if(pl.movement_y > 0) movestate += 1;
-#ifdef GMQCC
-       note = 0;
-#endif
-       switch(movestate)
-       {
-       // layout: originally I wanted
-       //   eb e  e#=f
-       //   B  c  d
-       //   Gb G  G#
-       // but then you only use forward and right key. So to make things more
-       // interesting, I swapped B with e#. Har har har...
-       //   eb e  B
-       // f=e# c  d
-       //   Gb G  G#
-               case 1: note = -6; break; // Gb
-               case 2: note = -5; break; // G
-               case 3: note = -4; break; // G#
-               case 4: note = +5; break; // e#
-               default:
-               case 5: note =  0; break; // c
-               case 6: note = +2; break; // d
-               case 7: note = +3; break; // eb
-               case 8: note = +4; break; // e
-               case 9: note = -1; break; // B
-       }
-       if(pl.BUTTON_CROUCH)
-               note -= 12;
-       if(pl.BUTTON_JUMP)
-               note += 12;
-       if(hittype & HITTYPE_SECONDARY)
-               note += 7;
-
-       // we support two kinds of tubas, those tuned in Eb and those tuned in C
-       // kind of tuba currently is player slot number, or team number if in
-       // teamplay
-       // that way, holes in the range of notes are "plugged"
-       if(teamplay)
-       {
-               if(pl.team == NUM_TEAM_2 || pl.team == NUM_TEAM_4)
-                       note += 3;
-       }
-       else
-       {
-               if(pl.clientcolors & 1)
-                       note += 3;
-       }
-
-       // total range of notes:
-       //                       0
-       //                 ***  ** ****
-       //                        ***  ** ****
-       //     ***  ** ****
-       //            ***  ** ****
-       //     ***  ********************* ****
-       //     -18.........................+12
-       //        ***  ********************* ****
-       //     -18............................+15
-       //     with jump: ... +24
-       //     ... +27
-       return note;
-}
-
-float W_Tuba_NoteSendEntity(entity to, float sf)
-{
-       float f;
-
-       msg_entity = to;
-       if(!sound_allowed(MSG_ONE, self.realowner))
-               return FALSE;
-
-       WriteByte(MSG_ENTITY, ENT_CLIENT_TUBANOTE);
-       WriteByte(MSG_ENTITY, sf);
-       if(sf & 1)
-       {
-               WriteChar(MSG_ENTITY, self.cnt);
-               f = 0;
-               if(self.realowner != to)
-                       f |= 1;
-               f |= 2 * self.tuba_instrument;
-               WriteByte(MSG_ENTITY, f);
-       }
-       if(sf & 2)
-       {
-               WriteCoord(MSG_ENTITY, self.origin_x);
-               WriteCoord(MSG_ENTITY, self.origin_y);
-               WriteCoord(MSG_ENTITY, self.origin_z);
-       }
-       return TRUE;
-}
-
-void W_Tuba_NoteThink()
-{
-       float dist_mult;
-       float vol0, vol1;
-       vector dir0, dir1;
-       vector v;
-       entity e;
-       if(time > self.teleport_time)
-       {
-               W_Tuba_NoteOff();
-               return;
-       }
-       self.nextthink = time;
-       dist_mult = autocvar_g_balance_tuba_attenuation / autocvar_snd_soundradius;
-       FOR_EACH_REALCLIENT(e)
-       if(e != self.realowner)
-       {
-               v = self.origin - (e.origin + e.view_ofs);
-               vol0 = max(0, 1 - vlen(v) * dist_mult);
-               dir0 = normalize(v);
-               v = self.realowner.origin - (e.origin + e.view_ofs);
-               vol1 = max(0, 1 - vlen(v) * dist_mult);
-               dir1 = normalize(v);
-               if(fabs(vol0 - vol1) > 0.005) // 0.5 percent change in volume
-               {
-                       setorigin(self, self.realowner.origin);
-                       self.SendFlags |= 2;
-                       break;
-               }
-               if(dir0 * dir1 < 0.9994) // 2 degrees change in angle
-               {
-                       setorigin(self, self.realowner.origin);
-                       self.SendFlags |= 2;
-                       break;
-               }
-       }
-}
-
-void W_Tuba_NoteOn(float hittype)
-{
-       vector o;
-       float n;
-
-       W_SetupShot(self, FALSE, 2, "", 0, autocvar_g_balance_tuba_damage);
-
-       n = Tuba_GetNote(self, hittype);
-
-       hittype = 0;
-       if(self.tuba_instrument & 1)
-               hittype |= HITTYPE_SECONDARY;
-       if(self.tuba_instrument & 2)
-               hittype |= HITTYPE_BOUNCE;
-
-       if(self.tuba_note)
-       {
-               if(self.tuba_note.cnt != n || self.tuba_note.tuba_instrument != self.tuba_instrument)
-               {
-                       entity oldself = self;
-                       self = self.tuba_note;
-                       W_Tuba_NoteOff();
-                       self = oldself;
-               }
-       }
-
-       if (!self.tuba_note)
-       {
-               self.tuba_note = spawn();
-               self.tuba_note.owner = self.tuba_note.realowner = self;
-               self.tuba_note.cnt = n;
-               self.tuba_note.tuba_instrument = self.tuba_instrument;
-               self.tuba_note.think = W_Tuba_NoteThink;
-               self.tuba_note.nextthink = time;
-               self.tuba_note.spawnshieldtime = time;
-               Net_LinkEntity(self.tuba_note, FALSE, 0, W_Tuba_NoteSendEntity);
-       }
-
-       self.tuba_note.teleport_time = time + autocvar_g_balance_tuba_refire * 2 * W_WeaponRateFactor(); // so it can get prolonged safely
-
-       //sound(self, c, TUBA_NOTE(n), bound(0, VOL_BASE * cvar("g_balance_tuba_volume"), 1), autocvar_g_balance_tuba_attenuation);
-       RadiusDamage(self, self, autocvar_g_balance_tuba_damage, autocvar_g_balance_tuba_edgedamage, autocvar_g_balance_tuba_radius, world, autocvar_g_balance_tuba_force, hittype | WEP_TUBA, world);
-
-       o = gettaginfo(self.exteriorweaponentity, 0);
-       if(time > self.tuba_smoketime)
-       {
-               pointparticles(particleeffectnum("smoke_ring"), o + v_up * 45 + v_right * -6 + v_forward * 8, v_up * 100, 1);
-               self.tuba_smoketime = time + 0.25;
-       }
-}
-
-void spawnfunc_weapon_tuba (void)
-{
-       weapon_defaultspawnfunc(WEP_TUBA);
-}
-
-float w_tuba(float req)
-{
-       if (req == WR_AIM)
-       {
-               // bots cannot play the Tuba well yet
-               // I think they should start with the recorder first
-               if(vlen(self.origin - self.enemy.origin) < autocvar_g_balance_tuba_radius)
-               {
-                       if(random() > 0.5)
-                               self.BUTTON_ATCK = 1;
-                       else
-                               self.BUTTON_ATCK2 = 1;
-               }
-       }
-       else if (req == WR_THINK)
-       {
-               if (self.BUTTON_ATCK)
-               if (weapon_prepareattack(0, autocvar_g_balance_tuba_refire))
-               {
-                       W_Tuba_NoteOn(0);
-                       //weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_tuba_animtime, w_ready);
-                       weapon_thinkf(WFRAME_IDLE, autocvar_g_balance_tuba_animtime, w_ready);
-               }
-               if (self.BUTTON_ATCK2)
-               if (weapon_prepareattack(1, autocvar_g_balance_tuba_refire))
-               {
-                       W_Tuba_NoteOn(HITTYPE_SECONDARY);
-                       //weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_tuba_animtime, w_ready);
-                       weapon_thinkf(WFRAME_IDLE, autocvar_g_balance_tuba_animtime, w_ready);
-               }
-               if(self.tuba_note)
-               {
-                       if(!self.BUTTON_ATCK && !self.BUTTON_ATCK2)
-                       {
-                               entity oldself = self;
-                               self = self.tuba_note;
-                               W_Tuba_NoteOff();
-                               self = oldself;
-                       }
-               }
-       }
-       else if (req == WR_PRECACHE)
-       {
-               precache_model ("models/weapons/g_tuba.md3");
-               precache_model ("models/weapons/v_tuba.md3");
-               precache_model ("models/weapons/h_tuba.iqm");
-               precache_model ("models/weapons/v_akordeon.md3");
-               precache_model ("models/weapons/h_akordeon.iqm");
-               precache_model ("models/weapons/v_kleinbottle.md3");
-               precache_model ("models/weapons/h_kleinbottle.iqm");
-
-               //float i;
-               //for(i = -18; i <= +27; ++i)
-               //      precache_sound(TUBA_NOTE(i));
-       }
-       else if (req == WR_SETUP)
-       {
-               weapon_setup(WEP_TUBA);
-               self.current_ammo = ammo_none;
-               self.tuba_instrument = 0;
-       }
-       else if (req == WR_RELOAD)
-       {
-               // switch to alternate instruments :)
-               if(self.weaponentity.state == WS_READY)
-               {
-                       switch(self.tuba_instrument)
-                       {
-                               case 0:
-                                       self.tuba_instrument = 1;
-                                       self.weaponname = "akordeon";
-                                       break;
-                               case 1:
-                                       self.tuba_instrument = 2;
-                                       self.weaponname = "kleinbottle";
-                                       break;
-                               case 2:
-                                       self.tuba_instrument = 0;
-                                       self.weaponname = "tuba";
-                                       break;
-                       }
-                       W_SetupShot(self, FALSE, 0, "", 0, 0);
-                       pointparticles(particleeffectnum("teleport"), w_shotorg, '0 0 0', 1);
-                       self.weaponentity.state = WS_INUSE;
-                       weapon_thinkf(WFRAME_RELOAD, 0.5, w_ready);
-               }
-       }
-       else if (req == WR_CHECKAMMO1)
-               return TRUE; // TODO use fuel?
-       else if (req == WR_CHECKAMMO2)
-               return TRUE; // TODO use fuel?
-       else if (req == WR_SUICIDEMESSAGE)
-       {
-               if(w_deathtype & HITTYPE_BOUNCE)
-                       return WEAPON_KLEINBOTTLE_SUICIDE;
-               else if(w_deathtype & HITTYPE_SECONDARY)
-                       return WEAPON_ACCORDEON_SUICIDE;
-               else
-                       return WEAPON_TUBA_SUICIDE;
-       }
-       else if (req == WR_KILLMESSAGE)
-       {
-               if(w_deathtype & HITTYPE_BOUNCE)
-                       return WEAPON_KLEINBOTTLE_MURDER;
-               else if(w_deathtype & HITTYPE_SECONDARY)
-                       return WEAPON_ACCORDEON_MURDER;
-               else
-                       return WEAPON_TUBA_MURDER;
-       }
-       return TRUE;
-}
-#endif
-#ifdef CSQC
-float w_tuba(float req)
-{
-       // nothing to do here; particles of tuba are handled differently
-
-       return TRUE;
-}
-#endif
-#endif
diff --git a/qcsrc/server/w_uzi.qc b/qcsrc/server/w_uzi.qc
deleted file mode 100644 (file)
index 5fd890a..0000000
+++ /dev/null
@@ -1,341 +0,0 @@
-#ifdef REGISTER_WEAPON
-REGISTER_WEAPON(
-/* WEP_##id  */ UZI,
-/* function  */ w_uzi,
-/* ammotype  */ IT_NAILS,
-/* impulse   */ 3,
-/* flags     */ WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN,
-/* rating    */ BOT_PICKUP_RATING_MID,
-/* model     */ "uzi",
-/* shortname */ "uzi",
-/* fullname  */ _("Machine Gun")
-);
-#else
-#ifdef SVQC
-
-// leilei's fancy muzzleflash stuff
-void UZI_Flash_Go()
-{
-       self.frame = self.frame + 2;
-       self.scale = self.scale * 0.5;
-       self.alpha = self.alpha - 0.25;
-       self.nextthink = time + 0.05;
-
-       if (self.alpha <= 0)
-       {
-               self.think = SUB_Remove;
-               self.nextthink = time;
-               self.realowner.muzzle_flash = world;
-               return;
-       }
-
-}
-
-void UziFlash()
-{
-       if (self.muzzle_flash == world)
-               self.muzzle_flash = spawn();
-
-       // muzzle flash for 1st person view
-       setmodel(self.muzzle_flash, "models/uziflash.md3"); // precision set below
-
-       self.muzzle_flash.scale = 0.75;
-       self.muzzle_flash.think = UZI_Flash_Go;
-       self.muzzle_flash.nextthink = time + 0.02;
-       self.muzzle_flash.frame = 2;
-       self.muzzle_flash.alpha = 0.75;
-       self.muzzle_flash.angles_z = random() * 180;
-       self.muzzle_flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
-       self.muzzle_flash.owner = self.muzzle_flash.realowner = self;
-}
-
-void W_UZI_Attack (float deathtype)
-{
-       W_SetupShot (self, TRUE, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, ((self.misc_bulletcounter == 1) ? autocvar_g_balance_uzi_first_damage : autocvar_g_balance_uzi_sustained_damage));
-       if (!autocvar_g_norecoil)
-       {
-               self.punchangle_x = random () - 0.5;
-               self.punchangle_y = random () - 0.5;
-       }
-
-       // this attack_finished just enforces a cooldown at the end of a burst
-       ATTACK_FINISHED(self) = time + autocvar_g_balance_uzi_first_refire * W_WeaponRateFactor();
-
-       if (self.misc_bulletcounter == 1)
-               fireBallisticBullet(w_shotorg, w_shotdir, autocvar_g_balance_uzi_first_spread, autocvar_g_balance_uzi_speed, 5, autocvar_g_balance_uzi_first_damage, autocvar_g_balance_uzi_first_force, deathtype, 0, autocvar_g_balance_uzi_bulletconstant);
-       else
-               fireBallisticBullet(w_shotorg, w_shotdir, autocvar_g_balance_uzi_sustained_spread, autocvar_g_balance_uzi_speed, 5, autocvar_g_balance_uzi_sustained_damage, autocvar_g_balance_uzi_sustained_force, deathtype, 0, autocvar_g_balance_uzi_bulletconstant);
-       endFireBallisticBullet();
-
-       pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
-       UziFlash();
-       W_AttachToShotorg(self.muzzle_flash, '5 0 0');
-
-       // casing code
-       if (autocvar_g_casings >= 2)
-               SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, self);
-
-       if (self.misc_bulletcounter == 1)
-               W_DecreaseAmmo(ammo_nails, autocvar_g_balance_uzi_first_ammo, autocvar_g_balance_uzi_reload_ammo);
-       else
-               W_DecreaseAmmo(ammo_nails, autocvar_g_balance_uzi_sustained_ammo, autocvar_g_balance_uzi_reload_ammo);
-}
-
-// weapon frames
-void uzi_fire1_02()
-{
-       if(self.weapon != self.switchweapon) // abort immediately if switching
-       {
-               w_ready();
-               return;
-       }
-       if (self.BUTTON_ATCK)
-       {
-               if (!weapon_action(self.weapon, WR_CHECKAMMO2))
-               if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
-               {
-                       W_SwitchWeapon_Force(self, w_getbestweapon(self));
-                       w_ready();
-                       return;
-               }
-               self.misc_bulletcounter = self.misc_bulletcounter + 1;
-               W_UZI_Attack(WEP_UZI);
-               weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_uzi_sustained_refire, uzi_fire1_02);
-       }
-       else
-               weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_uzi_sustained_refire, w_ready);
-}
-
-
-void uzi_mode1_fire_auto()
-{
-       float uzi_spread;
-
-       if (!self.BUTTON_ATCK)
-       {
-               w_ready();
-               return;
-       }
-
-       if (!weapon_action(self.weapon, WR_CHECKAMMO1))
-       if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
-       {
-               W_SwitchWeapon_Force(self, w_getbestweapon(self));
-               w_ready();
-               return;
-       }
-
-       W_DecreaseAmmo(ammo_nails, autocvar_g_balance_uzi_sustained_ammo, autocvar_g_balance_uzi_reload_ammo);
-
-       W_SetupShot (self, TRUE, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, autocvar_g_balance_uzi_sustained_damage);
-       if (!autocvar_g_norecoil)
-       {
-               self.punchangle_x = random () - 0.5;
-               self.punchangle_y = random () - 0.5;
-       }
-
-       uzi_spread = bound(autocvar_g_balance_uzi_spread_min, autocvar_g_balance_uzi_spread_min + (autocvar_g_balance_uzi_spread_add * self.misc_bulletcounter), autocvar_g_balance_uzi_spread_max);
-       fireBallisticBullet(w_shotorg, w_shotdir, uzi_spread, autocvar_g_balance_uzi_speed, 5, autocvar_g_balance_uzi_sustained_damage, autocvar_g_balance_uzi_sustained_force, WEP_UZI, 0, autocvar_g_balance_uzi_bulletconstant);
-       endFireBallisticBullet();
-
-       self.misc_bulletcounter = self.misc_bulletcounter + 1;
-
-       pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
-       UziFlash();
-       W_AttachToShotorg(self.muzzle_flash, '5 0 0');
-
-       if (autocvar_g_casings >= 2) // casing code
-               SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, self);
-
-       ATTACK_FINISHED(self) = time + autocvar_g_balance_uzi_first_refire * W_WeaponRateFactor();
-       weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_uzi_sustained_refire, uzi_mode1_fire_auto);
-}
-
-void uzi_mode1_fire_burst()
-{
-       W_SetupShot (self, TRUE, 0, "weapons/uzi_fire.wav", CH_WEAPON_A, autocvar_g_balance_uzi_sustained_damage);
-       if (!autocvar_g_norecoil)
-       {
-               self.punchangle_x = random () - 0.5;
-               self.punchangle_y = random () - 0.5;
-       }
-
-       fireBallisticBullet(w_shotorg, w_shotdir, autocvar_g_balance_uzi_burst_spread, autocvar_g_balance_uzi_speed, 5, autocvar_g_balance_uzi_sustained_damage, autocvar_g_balance_uzi_sustained_force, WEP_UZI, 0, autocvar_g_balance_uzi_bulletconstant);
-       endFireBallisticBullet();
-
-
-       pointparticles(particleeffectnum("uzi_muzzleflash"), w_shotorg, w_shotdir * 1000, 1);
-
-       UziFlash();
-       W_AttachToShotorg(self.muzzle_flash, '5 0 0');
-
-       if (autocvar_g_casings >= 2) // casing code
-               SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, self);
-
-       self.misc_bulletcounter = self.misc_bulletcounter + 1;
-       if (self.misc_bulletcounter == 0)
-       {
-               ATTACK_FINISHED(self) = time + autocvar_g_balance_uzi_burst_refire2 * W_WeaponRateFactor();
-               weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_uzi_burst_animtime, w_ready);
-       }
-       else
-       {
-               weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_uzi_burst_refire, uzi_mode1_fire_burst);
-       }
-
-}
-
-void spawnfunc_weapon_machinegun(); // defined in t_items.qc
-
-float w_uzi(float req)
-{
-       float ammo_amount;
-       if (req == WR_AIM)
-               if(vlen(self.origin-self.enemy.origin) < 3000 - bound(0, skill, 10) * 200)
-                       self.BUTTON_ATCK = bot_aim(1000000, 0, 0.001, FALSE);
-               else
-               {
-                       self.BUTTON_ATCK2 = bot_aim(1000000, 0, 0.001, FALSE);
-               }
-       else if (req == WR_THINK)
-       {
-               if(autocvar_g_balance_uzi_reload_ammo && self.clip_load < min(max(autocvar_g_balance_uzi_sustained_ammo, autocvar_g_balance_uzi_first_ammo), autocvar_g_balance_uzi_burst_ammo)) // forced reload
-                       weapon_action(self.weapon, WR_RELOAD);
-               else if(autocvar_g_balance_uzi_mode == 1)
-               {
-                       if (self.BUTTON_ATCK)
-                       if (weapon_prepareattack(0, 0))
-                       {
-                               self.misc_bulletcounter = 0;
-                               uzi_mode1_fire_auto();
-                       }
-
-                       if(self.BUTTON_ATCK2)
-                       if(weapon_prepareattack(1, 0))
-                       {
-                               if (!weapon_action(self.weapon, WR_CHECKAMMO2))
-                               if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
-                               {
-                                       W_SwitchWeapon_Force(self, w_getbestweapon(self));
-                                       w_ready();
-                                       return FALSE;
-                               }
-
-                               W_DecreaseAmmo(ammo_nails, autocvar_g_balance_uzi_burst_ammo, autocvar_g_balance_uzi_reload_ammo);
-
-                               self.misc_bulletcounter = autocvar_g_balance_uzi_burst * -1;
-                               uzi_mode1_fire_burst();
-                       }
-               }
-               else
-               {
-
-                       if (self.BUTTON_ATCK)
-                       if (weapon_prepareattack(0, 0))
-                       {
-                               self.misc_bulletcounter = 1;
-                               W_UZI_Attack(WEP_UZI); // sets attack_finished
-                               weapon_thinkf(WFRAME_FIRE1, autocvar_g_balance_uzi_sustained_refire, uzi_fire1_02);
-                       }
-
-                       if (self.BUTTON_ATCK2 && autocvar_g_balance_uzi_first)
-                       if (weapon_prepareattack(1, 0))
-                       {
-                               self.misc_bulletcounter = 1;
-                               W_UZI_Attack(WEP_UZI | HITTYPE_SECONDARY); // sets attack_finished
-                               weapon_thinkf(WFRAME_FIRE2, autocvar_g_balance_uzi_first_refire, w_ready);
-                       }
-               }
-       }
-       else if (req == WR_PRECACHE)
-       {
-               precache_model ("models/uziflash.md3");
-               precache_model ("models/weapons/g_uzi.md3");
-               precache_model ("models/weapons/v_uzi.md3");
-               precache_model ("models/weapons/h_uzi.iqm");
-               precache_sound ("weapons/uzi_fire.wav");
-               //precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound somewhere else
-       }
-       else if (req == WR_SETUP)
-       {
-               weapon_setup(WEP_UZI);
-               self.current_ammo = ammo_nails;
-       }
-       else if (req == WR_CHECKAMMO1)
-       {
-               if(autocvar_g_balance_uzi_mode == 1)
-                       ammo_amount = self.ammo_nails >= autocvar_g_balance_uzi_sustained_ammo;
-               else
-                       ammo_amount = self.ammo_nails >= autocvar_g_balance_uzi_first_ammo;
-
-               if(autocvar_g_balance_uzi_reload_ammo)
-               {
-                       if(autocvar_g_balance_uzi_mode == 1)
-                               ammo_amount += self.(weapon_load[WEP_UZI]) >= autocvar_g_balance_uzi_sustained_ammo;
-                       else
-                               ammo_amount += self.(weapon_load[WEP_UZI]) >= autocvar_g_balance_uzi_first_ammo;
-               }
-               return ammo_amount;
-       }
-       else if (req == WR_CHECKAMMO2)
-       {
-               if(autocvar_g_balance_uzi_mode == 1)
-                       ammo_amount = self.ammo_nails >= autocvar_g_balance_uzi_burst_ammo;
-               else
-                       ammo_amount = self.ammo_nails >= autocvar_g_balance_uzi_first_ammo;
-
-               if(autocvar_g_balance_uzi_reload_ammo)
-               {
-                       if(autocvar_g_balance_uzi_mode == 1)
-                               ammo_amount += self.(weapon_load[WEP_UZI]) >= autocvar_g_balance_uzi_burst_ammo;
-                       else
-                               ammo_amount += self.(weapon_load[WEP_UZI]) >= autocvar_g_balance_uzi_first_ammo;
-               }
-               return ammo_amount;
-       }
-       else if (req == WR_RELOAD)
-       {
-               W_Reload(min(max(autocvar_g_balance_uzi_sustained_ammo, autocvar_g_balance_uzi_first_ammo), autocvar_g_balance_uzi_burst_ammo), autocvar_g_balance_uzi_reload_ammo, autocvar_g_balance_uzi_reload_time, "weapons/reload.wav");
-       }
-       else if (req == WR_SUICIDEMESSAGE)
-       {
-               return WEAPON_THINKING_WITH_PORTALS;
-       }
-       else if (req == WR_KILLMESSAGE)
-       {
-               if(w_deathtype & HITTYPE_SECONDARY)
-                       return WEAPON_UZI_MURDER_SNIPE;
-               else
-                       return WEAPON_UZI_MURDER_SPRAY;
-       }
-       return TRUE;
-}
-#endif
-#ifdef CSQC
-float w_uzi(float req)
-{
-       if(req == WR_IMPACTEFFECT)
-       {
-               vector org2;
-               org2 = w_org + w_backoff * 2;
-               pointparticles(particleeffectnum("machinegun_impact"), org2, w_backoff * 1000, 1);
-               if(!w_issilent)
-                       if(w_random < 0.05)
-                               sound(self, CH_SHOTS, "weapons/ric1.wav", VOL_BASE, ATTEN_NORM);
-                       else if(w_random < 0.1)
-                               sound(self, CH_SHOTS, "weapons/ric2.wav", VOL_BASE, ATTEN_NORM);
-                       else if(w_random < 0.2)
-                               sound(self, CH_SHOTS, "weapons/ric3.wav", VOL_BASE, ATTEN_NORM);
-       }
-       else if(req == WR_PRECACHE)
-       {
-               precache_sound("weapons/ric1.wav");
-               precache_sound("weapons/ric2.wav");
-               precache_sound("weapons/ric3.wav");
-       }
-       return TRUE;
-}
-#endif
-#endif
diff --git a/qcsrc/server/weapons/accuracy.qc b/qcsrc/server/weapons/accuracy.qc
new file mode 100644 (file)
index 0000000..00605ce
--- /dev/null
@@ -0,0 +1,120 @@
+float accuracy_byte(float n, float d)
+{
+       //print(sprintf("accuracy: %d / %d\n", n, d));
+       if(n <= 0)
+               return 0;
+       if(n > d)
+               return 255;
+       return 1 + rint(n * 100.0 / d);
+}
+
+float accuracy_send(entity to, float sf)
+{
+       float w, f;
+       entity a;
+       WriteByte(MSG_ENTITY, ENT_CLIENT_ACCURACY);
+
+       a = self.owner;
+       if(IS_SPEC(a))
+               a = a.enemy;
+       a = a.accuracy;
+
+       if(to != a.owner)
+               if (!(self.owner.cvar_cl_accuracy_data_share && autocvar_sv_accuracy_data_share))
+                       sf = 0;
+       // note: zero sendflags can never be sent... so we can use that to say that we send no accuracy!
+       WriteInt24_t(MSG_ENTITY, sf);
+       if(sf == 0)
+               return TRUE;
+       // note: we know that client and server agree about SendFlags...
+       for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w)
+       {
+               if(sf & f)
+                       WriteByte(MSG_ENTITY, accuracy_byte(self.(accuracy_hit[w]), self.(accuracy_fired[w])));
+               if(f == 0x800000)
+                       f = 1;
+               else
+                       f *= 2;
+       }
+       return TRUE;
+}
+
+// init/free
+void accuracy_init(entity e)
+{
+       e.accuracy = spawn();
+       e.accuracy.owner = e;
+       e.accuracy.classname = "accuracy";
+       e.accuracy.drawonlytoclient = e;
+       Net_LinkEntity(e.accuracy, FALSE, 0, accuracy_send);
+}
+
+void accuracy_free(entity e)
+{
+       remove(e.accuracy);
+}
+
+// force a resend of a player's accuracy stats
+void accuracy_resend(entity e)
+{
+       e.accuracy.SendFlags = 0xFFFFFF;
+}
+
+// update accuracy stats
+.float hit_time;
+.float fired_time;
+
+void accuracy_add(entity e, float w, float fired, float hit)
+{
+       entity a;
+       float b;
+       if(IS_INDEPENDENT_PLAYER(e))
+               return;
+       a = e.accuracy;
+       if(!a || !(hit || fired))
+               return;
+       w -= WEP_FIRST;
+       b = accuracy_byte(a.(accuracy_hit[w]), a.(accuracy_fired[w]));
+       if(hit)
+               a.(accuracy_hit[w]) += hit;
+       if(fired)
+               a.(accuracy_fired[w]) += fired;
+
+    if(hit && a.hit_time != time) // only run this once per frame
+    {
+        a.(accuracy_cnt_hit[w]) += 1;
+        a.hit_time = time;
+    }
+
+    if(fired && a.fired_time != time) // only run this once per frame
+    {
+        a.(accuracy_cnt_fired[w]) += 1;
+        a.fired_time = time;
+    }
+
+       if(b == accuracy_byte(a.(accuracy_hit[w]), a.(accuracy_fired[w])))
+               return;
+       w = pow(2, mod(w, 24));
+       a.SendFlags |= w;
+       FOR_EACH_CLIENT(a)
+               if(IS_SPEC(a))
+                       if(a.enemy == e)
+                               a.SendFlags |= w;
+}
+
+float accuracy_isgooddamage(entity attacker, entity targ)
+{
+       if(!warmup_stage)
+       if(IS_CLIENT(targ))
+       if(targ.deadflag == DEAD_NO)
+       if(DIFF_TEAM(attacker, targ))
+               return TRUE;
+       return FALSE;
+}
+
+float accuracy_canbegooddamage(entity attacker)
+{
+       if(!warmup_stage)
+               return TRUE;
+       return FALSE;
+}
diff --git a/qcsrc/server/weapons/accuracy.qh b/qcsrc/server/weapons/accuracy.qh
new file mode 100644 (file)
index 0000000..4cd43ee
--- /dev/null
@@ -0,0 +1,25 @@
+.float cvar_cl_accuracy_data_share;
+.float cvar_cl_accuracy_data_receive;
+
+.entity accuracy;
+.float accuracy_frags[WEP_MAXCOUNT];
+
+.float accuracy_hit[WEP_MAXCOUNT];
+.float accuracy_fired[WEP_MAXCOUNT];
+.float accuracy_cnt_hit[WEP_MAXCOUNT];
+.float accuracy_cnt_fired[WEP_MAXCOUNT];
+
+
+// init/free
+void accuracy_init(entity e);
+void accuracy_free(entity e);
+
+// force a resend of a player's accuracy stats
+void accuracy_resend(entity e);
+
+// update accuracy stats
+void accuracy_add(entity e, float w, float fired, float hit);
+
+// helper
+float accuracy_isgooddamage(entity attacker, entity targ);
+float accuracy_canbegooddamage(entity attacker);
diff --git a/qcsrc/server/weapons/common.qc b/qcsrc/server/weapons/common.qc
new file mode 100644 (file)
index 0000000..6108c38
--- /dev/null
@@ -0,0 +1,89 @@
+
+void W_GiveWeapon (entity e, float wep)
+{
+       entity oldself;
+
+       if (!wep)
+               return;
+
+       e.weapons |= WepSet_FromWeapon(wep);
+
+       oldself = self;
+       self = e;
+
+       if(IS_PLAYER(other))
+               { Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_WEAPON_GOT, wep); }
+
+       self = oldself;
+}
+
+void W_PlayStrengthSound(entity player) // void W_PlayStrengthSound
+{
+       if((player.items & IT_STRENGTH)
+               && ((time > player.prevstrengthsound + autocvar_sv_strengthsound_antispam_time) // prevent insane sound spam
+               || (time > player.prevstrengthsoundattempt + autocvar_sv_strengthsound_antispam_refire_threshold)))
+               {
+                       sound(player, CH_TRIGGER, "weapons/strength_fire.wav", VOL_BASE, ATTEN_NORM);
+                       player.prevstrengthsound = time;
+               }
+               player.prevstrengthsoundattempt = time;
+}
+
+float W_CheckProjectileDamage(entity inflictor, entity projowner, float deathtype, float exception)
+{
+       float is_from_contents = (deathtype == DEATH_SLIME || deathtype == DEATH_LAVA);
+       float is_from_owner = (inflictor == projowner);
+       float is_from_exception = (exception != -1);
+       
+       //dprint(strcat("W_CheckProjectileDamage: from_contents ", ftos(is_from_contents), " : from_owner ", ftos(is_from_owner), " : exception ", strcat(ftos(is_from_exception), " (", ftos(exception), "). \n")));
+
+       if(autocvar_g_projectiles_damage <= -2)
+       {
+               return FALSE; // no damage to projectiles at all, not even with the exceptions
+       }
+       else if(autocvar_g_projectiles_damage == -1)
+       {
+               if(is_from_exception)
+                       return (exception); // if exception is detected, allow it to override
+               else
+                       return FALSE; // otherwise, no other damage is allowed
+       }
+       else if(autocvar_g_projectiles_damage == 0)
+       {
+               if(is_from_exception)
+                       return (exception); // if exception is detected, allow it to override
+               else if 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;
+}
diff --git a/qcsrc/server/weapons/common.qh b/qcsrc/server/weapons/common.qh
new file mode 100644 (file)
index 0000000..8f9454e
--- /dev/null
@@ -0,0 +1,7 @@
+
+void W_GiveWeapon (entity e, float wep);
+.float prevstrengthsound;
+.float prevstrengthsoundattempt;
+void W_PlayStrengthSound(entity player);
+float W_CheckProjectileDamage(entity inflictor, entity projowner, float deathtype, float exception);
+void W_PrepareExplosionByDamage(entity attacker, void() explode);
diff --git a/qcsrc/server/weapons/csqcprojectile.qc b/qcsrc/server/weapons/csqcprojectile.qc
new file mode 100644 (file)
index 0000000..4d208eb
--- /dev/null
@@ -0,0 +1,116 @@
+.float csqcprojectile_type;
+
+float CSQCProjectile_SendEntity(entity to, float sf)
+{
+       float ft, fr;
+
+       // note: flag 0x08 = no trail please (teleport bit)
+       sf = sf & 0x0F;
+
+       if(self.csqcprojectile_clientanimate)
+               sf |= 0x80; // client animated, not interpolated
+
+       if(self.flags & FL_ONGROUND)
+               sf |= 0x40;
+
+       ft = fr = 0;
+       if(self.fade_time != 0 || self.fade_rate != 0)
+       {
+               ft = (self.fade_time - time) / sys_frametime;
+               fr = (1 / self.fade_rate) / sys_frametime;
+               if(ft <= 255 && fr <= 255 && fr >= 1)
+                       sf |= 0x20;
+       }
+
+       if(self.gravity != 0)
+               sf |= 0x10;
+
+       WriteByte(MSG_ENTITY, ENT_CLIENT_PROJECTILE);
+       WriteByte(MSG_ENTITY, sf);
+
+       if(sf & 1)
+       {
+               WriteCoord(MSG_ENTITY, self.origin_x);
+               WriteCoord(MSG_ENTITY, self.origin_y);
+               WriteCoord(MSG_ENTITY, self.origin_z);
+
+               if(sf & 0x80)
+               {
+                       WriteCoord(MSG_ENTITY, self.velocity_x);
+                       WriteCoord(MSG_ENTITY, self.velocity_y);
+                       WriteCoord(MSG_ENTITY, self.velocity_z);
+                       if(sf & 0x10)
+                               WriteCoord(MSG_ENTITY, self.gravity);
+               }
+
+               if(sf & 0x20)
+               {
+                       WriteByte(MSG_ENTITY, ft);
+                       WriteByte(MSG_ENTITY, fr);
+               }
+       }
+
+       if(sf & 2)
+               WriteByte(MSG_ENTITY, self.csqcprojectile_type); // TODO maybe put this into sf?
+
+       return 1;
+}
+
+.vector csqcprojectile_oldorigin;
+void CSQCProjectile_Check(entity e)
+{
+       if(e.csqcprojectile_clientanimate)
+       if(e.flags & FL_ONGROUND)
+       if(e.origin != e.csqcprojectile_oldorigin)
+               UpdateCSQCProjectile(e);
+       e.csqcprojectile_oldorigin = e.origin;
+}
+
+void CSQCProjectile(entity e, float clientanimate, float type, float docull)
+{
+       Net_LinkEntity(e, docull, 0, CSQCProjectile_SendEntity);
+
+       e.csqcprojectile_clientanimate = clientanimate;
+
+       if(e.movetype == MOVETYPE_TOSS || e.movetype == MOVETYPE_BOUNCE)
+       {
+               if(e.gravity == 0)
+                       e.gravity = 1;
+       }
+       else
+               e.gravity = 0;
+
+       if(!sound_allowed(MSG_BROADCAST, e))
+               type |= 0x80;
+       e.csqcprojectile_type = type;
+}
+
+// FIXME HACK
+float ItemSend(entity to, float sf);
+void ItemUpdate(entity item);
+// END HACK
+void UpdateCSQCProjectile(entity e)
+{
+       if(e.SendEntity == CSQCProjectile_SendEntity)
+       {
+               // send new origin data
+               e.SendFlags |= 0x01;
+       }
+// FIXME HACK
+       else if(e.SendEntity == ItemSend)
+       {
+               ItemUpdate(e);
+       }
+// END HACK
+}
+
+void UpdateCSQCProjectileAfterTeleport(entity e)
+{
+       if(e.SendEntity == CSQCProjectile_SendEntity)
+       {
+               // send new origin data
+               e.SendFlags |= 0x01;
+               // mark as teleported
+               e.SendFlags |= 0x08;
+       }
+}
diff --git a/qcsrc/server/weapons/csqcprojectile.qh b/qcsrc/server/weapons/csqcprojectile.qh
new file mode 100644 (file)
index 0000000..e00c098
--- /dev/null
@@ -0,0 +1,6 @@
+.float csqcprojectile_clientanimate;
+
+void CSQCProjectile(entity e, float clientanimate, float type, float docull);
+void UpdateCSQCProjectile(entity e);
+void UpdateCSQCProjectileAfterTeleport(entity e);
+void CSQCProjectile_Check(entity e);
diff --git a/qcsrc/server/weapons/hitplot.qc b/qcsrc/server/weapons/hitplot.qc
new file mode 100644 (file)
index 0000000..5ef9f12
--- /dev/null
@@ -0,0 +1,91 @@
+vector W_HitPlotUnnormalizedUntransform(vector screenforward, vector screenright, vector screenup, vector v)
+{
+       vector ret;
+       ret_x = screenright * v;
+       ret_y = screenup * v;
+       ret_z = screenforward * v;
+       return ret;
+}
+
+vector W_HitPlotNormalizedUntransform(vector org, entity targ, vector screenforward, vector screenright, vector screenup, vector v)
+{
+       float i, j, k;
+       vector mi, ma, thisv, myv, ret;
+
+       myv = W_HitPlotUnnormalizedUntransform(screenforward, screenright, screenup, org);
+
+       // x = 0..1 relative to hitbox; y = 0..1 relative to hitbox; z = distance
+
+       mi = ma = targ.origin + 0.5 * (targ.mins + targ.maxs);
+       for(i = 0; i < 2; ++i) for(j = 0; j < 2; ++j) for(k = 0; k < 2; ++k)
+       {
+               thisv = targ.origin;
+               if(i) thisv_x += targ.maxs_x; else thisv_x += targ.mins_x;
+               if(j) thisv_y += targ.maxs_y; else thisv_y += targ.mins_y;
+               if(k) thisv_z += targ.maxs_z; else thisv_z += targ.mins_z;
+               thisv = W_HitPlotUnnormalizedUntransform(screenforward, screenright, screenup, thisv);
+               if(i || j || k)
+               {
+                       if(mi_x > thisv_x) mi_x = thisv_x; if(ma_x < thisv_x) ma_x = thisv_x;
+                       if(mi_y > thisv_y) mi_y = thisv_y; if(ma_y < thisv_y) ma_y = thisv_y;
+                       //if(mi_z > thisv_z) mi_z = thisv_z; if(ma_z < thisv_z) ma_y = thisv_z;
+               }
+               else
+               {
+                       // first run
+                       mi = ma = thisv;
+               }
+       }
+
+       thisv = W_HitPlotUnnormalizedUntransform(screenforward, screenright, screenup, v);
+       ret_x = (thisv_x - mi_x) / (ma_x - mi_x);
+       ret_y = (thisv_y - mi_y) / (ma_y - mi_y);
+       ret_z = thisv_z - myv_z;
+       return ret;
+}
+
+void W_HitPlotAnalysis(entity player, vector screenforward, vector screenright, vector screenup)
+{
+       vector hitplot;
+       vector org;
+       float lag;
+
+       if(player.hitplotfh >= 0)
+       {
+               lag = ANTILAG_LATENCY(player);
+               if(lag < 0.001)
+                       lag = 0;
+               if not(IS_REAL_CLIENT(player))
+                       lag = 0; // only antilag for clients
+
+               org = player.origin + player.view_ofs;
+               traceline_antilag_force(player, org, org + screenforward * MAX_SHOT_DISTANCE, MOVE_NORMAL, player, lag);
+               if(IS_CLIENT(trace_ent))
+               {
+                       antilag_takeback(trace_ent, time - lag);
+                       hitplot = W_HitPlotNormalizedUntransform(org, trace_ent, screenforward, screenright, screenup, trace_endpos);
+                       antilag_restore(trace_ent);
+                       fputs(player.hitplotfh, strcat(ftos(hitplot_x), " ", ftos(hitplot_y), " ", ftos(hitplot_z), " ", ftos(player.switchweapon), "\n"));
+                       //print(strcat(ftos(hitplot_x), " ", ftos(hitplot_y), " ", ftos(hitplot_z), "\n"));
+               }
+       }
+}
+
+void W_HitPlotOpen(entity player)
+{
+       if(autocvar_g_hitplots || strstrofs(strcat(" ", autocvar_g_hitplots_individuals, " "), strcat(" ", player.netaddress, " "), 0) >= 0)
+       {
+               player.hitplotfh = fopen(strcat("hits-", matchid, "-", player.netaddress, "-", ftos(player.playerid), ".plot"), FILE_WRITE);
+               fputs(player.hitplotfh, strcat("#name ", player.netname, "\n"));
+       }
+       else { player.hitplotfh = -1; }
+}
+
+void W_HitPlotClose(entity player)
+{
+       if(player.hitplotfh >= 0)
+       {
+               fclose(player.hitplotfh);
+               player.hitplotfh = -1;
+       }
+}
diff --git a/qcsrc/server/weapons/hitplot.qh b/qcsrc/server/weapons/hitplot.qh
new file mode 100644 (file)
index 0000000..9bc8ff3
--- /dev/null
@@ -0,0 +1,5 @@
+.float hitplotfh;
+
+void W_HitPlotAnalysis(entity player, vector screenforward, vector screenright, vector screenup);
+void W_HitPlotOpen(entity player);
+void W_HitPlotClose(entity player);
diff --git a/qcsrc/server/weapons/selection.qc b/qcsrc/server/weapons/selection.qc
new file mode 100644 (file)
index 0000000..c232795
--- /dev/null
@@ -0,0 +1,273 @@
+// 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(wpn == WEP_HOOK && !g_grappling_hook && autocvar_g_nades && !((cl.weapons | weaponsInMap) & WepSet_FromWeapon(wpn)))
+               complain = 0;
+               
+       if(complain)
+               self.hasweapon_complain_spam = time + 0.2;
+
+       if (wpn < WEP_FIRST || wpn > WEP_LAST)
+       {
+               if (complain)
+                       sprint(self, "Invalid weapon\n");
+               return FALSE;
+       }
+       if (cl.weapons & WepSet_FromWeapon(wpn))
+       {
+               if (andammo)
+               {
+                       if(cl.items & IT_UNLIMITED_WEAPON_AMMO)
+                       {
+                               f = 1;
+                       }
+                       else
+                       {
+                               oldself = self;
+                               self = cl;
+                               f = WEP_ACTION(wpn, WR_CHECKAMMO1);
+                               f = f + WEP_ACTION(wpn, WR_CHECKAMMO2);
+
+                               // always allow selecting the Mine Layer if we placed mines, so that we can detonate them
+                               entity mine;
+                               if(wpn == WEP_MINE_LAYER)
+                               for(mine = world; (mine = find(mine, classname, "mine")); ) if(mine.owner == self)
+                                       f = 1;
+
+                               self = oldself;
+                       }
+                       if (!f)
+                       {
+                               if (complain)
+                               if(IS_REAL_CLIENT(cl))
+                               {
+                                       play2(cl, "weapons/unavailable.wav");
+                                       Send_WeaponComplain (cl, wpn, W_Name(wpn), 0);
+                               }
+                               return FALSE;
+                       }
+               }
+               return TRUE;
+       }
+       if (complain)
+       {
+               // DRESK - 3/16/07
+               // Report Proper Weapon Status / Modified Weapon Ownership Message
+               if (weaponsInMap & WepSet_FromWeapon(wpn))
+               {
+                       Send_WeaponComplain(cl, wpn, W_Name(wpn), 1);
+
+                       if(autocvar_g_showweaponspawns)
+                       {
+                               entity e;
+                               string s;
+
+                               e = get_weaponinfo(wpn);
+                               s = e.model2;
+
+                               for(e = world; (e = findfloat(e, weapon, wpn)); )
+                               {
+                                       if(e.classname == "droppedweapon")
+                                               continue;
+                                       if 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;
+       if(pl.weapons & WepSet_FromWeapon(w))
+       {
+               pl.weapons &= ~WepSet_FromWeapon(w);
+               ww = w_getbestweapon(pl);
+               pl.weapons |= WepSet_FromWeapon(w);
+       }
+       else
+               ww = w_getbestweapon(pl);
+       if(ww)
+               W_SwitchWeapon_Force(pl, ww);
+}
+
+void W_SwitchWeapon(float imp)
+{
+       if (self.switchweapon != imp)
+       {
+               if (client_hasweapon(self, imp, TRUE, TRUE))
+                       W_SwitchWeapon_Force(self, imp);
+               else
+                       self.selectweapon = imp; // update selectweapon ANYWAY
+       }
+       else { WEP_ACTION(self.weapon, WR_RELOAD); }
+}
+
+void W_CycleWeapon(string weaponorder, float dir)
+{
+       float w;
+       w = W_GetCycleWeapon(self, weaponorder, dir, -1, 1, TRUE);
+       if(w > 0)
+               W_SwitchWeapon(w);
+}
+
+void W_NextWeaponOnImpulse(float imp)
+{
+       float w;
+       w = W_GetCycleWeapon(self, self.cvar_cl_weaponpriority, +1, imp, 1, (self.cvar_cl_weaponimpulsemode == 0));
+       if(w > 0)
+               W_SwitchWeapon(w);
+}
+
+// next weapon
+void W_NextWeapon(float list)
+{
+       if(list == 0)
+               W_CycleWeapon(weaponorder_byid, -1);
+       else if(list == 1)
+               W_CycleWeapon(self.weaponorder_byimpulse, -1);
+       else if(list == 2)
+               W_CycleWeapon(self.cvar_cl_weaponpriority, -1);
+}
+
+// prev weapon
+void W_PreviousWeapon(float list)
+{
+       if(list == 0)
+               W_CycleWeapon(weaponorder_byid, +1);
+       else if(list == 1)
+               W_CycleWeapon(self.weaponorder_byimpulse, +1);
+       else if(list == 2)
+               W_CycleWeapon(self.cvar_cl_weaponpriority, +1);
+}
+
+// previously used if exists and has ammo, (second) best otherwise
+void W_LastWeapon(void)
+{
+       if(client_hasweapon(self, self.cnt, TRUE, FALSE))
+               W_SwitchWeapon(self.cnt);
+       else
+               W_SwitchToOtherWeapon(self);
+}
diff --git a/qcsrc/server/weapons/selection.qh b/qcsrc/server/weapons/selection.qh
new file mode 100644 (file)
index 0000000..17310eb
--- /dev/null
@@ -0,0 +1,29 @@
+// switch between weapons
+void Send_WeaponComplain(entity e, float wpn, string wpnname, float type);
+
+.float hasweapon_complain_spam;
+float client_hasweapon(entity cl, float wpn, float andammo, float complain);
+
+.float weaponcomplainindex;
+float W_GetCycleWeapon(entity pl, string weaponorder, float dir, float imp, float complain, float skipmissing);
+
+#define w_getbestweapon(ent) W_GetCycleWeapon(ent, ent.cvar_cl_weaponpriority, 0, -1, FALSE, TRUE)
+
+void W_SwitchWeapon_Force(entity e, float w);
+
+// perform weapon to attack (weaponstate and attack_finished check is here)
+void W_SwitchToOtherWeapon(entity pl);
+void W_SwitchWeapon(float imp);
+
+void W_CycleWeapon(string weaponorder, float dir);
+
+void W_NextWeaponOnImpulse(float imp);
+
+// next weapon
+void W_NextWeapon(float list);
+
+// prev weapon
+void W_PreviousWeapon(float list);
+
+// previously used if exists and has ammo, (second) best otherwise
+void W_LastWeapon(void);
diff --git a/qcsrc/server/weapons/spawning.qc b/qcsrc/server/weapons/spawning.qc
new file mode 100644 (file)
index 0000000..65188fe
--- /dev/null
@@ -0,0 +1,153 @@
+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(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
+               WEP_ACTION(e.weapon, WR_INIT);
+}
diff --git a/qcsrc/server/weapons/spawning.qh b/qcsrc/server/weapons/spawning.qh
new file mode 100644 (file)
index 0000000..c6939e5
--- /dev/null
@@ -0,0 +1,3 @@
+string W_Apply_Weaponreplace(string in);
+
+void weapon_defaultspawnfunc(float wpn);
diff --git a/qcsrc/server/weapons/throwing.qc b/qcsrc/server/weapons/throwing.qc
new file mode 100644 (file)
index 0000000..80a97d1
--- /dev/null
@@ -0,0 +1,190 @@
+void thrown_wep_think()
+{
+       self.owner = world;
+       float timeleft = self.savenextthink - time;
+       if(timeleft > 1)
+               SUB_SetFade(self, self.savenextthink - 1, 1);
+       else if(timeleft > 0)
+               SUB_SetFade(self, time, timeleft);
+       else
+               SUB_VanishOrRemove(self);
+}
+
+// returns amount of ammo used as string, or -1 for failure, or 0 for no ammo count
+string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo)
+{
+       entity oldself, wep;
+       float wa, thisammo, i, j;
+       string s;
+       var .float ammofield;
+
+       wep = spawn();
+
+       setorigin(wep, org);
+       wep.classname = "droppedweapon";
+       wep.velocity = velo;
+       wep.owner = wep.enemy = own;
+       wep.flags |= FL_TOSSED;
+       wep.colormap = own.colormap;
+
+       if(WepSet_FromWeapon(wpn) & WEPSET_SUPERWEAPONS)
+       {
+               if(own.items & IT_UNLIMITED_SUPERWEAPONS)
+               {
+                       wep.superweapons_finished = time + autocvar_g_balance_superweapons_time;
+               }
+               else
+               {
+                       float superweapons = 1;
+                       for(i = WEP_FIRST; i <= WEP_LAST; ++i)
+                               if(WepSet_FromWeapon(i) & WEPSET_SUPERWEAPONS)
+                                       if(own.weapons & WepSet_FromWeapon(i))
+                                               ++superweapons;
+                       if(superweapons <= 1)
+                       {
+                               wep.superweapons_finished = own.superweapons_finished;
+                               own.superweapons_finished = 0;
+                       }
+                       else
+                       {
+                               float timeleft = own.superweapons_finished - time;
+                               float weptimeleft = timeleft / superweapons;
+                               wep.superweapons_finished = time + weptimeleft;
+                               own.superweapons_finished -= weptimeleft;
+                       }
+               }
+       }
+
+       wa = W_AmmoItemCode(wpn);
+       if(wa == 0)
+       {
+               oldself = self;
+               self = wep;
+               weapon_defaultspawnfunc(wpn);
+               self = oldself;
+               if(startitem_failed)
+                       return string_null;
+               wep.glowmod = own.weaponentity_glowmod;
+               wep.think = thrown_wep_think;
+               wep.savenextthink = wep.nextthink;
+               wep.nextthink = min(wep.nextthink, time + 0.5);
+               wep.pickup_anyway = TRUE; // these are ALWAYS pickable
+               return "";
+       }
+       else
+       {
+               s = "";
+               oldself = self;
+               self = wep;
+               weapon_defaultspawnfunc(wpn);
+               self = oldself;
+               if(startitem_failed)
+                       return string_null;
+               if(doreduce && g_weapon_stay == 2)
+               {
+                       for(i = 0, j = 1; i < 24; ++i, j *= 2)
+                       {
+                               if(wa & j)
+                               {
+                                       ammofield = Item_CounterField(j);
+
+                                       // if our weapon is loaded, give its load back to the player
+                                       if(self.(weapon_load[self.weapon]) > 0)
+                                       {
+                                               own.ammofield += self.(weapon_load[self.weapon]);
+                                               self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading
+                                       }
+
+                                       wep.ammofield = 0;
+                               }
+                       }
+               }
+               else if(doreduce)
+               {
+                       for(i = 0, j = 1; i < 24; ++i, j *= 2)
+                       {
+                               if(wa & j)
+                               {
+                                       ammofield = Item_CounterField(j);
+
+                                       // if our weapon is loaded, give its load back to the player
+                                       if(self.(weapon_load[self.weapon]) > 0)
+                                       {
+                                               own.ammofield += self.(weapon_load[self.weapon]);
+                                               self.(weapon_load[self.weapon]) = -1; // schedule the weapon for reloading
+                                       }
+
+                                       thisammo = min(own.ammofield, wep.ammofield);
+                                       wep.ammofield = thisammo;
+                                       own.ammofield -= thisammo;
+                                       s = strcat(s, " and ", ftos(thisammo), " ", Item_CounterFieldName(j));
+                               }
+                       }
+                       s = substring(s, 5, -1);
+               }
+               wep.glowmod = own.weaponentity_glowmod;
+               wep.think = thrown_wep_think;
+               wep.savenextthink = wep.nextthink;
+               wep.nextthink = min(wep.nextthink, time + 0.5);
+               wep.pickup_anyway = TRUE; // these are ALWAYS pickable
+
+               return s;
+       }
+}
+
+float W_IsWeaponThrowable(float w)
+{
+       float wa;
+
+       if (!autocvar_g_pickup_items)
+               return 0;
+       if (g_weaponarena)
+               return 0;
+       if (g_cts)
+               return 0;
+       if (g_nexball && w == WEP_GRENADE_LAUNCHER)
+               return 0;
+    if(w == 0)
+        return 0;
+       
+       wa = W_AmmoItemCode(w);
+       if(start_weapons & WepSet_FromWeapon(w))
+       {
+               // start weapons that take no ammo can't be dropped (this prevents dropping the laser, as long as it continues to use no ammo)
+               if(start_items & IT_UNLIMITED_WEAPON_AMMO)
+                       return 0;
+               if(wa == 0)
+                       return 0;
+       }
+
+       return 1;
+}
+
+// toss current weapon
+void W_ThrowWeapon(vector velo, vector delta, float doreduce)
+{
+       float w;
+       string a;
+
+       w = self.weapon;
+       if (w == 0)
+               return; // just in case
+       if(MUTATOR_CALLHOOK(ForbidThrowCurrentWeapon))
+               return;
+       if(!autocvar_g_weapon_throwable)
+               return;
+       if(self.weaponentity.state != WS_READY)
+               return;
+       if(!W_IsWeaponThrowable(w))
+               return;
+
+       if(!(self.weapons & WepSet_FromWeapon(w)))
+               return;
+       self.weapons &= ~WepSet_FromWeapon(w);
+
+       W_SwitchWeapon_Force(self, w_getbestweapon(self));
+       a = W_ThrowNewWeapon(self, w, doreduce, self.origin + delta, velo);
+       
+       if not(a) return;
+       Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_WEAPON_DROP, a, w);
+}
diff --git a/qcsrc/server/weapons/throwing.qh b/qcsrc/server/weapons/throwing.qh
new file mode 100644 (file)
index 0000000..9dd70a6
--- /dev/null
@@ -0,0 +1,10 @@
+.float savenextthink;
+void thrown_wep_think();
+
+// returns amount of ammo used as string, or -1 for failure, or 0 for no ammo count
+string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo);
+
+float W_IsWeaponThrowable(float w);
+
+// toss current weapon
+void W_ThrowWeapon(vector velo, vector delta, float doreduce);
diff --git a/qcsrc/server/weapons/tracing.qc b/qcsrc/server/weapons/tracing.qc
new file mode 100644 (file)
index 0000000..b709889
--- /dev/null
@@ -0,0 +1,697 @@
+// 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(!(IS_SPEC(msg_entity) && msg_entity.enemy == self)) // we use realclient, so spectators can hear the whoosh too
+               {
+                       // nearest point on the beam
+                       beampos = start + dir * bound(0, (msg_entity.origin - start) * dir, length);
+
+                       f = bound(0, 1 - vlen(beampos - msg_entity.origin) / 512, 1);
+                       if(f <= 0)
+                               continue;
+
+                       snd = strcat("weapons/nexwhoosh", ftos(floor(random() * 3) + 1), ".wav");
+
+                       if(!pseudoprojectile)
+                               pseudoprojectile = spawn(); // we need this so the sound uses the "entchannel4" volume
+                       soundtoat(MSG_ONE, pseudoprojectile, beampos, CH_SHOTS, snd, VOL_BASE * f, ATTEN_NONE);
+               }
+
+               if(pseudoprojectile)
+                       remove(pseudoprojectile);
+       }
+
+       // find all the entities the railgun hit and hurt them
+       ent = findfloat(world, railgunhit, TRUE);
+       while (ent)
+       {
+               // get the details we need to call the damage function
+               hitloc = ent.railgunhitloc;
+
+               f = ExponentialFalloff(mindist, maxdist, halflifedist, ent.railgundistance);
+               ffs = ExponentialFalloff(mindist, maxdist, forcehalflifedist, ent.railgundistance);
+
+               if(accuracy_isgooddamage(self.realowner, ent))
+                       totaldmg += bdamage * f;
+
+               // apply the damage
+               if (ent.takedamage)
+                       Damage (ent, self, self, bdamage * f, deathtype, hitloc, ent.railgunforce * ffs);
+
+               // create a small explosion to throw gibs around (if applicable)
+               //setorigin (explosion, hitloc);
+               //RadiusDamage (explosion, self, 10, 0, 50, world, world, 300, deathtype);
+
+               ent.railgunhitloc = '0 0 0';
+               ent.railgunhitsolidbackup = SOLID_NOT;
+               ent.railgunhit = FALSE;
+               ent.railgundistance = 0;
+
+               // advance to the next entity
+               ent = findfloat(ent, railgunhit, TRUE);
+       }
+
+       // calculate hits and fired shots for hitscan
+       accuracy_add(self, self.weapon, 0, min(bdamage, totaldmg));
+
+       trace_endpos = endpoint;
+       trace_ent = endent;
+       trace_dphitq3surfaceflags = endq3surfaceflags;
+}
+
+void 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 (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID))
+       if (!(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 bulletconstant)
+{
+       float lag, dt, savetime; //, density;
+       entity pl, oldself;
+
+       entity proj;
+       proj = spawn();
+       proj.classname = "bullet";
+       proj.owner = proj.realowner = self;
+       PROJECTILE_MAKETRIGGER(proj);
+       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, TRUE);
+       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);
+
+       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 (!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;
+               v0 = self.velocity;
+               self.velocity = self.velocity * 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;
+
+               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 (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID))
+               if (!(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;
+}
+
+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 (!(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;
+}
diff --git a/qcsrc/server/weapons/tracing.qh b/qcsrc/server/weapons/tracing.qh
new file mode 100644 (file)
index 0000000..4dda934
--- /dev/null
@@ -0,0 +1,72 @@
+.float antilag_debug;
+
+vector w_shotorg;
+vector w_shotdir;
+vector w_shotend;
+
+// this function calculates w_shotorg and w_shotdir based on the weapon model
+// offset, trueaim and antilag, and won't put w_shotorg inside a wall.
+// make sure you call makevectors first (FIXME?)
+void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector mi, vector ma, float antilag, float recoil, string snd, float chan, float maxdamage, float range);
+
+#define W_SetupShot_Dir_ProjectileSize(ent,s_forward,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize_Range(ent, s_forward, mi, ma, antilag, recoil, snd, chan, maxdamage, MAX_SHOT_DISTANCE)
+#define W_SetupShot_ProjectileSize(ent,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, v_forward, mi, ma, antilag, recoil, snd, chan, maxdamage)
+#define W_SetupShot_Dir(ent,s_forward,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, s_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage)
+#define W_SetupShot(ent,antilag,recoil,snd,chan,maxdamage) W_SetupShot_ProjectileSize(ent, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage)
+#define W_SetupShot_Range(ent,antilag,recoil,snd,chan,maxdamage,range) W_SetupShot_Dir_ProjectileSize_Range(ent, v_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage, range)
+
+vector W_CalculateProjectileVelocity(vector pvelocity, vector mvelocity, float forceAbsolute);
+
+#if 0
+float mspercallsum;
+float mspercallsstyle;
+float mspercallcount;
+#endif
+void W_SetupProjectileVelocityEx(entity missile, vector dir, vector upDir, float pSpeed, float pUpSpeed, float pZSpeed, float spread, float forceAbsolute);
+
+void W_SetupProjectileVelocity(entity missile, float pSpeed, float spread); /* WEAPONTODO
+{
+       W_SetupProjectileVelocityEx(missile, w_shotdir, v_up, pSpeed, 0, 0, spread, FALSE);
+}*/
+
+#define W_SETUPPROJECTILEVELOCITY_UP(m,s) W_SetupProjectileVelocityEx(m, w_shotdir, v_up, cvar(#s "_speed"), cvar(#s "_speed_up"), cvar(#s "_speed_z"), cvar(#s "_spread"), FALSE)
+#define W_SETUPPROJECTILEVELOCITY(m,s) W_SetupProjectileVelocityEx(m, w_shotdir, v_up, cvar(#s "_speed"), 0, 0, cvar(#s "_spread"), FALSE)
+
+
+// ====================
+//  Ballistics Tracing
+// ====================
+
+.float railgundistance;
+.vector railgunforce;
+void FireRailgunBullet (vector start, vector end, float bdamage, float bforce, float mindist, float maxdist, float halflifedist, float forcehalflifedist, float deathtype);
+
+.float dmg_force;
+.float dmg_radius;
+.float dmg_total;
+//.float last_yoda;
+void W_BallisticBullet_Hit (void);
+
+.void(void) W_BallisticBullet_LeaveSolid_think_save;
+.float W_BallisticBullet_LeaveSolid_nextthink_save;
+.vector W_BallisticBullet_LeaveSolid_origin;
+.vector W_BallisticBullet_LeaveSolid_velocity;
+
+void W_BallisticBullet_LeaveSolid_think();
+
+float W_BallisticBullet_LeaveSolid(float eff);
+
+void W_BallisticBullet_Touch (void);
+
+void endFireBallisticBullet(); /* WEAPONTODO
+{
+       endzcurveparticles();
+}*/
+
+entity fireBallisticBullet_trace_callback_ent;
+float fireBallisticBullet_trace_callback_eff;
+void fireBallisticBullet_trace_callback(vector start, vector hit, vector end);
+
+void fireBallisticBullet(vector start, vector dir, float spread, float pSpeed, float lifetime, float damage, float force, float dtype, float tracereffects, float gravityfactor, float bulletconstant);
+
+void fireBullet (vector start, vector dir, float spread, float damage, float force, float dtype, float tracer);
diff --git a/qcsrc/server/weapons/weaponsystem.qc b/qcsrc/server/weapons/weaponsystem.qc
new file mode 100644 (file)
index 0000000..ddf1424
--- /dev/null
@@ -0,0 +1,943 @@
+/*
+===========================================================================
+
+  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
+const float WFRAME_DONTCHANGE = -1;
+const float WFRAME_FIRE1 = 0;
+const float WFRAME_FIRE2 = 1;
+const float WFRAME_IDLE = 2;
+const float WFRAME_RELOAD = 3;
+.float wframe;
+
+void(float fr, float t, void() func) weapon_thinkf;
+
+float CL_Weaponentity_CustomizeEntityForClient()
+{
+       self.viewmodelforclient = self.owner;
+       if(IS_SPEC(other))
+               if(other.enemy == self.owner)
+                       self.viewmodelforclient = other;
+       return TRUE;
+}
+
+/*
+ * supported formats:
+ *
+ * 1. simple animated model, muzzle flash handling on h_ model:
+ *    h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation
+ *      tags:
+ *        shot = muzzle end (shot origin, also used for muzzle flashes)
+ *        shell = casings ejection point (must be on the right hand side of the gun)
+ *        weapon = attachment for v_tuba.md3
+ *    v_tuba.md3 - first and third person model
+ *    g_tuba.md3 - pickup model
+ *
+ * 2. simple animated model, muzzle flash handling on v_ model:
+ *    h_tuba.dpm, h_tuba.dpm.framegroups - invisible model controlling the animation
+ *      tags:
+ *        weapon = attachment for v_tuba.md3
+ *    v_tuba.md3 - first and third person model
+ *      tags:
+ *        shot = muzzle end (shot origin, also used for muzzle flashes)
+ *        shell = casings ejection point (must be on the right hand side of the gun)
+ *    g_tuba.md3 - pickup model
+ *
+ * 3. fully animated model, muzzle flash handling on h_ model:
+ *    h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model
+ *      tags:
+ *        shot = muzzle end (shot origin, also used for muzzle flashes)
+ *        shell = casings ejection point (must be on the right hand side of the gun)
+ *        handle = corresponding to the origin of v_tuba.md3 (used for muzzle flashes)
+ *    v_tuba.md3 - third person model
+ *    g_tuba.md3 - pickup model
+ *
+ * 4. fully animated model, muzzle flash handling on v_ model:
+ *    h_tuba.dpm, h_tuba.dpm.framegroups - animated first person model
+ *      tags:
+ *        shot = muzzle end (shot origin)
+ *        shell = casings ejection point (must be on the right hand side of the gun)
+ *    v_tuba.md3 - third person model
+ *      tags:
+ *        shot = muzzle end (for muzzle flashes)
+ *    g_tuba.md3 - pickup model
+ */
+
+// writes:
+//   self.origin, self.angles
+//   self.weaponentity
+//   self.movedir, self.view_ofs
+//   attachment stuff
+//   anim stuff
+// to free:
+//   call again with ""
+//   remove the ent
+void CL_WeaponEntity_SetModel(string name)
+{
+       float v_shot_idx;
+       if (name != "")
+       {
+               // if there is a child entity, hide it until we're sure we use it
+               if (self.weaponentity)
+                       self.weaponentity.model = "";
+               setmodel(self, strcat("models/weapons/v_", name, ".md3")); // precision set below
+               v_shot_idx = gettagindex(self, "shot"); // used later
+               if(!v_shot_idx)
+                       v_shot_idx = gettagindex(self, "tag_shot");
+
+               setmodel(self, strcat("models/weapons/h_", name, ".iqm")); // precision set below
+               // preset some defaults that work great for renamed zym files (which don't need an animinfo)
+               self.anim_fire1  = animfixfps(self, '0 1 0.01', '0 0 0');
+               self.anim_fire2  = animfixfps(self, '1 1 0.01', '0 0 0');
+               self.anim_idle   = animfixfps(self, '2 1 0.01', '0 0 0');
+               self.anim_reload = animfixfps(self, '3 1 0.01', '0 0 0');
+
+               // if we have a "weapon" tag, let's attach the v_ model to it ("invisible hand" style model)
+               // if we don't, this is a "real" animated model
+               if(gettagindex(self, "weapon"))
+               {
+                       if (!self.weaponentity)
+                               self.weaponentity = spawn();
+                       setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter
+                       setattachment(self.weaponentity, self, "weapon");
+               }
+               else if(gettagindex(self, "tag_weapon"))
+               {
+                       if (!self.weaponentity)
+                               self.weaponentity = spawn();
+                       setmodel(self.weaponentity, strcat("models/weapons/v_", name, ".md3")); // precision does not matter
+                       setattachment(self.weaponentity, self, "tag_weapon");
+               }
+               else
+               {
+                       if(self.weaponentity)
+                               remove(self.weaponentity);
+                       self.weaponentity = world;
+               }
+
+               setorigin(self,'0 0 0');
+               self.angles = '0 0 0';
+               self.frame = 0;
+               self.viewmodelforclient = world;
+
+               float idx;
+
+               if(v_shot_idx) // v_ model attached to invisible h_ model
+               {
+                       self.movedir = gettaginfo(self.weaponentity, v_shot_idx);
+               }
+               else
+               {
+                       idx = gettagindex(self, "shot");
+                       if(!idx)
+                               idx = gettagindex(self, "tag_shot");
+                       if(idx)
+                               self.movedir = gettaginfo(self, idx);
+                       else
+                       {
+                               print("WARNING: weapon model ", self.model, " does not support the 'shot' tag, will display shots TOTALLY wrong\n");
+                               self.movedir = '0 0 0';
+                       }
+               }
+
+               if(self.weaponentity) // v_ model attached to invisible h_ model
+               {
+                       idx = gettagindex(self.weaponentity, "shell");
+                       if(!idx)
+                               idx = gettagindex(self.weaponentity, "tag_shell");
+                       if(idx)
+                               self.spawnorigin = gettaginfo(self.weaponentity, idx);
+               }
+               else
+                       idx = 0;
+               if(!idx)
+               {
+                       idx = gettagindex(self, "shell");
+                       if(!idx)
+                               idx = gettagindex(self, "tag_shell");
+                       if(idx)
+                               self.spawnorigin = gettaginfo(self, idx);
+                       else
+                       {
+                               print("WARNING: weapon model ", self.model, " does not support the 'shell' tag, will display casings wrong\n");
+                               self.spawnorigin = self.movedir;
+                       }
+               }
+
+               if(v_shot_idx)
+               {
+                       self.oldorigin = '0 0 0'; // use regular attachment
+               }
+               else
+               {
+                       if(self.weaponentity)
+                       {
+                               idx = gettagindex(self, "weapon");
+                               if(!idx)
+                                       idx = gettagindex(self, "tag_weapon");
+                       }
+                       else
+                       {
+                               idx = gettagindex(self, "handle");
+                               if(!idx)
+                                       idx = gettagindex(self, "tag_handle");
+                       }
+                       if(idx)
+                       {
+                               self.oldorigin = self.movedir - gettaginfo(self, idx);
+                       }
+                       else
+                       {
+                               print("WARNING: weapon model ", self.model, " does not support the 'handle' tag and neither does the v_ model support the 'shot' tag, will display muzzle flashes TOTALLY wrong\n");
+                               self.oldorigin = '0 0 0'; // there is no way to recover from this
+                       }
+               }
+
+               self.viewmodelforclient = self.owner;
+       }
+       else
+       {
+               self.model = "";
+               if(self.weaponentity)
+                       remove(self.weaponentity);
+               self.weaponentity = world;
+               self.movedir = '0 0 0';
+               self.spawnorigin = '0 0 0';
+               self.oldorigin = '0 0 0';
+               self.anim_fire1  = '0 1 0.01';
+               self.anim_fire2  = '0 1 0.01';
+               self.anim_idle   = '0 1 0.01';
+               self.anim_reload = '0 1 0.01';
+       }
+
+       self.view_ofs = '0 0 0';
+
+       if(self.movedir_x >= 0)
+       {
+               vector v0;
+               v0 = self.movedir;
+               self.movedir = shotorg_adjust(v0, FALSE, FALSE);
+               self.view_ofs = shotorg_adjust(v0, FALSE, TRUE) - v0;
+       }
+       self.owner.stat_shotorg = compressShotOrigin(self.movedir);
+       self.movedir = decompressShotOrigin(self.owner.stat_shotorg); // make them match perfectly
+
+       self.spawnorigin += self.view_ofs; // offset the casings origin by the same amount
+
+       // check if an instant weapon switch occurred
+       setorigin(self, self.view_ofs);
+       // reset animstate now
+       self.wframe = WFRAME_IDLE;
+       setanim(self, self.anim_idle, TRUE, FALSE, TRUE);
+}
+
+vector CL_Weapon_GetShotOrg(float wpn)
+{
+       entity wi, oldself;
+       vector ret;
+       wi = get_weaponinfo(wpn);
+       oldself = self;
+       self = spawn();
+       CL_WeaponEntity_SetModel(wi.mdl);
+       ret = self.movedir;
+       CL_WeaponEntity_SetModel("");
+       remove(self);
+       self = oldself;
+       return ret;
+}
+
+void CL_Weaponentity_Think()
+{
+       float tb;
+       self.nextthink = time;
+       if (intermission_running)
+               self.frame = self.anim_idle_x;
+       if (self.owner.weaponentity != self)
+       {
+               if (self.weaponentity)
+                       remove(self.weaponentity);
+               remove(self);
+               return;
+       }
+       if (self.owner.deadflag != DEAD_NO)
+       {
+               self.model = "";
+               if (self.weaponentity)
+                       self.weaponentity.model = "";
+               return;
+       }
+       if (self.weaponname != self.owner.weaponname || self.dmg != self.owner.modelindex || self.deadflag != self.owner.deadflag)
+       {
+               self.weaponname = self.owner.weaponname;
+               self.dmg = self.owner.modelindex;
+               self.deadflag = self.owner.deadflag;
+
+               CL_WeaponEntity_SetModel(self.owner.weaponname);
+       }
+
+       tb = (self.effects & (EF_TELEPORT_BIT | EF_RESTARTANIM_BIT));
+       self.effects = self.owner.effects & EFMASK_CHEAP;
+       self.effects &= ~EF_LOWPRECISION;
+       self.effects &= ~EF_FULLBRIGHT; // can mask team color, so get rid of it
+       self.effects &= ~EF_TELEPORT_BIT;
+       self.effects &= ~EF_RESTARTANIM_BIT;
+       self.effects |= tb;
+
+       if(self.owner.alpha == default_player_alpha)
+               self.alpha = default_weapon_alpha;
+       else if(self.owner.alpha != 0)
+               self.alpha = self.owner.alpha;
+       else
+               self.alpha = 1;
+
+       self.glowmod = self.owner.weaponentity_glowmod;
+       self.colormap = self.owner.colormap;
+       if (self.weaponentity)
+       {
+               self.weaponentity.effects = self.effects;
+               self.weaponentity.alpha = self.alpha;
+               self.weaponentity.colormap = self.colormap;
+               self.weaponentity.glowmod = self.glowmod;
+       }
+
+       self.angles = '0 0 0';
+
+       float f = (self.owner.weapon_nextthink - time);
+       if (self.state == WS_RAISE && !intermission_running)
+       {
+               entity newwep = get_weaponinfo(self.owner.switchweapon);
+               f = f * g_weaponratefactor / max(f, newwep.switchdelay_raise);
+               //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 (!(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
+               {
+                       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;
+               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);
+
+                       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;
+               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 (!(e.spawnflags & WEP_FLAG_RELOADABLE))
+       {
+               dprint("Warning: Attempted to reload a weapon that does not have the WEP_FLAG_RELOADABLE flag. Fix your code!\n");
+               return;
+       }
+
+       // return if reloading is disabled for this weapon
+       if(!self.reload_ammo_amount)
+               return;
+
+       // our weapon is fully loaded, no need to reload
+       if (self.clip_load >= self.reload_ammo_amount)
+               return;
+
+       // no ammo, so nothing to load
+       if(!self.(self.current_ammo) && self.reload_ammo_min)
+       if (!(self.items & IT_UNLIMITED_WEAPON_AMMO))
+       {
+               if(IS_REAL_CLIENT(self) && self.reload_complain < time)
+               {
+                       play2(self, "weapons/unavailable.wav");
+                       sprint(self, strcat("You don't have enough ammo to reload the ^2", W_Name(self.weapon), "\n"));
+                       self.reload_complain = time + 1;
+               }
+               // switch away if the amount of ammo is not enough to keep using this weapon
+               if (!(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;
+}
diff --git a/qcsrc/server/weapons/weaponsystem.qh b/qcsrc/server/weapons/weaponsystem.qh
new file mode 100644 (file)
index 0000000..bd60f4b
--- /dev/null
@@ -0,0 +1,4 @@
+float weaponswapping;
+float internalteam;
+
+void weapon_defaultspawnfunc(float wpn);
diff --git a/sound/weapons/gauntlet_fire.ogg b/sound/weapons/gauntlet_fire.ogg
deleted file mode 100644 (file)
index 03802f6..0000000
Binary files a/sound/weapons/gauntlet_fire.ogg and /dev/null differ
diff --git a/sound/weapons/gauntletbeam_fly.ogg b/sound/weapons/gauntletbeam_fly.ogg
deleted file mode 100644 (file)
index 874d246..0000000
Binary files a/sound/weapons/gauntletbeam_fly.ogg and /dev/null differ