]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into terencehill/itemstime
authorterencehill <piuntn@gmail.com>
Tue, 2 Oct 2012 17:08:40 +0000 (19:08 +0200)
committerterencehill <piuntn@gmail.com>
Tue, 2 Oct 2012 17:08:40 +0000 (19:08 +0200)
149 files changed:
balance25.cfg
balanceFruitieX.cfg
balanceXDF.cfg [new file with mode: 0644]
balanceXPM.cfg
balanceXonotic.cfg
build-compat-pack.sh [deleted file]
commands.cfg
cpp.cfg [new file with mode: 0644]
crosshairs.cfg
ctfscoring-ai.cfg
ctfscoring-alien.cfg [deleted file]
ctfscoring-alpha.cfg [deleted file]
ctfscoring-div0.cfg
ctfscoring-nex242.cfg
ctfscoring-samual.cfg [new file with mode: 0644]
ctfscoring-z-lowdeposit.cfg [deleted file]
ctfscoring-z.cfg [deleted file]
defaultXDF.cfg [new file with mode: 0644]
defaultXPM.cfg
defaultXonotic.cfg
effectinfo.txt
gamemodes.cfg [new file with mode: 0644]
gfx/vehicles/axh-rings.tga [new file with mode: 0644]
gfx/vehicles/bumb.tga [new file with mode: 0644]
gfx/vehicles/bumb_lgun.tga [new file with mode: 0644]
gfx/vehicles/bumb_rgun.tga [new file with mode: 0644]
gfx/vehicles/bumb_side.tga [new file with mode: 0644]
gfx/vehicles/bumb_side_gun.tga [new file with mode: 0644]
gfx/vehicles/energy.tga [new file with mode: 0644]
physicsXDF.cfg [new file with mode: 0644]
physicsXDFLight.cfg [new file with mode: 0644]
physicsXPM.cfg [deleted file]
physicsXPMLight.cfg [deleted file]
qcsrc/client/Main.qc
qcsrc/client/View.qc
qcsrc/client/announcer.qc
qcsrc/client/autocvars.qh
qcsrc/client/command/cl_cmd.qc
qcsrc/client/csqcmodel_hooks.qc
qcsrc/client/hud.qc
qcsrc/client/mapvoting.qc
qcsrc/client/miscfunctions.qc
qcsrc/client/progs.src
qcsrc/client/scoreboard.qc
qcsrc/client/tturrets.qc
qcsrc/client/vehicles/vehicles.qc
qcsrc/client/waypointsprites.qc
qcsrc/common/constants.qh
qcsrc/common/items.qh
qcsrc/common/util-pre.qh
qcsrc/common/util.qc
qcsrc/common/util.qh
qcsrc/dpdefs/csprogsdefs.qc
qcsrc/dpdefs/menudefs.qc
qcsrc/menu/draw.qc
qcsrc/menu/draw.qh
qcsrc/menu/item/borderimage.c
qcsrc/menu/item/label.c
qcsrc/menu/item/listbox.c
qcsrc/menu/menu.qc
qcsrc/menu/xonotic/colorbutton.c
qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.c
qcsrc/menu/xonotic/dialog_multiplayer_playersetup.c
qcsrc/menu/xonotic/playermodel.c
qcsrc/server/accuracy.qc
qcsrc/server/antilag.qc
qcsrc/server/attic/bot/havocbot/role_ctf.qc [new file with mode: 0644]
qcsrc/server/attic/bot/havocbot/role_freezetag.qc [new file with mode: 0644]
qcsrc/server/attic/bot/havocbot/role_keepaway.qc [new file with mode: 0644]
qcsrc/server/attic/ctf.qc [new file with mode: 0644]
qcsrc/server/attic/monsters/m_monsters.qc
qcsrc/server/attic/nexball.qc
qcsrc/server/autocvars.qh
qcsrc/server/bot/bot.qc
qcsrc/server/bot/havocbot/havocbot.qc
qcsrc/server/bot/havocbot/havocbot.qh
qcsrc/server/bot/havocbot/role_ctf.qc [deleted file]
qcsrc/server/bot/havocbot/role_freezetag.qc [deleted file]
qcsrc/server/bot/havocbot/role_keepaway.qc [deleted file]
qcsrc/server/bot/havocbot/roles.qc
qcsrc/server/bot/scripting.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/command/banning.qc
qcsrc/server/command/cmd.qc
qcsrc/server/command/common.qc
qcsrc/server/command/sv_cmd.qc
qcsrc/server/constants.qh
qcsrc/server/ctf.qc [deleted file]
qcsrc/server/defs.qh
qcsrc/server/domination.qc
qcsrc/server/g_damage.qc
qcsrc/server/g_hook.qc
qcsrc/server/g_subs.qc
qcsrc/server/g_triggers.qc
qcsrc/server/g_world.qc
qcsrc/server/miscfunctions.qc
qcsrc/server/mode_onslaught.qc [deleted file]
qcsrc/server/mutators/base.qh
qcsrc/server/mutators/gamemode_ctf.qc [new file with mode: 0644]
qcsrc/server/mutators/gamemode_ctf.qh [new file with mode: 0644]
qcsrc/server/mutators/gamemode_freezetag.qc
qcsrc/server/mutators/gamemode_keepaway.qc
qcsrc/server/mutators/gamemode_keepaway.qh [new file with mode: 0644]
qcsrc/server/mutators/gamemode_nexball.qc
qcsrc/server/mutators/gamemode_nexball.qh
qcsrc/server/mutators/gamemode_onslaught.qc [new file with mode: 0644]
qcsrc/server/mutators/mutator_superspec.qc [new file with mode: 0644]
qcsrc/server/mutators/mutators.qh
qcsrc/server/playerstats.qc
qcsrc/server/playerstats.qh
qcsrc/server/portals.qc
qcsrc/server/progs.src
qcsrc/server/scores_rules.qc
qcsrc/server/sv_main.qc
qcsrc/server/t_items.qc
qcsrc/server/t_jumppads.qc
qcsrc/server/t_plats.qc
qcsrc/server/t_quake3.qc
qcsrc/server/t_teleporters.qc
qcsrc/server/target_spawn.qc
qcsrc/server/teamplay.qc
qcsrc/server/tturrets/units/unit_ewheel.qc
qcsrc/server/tturrets/units/unit_walker.qc
qcsrc/server/vehicles/bumblebee.qc [new file with mode: 0644]
qcsrc/server/vehicles/racer.qc
qcsrc/server/vehicles/raptor.qc
qcsrc/server/vehicles/spiderbot.qc
qcsrc/server/vehicles/vehicles.qc
qcsrc/server/vehicles/vehicles.qh
qcsrc/server/vehicles/vehicles_def.qh
qcsrc/server/w_crylink.qc
qcsrc/warpzonelib/common.qc
qcsrc/warpzonelib/common.qh
sound/ctf/pass.wav [new file with mode: 0644]
sound/ctf/touch.wav [new file with mode: 0644]
textures/raptor.tga
textures/raptor_pants.jpg [deleted file]
textures/raptor_shirt.jpg [deleted file]
textures/raptor_shirt.tga [new file with mode: 0644]
vehicle_bumblebee.cfg
vehicle_racer.cfg
vehicle_raptor.cfg
vehicle_spiderbot.cfg
vehicles.cfg
xonotic-credits.txt

index aafb163b441941ea73ad9a0ba736837fcc0bc25f..068f650f8f443e841deb73004c25874a35495653 100644 (file)
@@ -451,6 +451,7 @@ 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
index 1bb8989120df04a0f5f3b7864c66c23ab0df2f57..5ccd6827b60626eb3574fa740d4ee2a46fdd8c15 100644 (file)
@@ -451,6 +451,7 @@ 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
diff --git a/balanceXDF.cfg b/balanceXDF.cfg
new file mode 100644 (file)
index 0000000..543a5c6
--- /dev/null
@@ -0,0 +1,680 @@
+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_balance_weaponswitchdelay 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_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_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_headshotaddeddamage 0
+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_headshotaddeddamage 0
+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_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_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_reload_ammo 0 //default: 20
+set g_balance_electro_reload_time 2
+// }}}
+// {{{ crylink 
+set g_balance_crylink_primary_damage 12
+set g_balance_crylink_primary_edgedamage 6
+set g_balance_crylink_primary_force -50
+set g_balance_crylink_primary_radius 80
+set g_balance_crylink_primary_speed 2000
+set g_balance_crylink_primary_spread 0.08
+set g_balance_crylink_primary_shots 6
+set g_balance_crylink_primary_bounces 1
+set g_balance_crylink_primary_refire 0.7
+set g_balance_crylink_primary_animtime 0.3
+set g_balance_crylink_primary_ammo 3
+set g_balance_crylink_primary_bouncedamagefactor 0.5
+set g_balance_crylink_primary_joindelay 0.1
+set g_balance_crylink_primary_joinspread 0.2
+set g_balance_crylink_primary_jointime 0
+set g_balance_crylink_primary_joinexplode 1
+set g_balance_crylink_primary_joinexplode_damage 0
+set g_balance_crylink_primary_joinexplode_edgedamage 0
+set g_balance_crylink_primary_joinexplode_radius 0
+set g_balance_crylink_primary_joinexplode_force 0
+set g_balance_crylink_primary_linkexplode 1
+
+set g_balance_crylink_primary_middle_lifetime 5 // range: 35000 full, fades to 70000
+set g_balance_crylink_primary_middle_fadetime 5
+set g_balance_crylink_primary_other_lifetime 5 
+set g_balance_crylink_primary_other_fadetime 5
+
+set g_balance_crylink_secondary 1
+set g_balance_crylink_secondary_damage 10
+set g_balance_crylink_secondary_edgedamage 5
+set g_balance_crylink_secondary_force -150
+set g_balance_crylink_secondary_radius 100
+set g_balance_crylink_secondary_speed 3000
+set g_balance_crylink_secondary_spread 0.01
+set g_balance_crylink_secondary_spreadtype 1
+set g_balance_crylink_secondary_shots 5
+set g_balance_crylink_secondary_bounces 0
+set g_balance_crylink_secondary_refire 0.7
+set g_balance_crylink_secondary_animtime 0.2
+set g_balance_crylink_secondary_ammo 2
+set g_balance_crylink_secondary_bouncedamagefactor 0.5
+set g_balance_crylink_secondary_joindelay 0
+set g_balance_crylink_secondary_joinspread 0
+set g_balance_crylink_secondary_jointime 0
+set g_balance_crylink_secondary_joinexplode 0                  
+set g_balance_crylink_secondary_joinexplode_damage 0   
+set g_balance_crylink_secondary_joinexplode_edgedamage 0
+set g_balance_crylink_secondary_joinexplode_radius 0
+set g_balance_crylink_secondary_joinexplode_force 0
+set g_balance_crylink_secondary_linkexplode 1
+
+set g_balance_crylink_secondary_middle_lifetime 5 // range: 35000 full, fades to 70000
+set g_balance_crylink_secondary_middle_fadetime 5
+set g_balance_crylink_secondary_line_lifetime 5 
+set g_balance_crylink_secondary_line_fadetime 5
+
+set g_balance_crylink_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_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_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_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_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_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 
+// }}}
+// {{{ 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
+// }}}
+// {{{ 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
+// }}}
index 759551d8d2357a4a2e53f7f1125d229659802fe7..cd440294c054be3d391685f4107a3e604979a08c 100644 (file)
@@ -395,9 +395,9 @@ 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 3
+set g_balance_electro_secondary_lifetime 4
 set g_balance_electro_secondary_refire 0.2
-set g_balance_electro_secondary_refire2 1.5
+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
@@ -409,7 +409,7 @@ 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 175
+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
@@ -419,7 +419,7 @@ 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 -60
+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
@@ -445,15 +445,16 @@ 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 5
-set g_balance_crylink_secondary_edgedamage 0
-set g_balance_crylink_secondary_force -40
-set g_balance_crylink_secondary_radius 70
-set g_balance_crylink_secondary_speed 2000
-set g_balance_crylink_secondary_spread 0.02
-set g_balance_crylink_secondary_shots 3
-set g_balance_crylink_secondary_bounces 1
-set g_balance_crylink_secondary_refire 0.2
+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
index 52c8a74d84cecfe381db5adbb156bc778f7da233..871b9a390f2a39fc8daa1b59d6f0bbe34f3b50fa 100644 (file)
@@ -395,9 +395,9 @@ 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 3
+set g_balance_electro_secondary_lifetime 4
 set g_balance_electro_secondary_refire 0.2
-set g_balance_electro_secondary_refire2 1.5
+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
@@ -409,7 +409,7 @@ 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 175
+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
@@ -419,7 +419,7 @@ 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 -60
+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
@@ -445,15 +445,16 @@ 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 5
-set g_balance_crylink_secondary_edgedamage 0
-set g_balance_crylink_secondary_force -40
-set g_balance_crylink_secondary_radius 70
-set g_balance_crylink_secondary_speed 2000
-set g_balance_crylink_secondary_spread 0.02
-set g_balance_crylink_secondary_shots 3
-set g_balance_crylink_secondary_bounces 1
-set g_balance_crylink_secondary_refire 0.2
+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
diff --git a/build-compat-pack.sh b/build-compat-pack.sh
deleted file mode 100755 (executable)
index e3772c5..0000000
+++ /dev/null
@@ -1,460 +0,0 @@
-#!/bin/sh
-
-# list of files v2.4.2 clients need to play on svn servers
-
-COMPAT_FILES="
-       effectinfo.txt
-       gfx/crosshairtuba.tga
-       gfx/hud/inv_weapon0.tga
-       gfx/hud/inv_weapon10.tga
-       gfx/hud/inv_weapon11.tga
-       gfx/hud/inv_weapon12.tga
-       gfx/hud/inv_weapon13.tga
-       gfx/hud/inv_weapon14.tga
-       gfx/hud/inv_weapon1.tga
-       gfx/hud/inv_weapon2.tga
-       gfx/hud/inv_weapon3.tga
-       gfx/hud/inv_weapon4.tga
-       gfx/hud/inv_weapon5.tga
-       gfx/hud/inv_weapon6.tga
-       gfx/hud/inv_weapon7.tga
-       gfx/hud/inv_weapon8.tga
-       gfx/hud/inv_weapon9.tga
-       gfx/hud/inv_weapon_hlacmod_renameit.tga
-       gfx/hud/keys/key_backward_inv.tga
-       gfx/hud/keys/key_backward.tga
-       gfx/hud/keys/key_bg.tga
-       gfx/hud/keys/key_crouch_inv.tga
-       gfx/hud/keys/key_crouch.tga
-       gfx/hud/keys/key_forward_inv.tga
-       gfx/hud/keys/key_forward.tga
-       gfx/hud/keys/key_jump_inv.tga
-       gfx/hud/keys/key_jump.tga
-       gfx/hud/keys/key_left_inv.tga
-       gfx/hud/keys/key_left.tga
-       gfx/hud/keys/key_right_inv.tga
-       gfx/hud/keys/key_right.tga
-       gfx/hud/num_0_stroke.tga
-       gfx/hud/num_0.tga
-       gfx/hud/num_1_stroke.tga
-       gfx/hud/num_1.tga
-       gfx/hud/num_2_stroke.tga
-       gfx/hud/num_2.tga
-       gfx/hud/num_3_stroke.tga
-       gfx/hud/num_3.tga
-       gfx/hud/num_4_stroke.tga
-       gfx/hud/num_4.tga
-       gfx/hud/num_5_stroke.tga
-       gfx/hud/num_5.tga
-       gfx/hud/num_6_stroke.tga
-       gfx/hud/num_6.tga
-       gfx/hud/num_7_stroke.tga
-       gfx/hud/num_7.tga
-       gfx/hud/num_8_stroke.tga
-       gfx/hud/num_8.tga
-       gfx/hud/num_9_stroke.tga
-       gfx/hud/num_9.tga
-       gfx/hud/num_colon_stroke.tga
-       gfx/hud/num_colon.tga
-       gfx/hud/num_dot_stroke.tga
-       gfx/hud/num_dot.tga
-       gfx/hud/num_minus_stroke.tga
-       gfx/hud/num_minus.tga
-       gfx/hud/num_plus_stroke.tga
-       gfx/hud/num_plus.tga
-       gfx/hud/rifle_ring_1.tga
-       gfx/hud/rifle_ring_2.tga
-       gfx/hud/rifle_ring_3.tga
-       gfx/hud/rifle_ring_4.tga
-       gfx/hud/rifle_ring_5.tga
-       gfx/hud/rifle_ring_6.tga
-       gfx/hud/rifle_ring_7.tga
-       gfx/hud/rifle_ring_8.tga
-       gfx/hud/sb_accuracy.tga
-       gfx/hud/sb_ammobg.tga
-       gfx/hud/sb_armor.tga
-       gfx/hud/sbar.tga
-       gfx/hud/sb_bullets.tga
-       gfx/hud/sb_cells.tga
-       gfx/hud/sb_flag_blue_carrying.tga
-       gfx/hud/sb_flag_blue_lost.tga
-       gfx/hud/sb_flag_blue_shielded.tga
-       gfx/hud/sb_flag_blue_taken.tga
-       gfx/hud/sb_flag_red_carrying.tga
-       gfx/hud/sb_flag_red_lost.tga
-       gfx/hud/sb_flag_red_shielded.tga
-       gfx/hud/sb_flag_red_taken.tga
-       gfx/hud/sb_fuel.tga
-       gfx/hud/sb_health.tga
-       gfx/hud/sb_highlight_1.tga
-       gfx/hud/sb_highlight_2.tga
-       gfx/hud/sb_highlight_3.tga
-       gfx/hud/sb_highlight_4.tga
-       gfx/hud/sb_invinc.tga
-       gfx/hud/sb_kh_blue.tga
-       gfx/hud/sb_kh_pink.tga
-       gfx/hud/sb_kh_red.tga
-       gfx/hud/sb_kh_yellow.tga
-       gfx/hud/sb_nexball_carrying.tga
-       gfx/hud/sb_rocket.tga
-       gfx/hud/sb_scoreboard_bg.tga
-       gfx/hud/sb_scoreboard_tableheader.tga
-       gfx/hud/sb_shells.tga
-       gfx/hud/sb_str.tga
-       gfx/hud/sb_timerbg.tga
-       models/ctf/shield.md3
-       models/ctf/shockwavetransring.md3
-       models/gibs/arm.md3
-       models/gibs/arm.md3_0.skin
-       models/gibs/arm.md3_1.skin
-       models/gibs/arm.md3_2.skin
-       models/gibs/bloodyskull.md3
-       models/gibs/bloodyskull.md3_0.skin
-       models/gibs/bloodyskull.md3_1.skin
-       models/gibs/bloodyskull.md3_2.skin
-       models/gibs/chest.md3
-       models/gibs/chest.md3_0.skin
-       models/gibs/chest.md3_1.skin
-       models/gibs/chest.md3_2.skin
-       models/gibs/chunk.mdl
-       models/gibs/eye.md3
-       models/gibs/leg1.md3
-       models/gibs/leg1.md3_0.skin
-       models/gibs/leg1.md3_1.skin
-       models/gibs/leg1.md3_2.skin
-       models/gibs/leg2.md3
-       models/gibs/leg2.md3_0.skin
-       models/gibs/leg2.md3_1.skin
-       models/gibs/leg2.md3_2.skin
-       models/gibs/smallchest.md3
-       models/gibs/smallchest.md3_0.skin
-       models/gibs/smallchest.md3_1.skin
-       models/gibs/smallchest.md3_2.skin
-       models/nexball/ball.md3
-       models/onslaught/boom.md3
-       models/onslaught/controlpoint_icon_dmg1.md3
-       models/onslaught/controlpoint_icon_dmg2.md3
-       models/onslaught/controlpoint_icon_dmg3.md3
-       models/onslaught/controlpoint_icon_gib1.md3
-       models/onslaught/controlpoint_icon_gib2.md3
-       models/onslaught/controlpoint_icon_gib4.md3
-       models/onslaught/controlpoint_pad2.md3
-       models/onslaught/generator_dead.md3
-       models/onslaught/generator_dmg1.md3
-       models/onslaught/generator_dmg2.md3
-       models/onslaught/generator_dmg3.md3
-       models/onslaught/generator_dmg4.md3
-       models/onslaught/generator_dmg5.md3
-       models/onslaught/generator_dmg6.md3
-       models/onslaught/generator_dmg7.md3
-       models/onslaught/generator_dmg8.md3
-       models/onslaught/generator_dmg9.md3
-       models/onslaught/gen_gib1.md3
-       models/onslaught/gen_gib2.md3
-       models/onslaught/gen_gib3.md3
-       models/onslaught/ons_ray.md3
-       models/onslaught/shockwave.md3
-       models/onslaught/shockwavetransring.md3
-       models/sprites/as-defend_frame0.tga
-       models/sprites/as-destroy_frame0.tga
-       models/sprites/as-push_frame0.tga
-       models/sprites/bluebase_frame0.tga
-       models/sprites/bluebase.tga
-       models/sprites/danger_frame0.tga
-       models/sprites/danger.tga
-       models/sprites/defend.tga
-       models/sprites/destroy.tga
-       models/sprites/dom-blue_frame0.tga
-       models/sprites/dom-neut_frame0.tga
-       models/sprites/dom-pink_frame0.tga
-       models/sprites/dom-red_frame0.tga
-       models/sprites/dom-yellow_frame0.tga
-       models/sprites/flagcarrier_frame0.tga
-       models/sprites/flagcarrier.tga
-       models/sprites/helpme_frame0.tga
-       models/sprites/helpme.tga
-       models/sprites/here_frame0.tga
-       models/sprites/here.tga
-       models/sprites/item-extralife_frame0.tga
-       models/sprites/item-extralife_frame1.tga
-       models/sprites/item-fuelregen_frame0.tga
-       models/sprites/item-fuelregen_frame1.tga
-       models/sprites/item-invis_frame0.tga
-       models/sprites/item-invis_frame1.tga
-       models/sprites/item-jetpack_frame0.tga
-       models/sprites/item-jetpack_frame1.tga
-       models/sprites/item-shield_frame0.tga
-       models/sprites/item-shield_frame1.tga
-       models/sprites/item-speed_frame0.tga
-       models/sprites/item-speed_frame1.tga
-       models/sprites/item-strength_frame0.tga
-       models/sprites/item-strength_frame1.tga
-       models/sprites/keycarrier-blue_frame0.tga
-       models/sprites/keycarrier-blue.tga
-       models/sprites/keycarrier-finish_frame0.tga
-       models/sprites/keycarrier-finish.tga
-       models/sprites/keycarrier-friend_frame0.tga
-       models/sprites/keycarrier-friend.tga
-       models/sprites/keycarrier-pink_frame0.tga
-       models/sprites/keycarrier-pink.tga
-       models/sprites/keycarrier-red_frame0.tga
-       models/sprites/keycarrier-red.tga
-       models/sprites/keycarrier-yellow_frame0.tga
-       models/sprites/keycarrier-yellow.tga
-       models/sprites/key-dropped_frame0.tga
-       models/sprites/key-dropped.tga
-       models/sprites/nb-ball_frame0.tga
-       models/sprites/ons-cp-atck-blue_frame0.tga
-       models/sprites/ons-cp-atck-blue_frame1.tga
-       models/sprites/ons-cp-atck-neut_frame0.tga
-       models/sprites/ons-cp-atck-neut_frame1.tga
-       models/sprites/ons-cp-atck-red_frame0.tga
-       models/sprites/ons-cp-atck-red_frame1.tga
-       models/sprites/ons-cp-blue_frame0.tga
-       models/sprites/ons-cp-blue.tga
-       models/sprites/ons-cp-dfnd-blue_frame0.tga
-       models/sprites/ons-cp-dfnd-blue_frame1.tga
-       models/sprites/ons-cp-dfnd-red_frame0.tga
-       models/sprites/ons-cp-dfnd-red_frame1.tga
-       models/sprites/ons-cp-neut_frame0.tga
-       models/sprites/ons-cp-neut.tga
-       models/sprites/ons-cp-red_frame0.tga
-       models/sprites/ons-cp-red.tga
-       models/sprites/ons-gen-blue_frame0.tga
-       models/sprites/ons-gen-blue.tga
-       models/sprites/ons-gen-red_frame0.tga
-       models/sprites/ons-gen-red.tga
-       models/sprites/ons-gen-shielded_frame0.tga
-       models/sprites/ons-gen-shielded.tga
-       models/sprites/push.tga
-       models/sprites/race-checkpoint_frame0.tga
-       models/sprites/race-checkpoint.tga
-       models/sprites/race-finish_frame0.tga
-       models/sprites/race-finish.tga
-       models/sprites/race-start_frame0.tga
-       models/sprites/redbase_frame0.tga
-       models/sprites/redbase.tga
-       models/sprites/waypoint_frame0.tga
-       models/sprites/waypoint.tga
-       models/sprites/wpn-campingrifle_frame0.tga
-       models/sprites/wpn-crylink_frame0.tga
-       models/sprites/wpn-electro_frame0.tga
-       models/sprites/wpn-gl_frame0.tga
-       models/sprites/wpn-hagar_frame0.tga
-       models/sprites/wpn-hlac_frame0.tga
-       models/sprites/wpn-hookgun_frame0.tga
-       models/sprites/wpn-laser_frame0.tga
-       models/sprites/wpn-minstanex_frame0.tga
-       models/sprites/wpn-nex_frame0.tga
-       models/sprites/wpn-porto_frame0.tga
-       models/sprites/wpn-rl_frame0.tga
-       models/sprites/wpn-shotgun_frame0.tga
-       models/sprites/wpn-uzi_frame0.tga
-       models/weapons/g_tuba.md3
-       models/weapons/h_tuba.dpm
-       models/weapons/v_tuba.md3
-       particles/particlefont.tga
-       scripts/onslaught.shader
-       scripts/tuba.shader
-       sound/announcer/male/amazing.ogg
-       sound/announcer/male/awesome.ogg
-       sound/ctf/blue_capture.wav
-       sound/ctf/blue_dropped.wav
-       sound/ctf/blue_returned.wav
-       sound/ctf/blue_taken.wav
-       sound/ctf/flag_respawn.wav
-       sound/ctf/red_capture.wav
-       sound/ctf/red_dropped.wav
-       sound/ctf/red_returned.wav
-       sound/ctf/red_taken.wav
-       sound/misc/armor10.wav
-       sound/misc/armor17_5.wav
-       sound/misc/armor1.wav
-       sound/misc/armor25.wav
-       sound/misc/itemrespawncountdown.ogg
-       sound/misc/poweroff.wav
-       sound/misc/powerup.ogg
-       sound/misc/shield_respawn.wav
-       sound/misc/strength_respawn.wav
-       sound/nexball/bounce.ogg
-       sound/nexball/drop.ogg
-       sound/nexball/shoot1.wav
-       sound/nexball/shoot2.ogg
-       sound/nexball/steal.ogg
-       sound/onslaught/electricity_explode.ogg
-       sound/onslaught/ons_hit1.ogg
-       sound/onslaught/ons_hit2.ogg
-       sound/onslaught/ons_spark1.ogg
-       sound/onslaught/ons_spark2.ogg
-       sound/onslaught/shockwave.ogg
-       sound/player/pyria-skadi/coms/needhelp2.ogg
-       sound/weapons/nexwhoosh1.ogg
-       sound/weapons/nexwhoosh2.ogg
-       sound/weapons/nexwhoosh3.ogg
-       sound/weapons/tuba_note0.ogg
-       sound/weapons/tuba_note-10.ogg
-       sound/weapons/tuba_note10.ogg
-       sound/weapons/tuba_note-11.ogg
-       sound/weapons/tuba_note11.ogg
-       sound/weapons/tuba_note-12.ogg
-       sound/weapons/tuba_note12.ogg
-       sound/weapons/tuba_note-13.ogg
-       sound/weapons/tuba_note13.ogg
-       sound/weapons/tuba_note-14.ogg
-       sound/weapons/tuba_note14.ogg
-       sound/weapons/tuba_note-15.ogg
-       sound/weapons/tuba_note15.ogg
-       sound/weapons/tuba_note-16.ogg
-       sound/weapons/tuba_note16.ogg
-       sound/weapons/tuba_note-17.ogg
-       sound/weapons/tuba_note17.ogg
-       sound/weapons/tuba_note-18.ogg
-       sound/weapons/tuba_note18.ogg
-       sound/weapons/tuba_note19.ogg
-       sound/weapons/tuba_note-1.ogg
-       sound/weapons/tuba_note1.ogg
-       sound/weapons/tuba_note20.ogg
-       sound/weapons/tuba_note21.ogg
-       sound/weapons/tuba_note22.ogg
-       sound/weapons/tuba_note23.ogg
-       sound/weapons/tuba_note24.ogg
-       sound/weapons/tuba_note25.ogg
-       sound/weapons/tuba_note26.ogg
-       sound/weapons/tuba_note27.ogg
-       sound/weapons/tuba_note-2.ogg
-       sound/weapons/tuba_note2.ogg
-       sound/weapons/tuba_note-3.ogg
-       sound/weapons/tuba_note3.ogg
-       sound/weapons/tuba_note-4.ogg
-       sound/weapons/tuba_note4.ogg
-       sound/weapons/tuba_note-5.ogg
-       sound/weapons/tuba_note5.ogg
-       sound/weapons/tuba_note-6.ogg
-       sound/weapons/tuba_note6.ogg
-       sound/weapons/tuba_note-7.ogg
-       sound/weapons/tuba_note7.ogg
-       sound/weapons/tuba_note-8.ogg
-       sound/weapons/tuba_note8.ogg
-       sound/weapons/tuba_note-9.ogg
-       sound/weapons/tuba_note9.ogg
-       sound/weapons/unavailable.wav
-       sound/weapons/weaponpickup.ogg
-       textures/bloodyskull_alien_glow.tga
-       textures/bloodyskull_alien.tga
-       textures/bloodyskull.jpg
-       textures/bloodyskull_robot_gloss.tga
-       textures/bloodyskull_robot_glow.tga
-       textures/bloodyskull_robot.tga
-       textures/generator_destroyed.tga
-       textures/generator_lightning2.tga
-       textures/generator_lightning.tga
-       textures/generator.tga
-       textures/meat_alien_gloss.tga
-       textures/meat_alien_glow.tga
-       textures/meat_alien_norm.tga
-       textures/meat_alien.tga
-       textures/meat_gloss.tga
-       textures/meat_norm.tga
-       textures/meat_robot_gloss.tga
-       textures/meat_robot_glow.tga
-       textures/meat_robot_norm.tga
-       textures/meat_robot.tga
-       textures/meat.tga
-       textures/nexball/ball_gloss.tga
-       textures/nexball/ball_norm.tga
-       textures/nexball/ball.tga
-       textures/ons_boom1.tga
-       textures/ons_gengib.tga
-       textures/ons_icon.tga
-       textures/ons_icon_thrust.tga
-       textures/ons_pad.tga
-       textures/ons_ray.tga
-       textures/ons_shockwave1.tga
-       textures/ons_shockwave2.tga
-       textures/ons_smoke1.tga
-       textures/ons_text.tga
-       textures/tuba_gloss.tga
-       textures/tuba_glow.tga
-       textures/tuba.tga
-       sound/weapons/fireball_fire2.wav
-       sound/weapons/fireball_fire.wav
-       sound/weapons/fireball_fly2.wav
-       sound/weapons/fireball_fly.wav
-       sound/weapons/fireball_impact2.wav
-       sound/weapons/fireball_prefire2.wav
-       models/weapons/g_fireball.md3
-       models/weapons/h_fireball.dpm
-       models/weapons/h_fireball.dpm.framegroups
-       models/weapons/v_fireball.md3
-       textures/fireball_gloss.tga
-       textures/fireball_glow.tga
-       textures/fireball.tga
-       models/sphere/sphere.md3
-       models/sphere/sphere.tga
-       textures/nutsandbolts1_gloss.tga
-       textures/nutsandbolts1.tga
-       textures/nutsandbolts3_gloss.tga
-       textures/nutsandbolts3.tga
-       textures/nutsandbolts4_gloss.tga
-       textures/nutsandbolts4.tga
-       textures/nutsandbolts5_gloss.tga
-       textures/nutsandbolts5.tga
-       models/gibs/robo1.md3
-       models/gibs/robo1.md3_0.skin
-       models/gibs/robo1.md3_1.skin
-       models/gibs/robo2.md3
-       models/gibs/robo2.md3_0.skin
-       models/gibs/robo2.md3_1.skin
-       models/gibs/robo3.md3
-       models/gibs/robo3.md3_0.skin
-       models/gibs/robo3.md3_1.skin
-       models/gibs/robo4.md3
-       models/gibs/robo4.md3_0.skin
-       models/gibs/robo4.md3_1.skin
-       models/gibs/robo5.md3
-       models/gibs/robo6.md3
-       models/gibs/robo7.md3
-       models/gibs/robo7.md3_0.skin
-       models/gibs/robo7.md3_1.skin
-       models/gibs/robo8.md3
-       models/gibs/robo8.md3_0.skin
-       models/gibs/robo8.md3_1.skin
-       models/gibs/robo.md3
-       models/gibs/robo.md3_0.skin
-       models/gibs/robo.md3_1.skin
-"
-
-rm -rf pack
-mkdir pack
-for F in $COMPAT_FILES; do
-       case "$F" in
-               */*)
-                       mkdir -p pack/${F%/*}
-                       ;;
-       esac
-       cp "$F" pack/"$F"
-done
-
-cd pack
-
-find textures/ -type f -print0 | qual=85 scaledown=256x256 xargs -0 ../../misc/tools/jpeg-if-not-alpha.sh
-
-if false; then
-       find . -name \*.ogg | while IFS= read -r NAME; do
-               c=`vorbiscomment -l "$NAME"`
-               oggdec -o "$NAME.wav" "$NAME"
-               oggenc -q 0 -o "$NAME" "$NAME.wav"
-               echo "$c" | vorbiscomment -w "$NAME"
-               rm -f "$NAME.wav"
-               touch "${NAME%.ogg}.wav" # to disable this file, should the client have it
-       done
-fi
-
-rev=`svnversion .. | sed 's/M$//g; s/.*://g;'`
-pack="zzz_svn-compat-$rev"
-echo "Support files to play on svn servers of revision $rev" > "$pack.txt"
-7za a -tzip -mx=9 "../$pack.pk3" .
-rm -f "$pack.txt"
-
-cd ..
-rm -rf pack
index 35d9e39b91947d5d17210434c6c047a6f8512131..881c0359aca39c8a9626f43d67b1b1b7bcae182f 100644 (file)
@@ -109,6 +109,7 @@ alias menu_loadmap_prepare "disconnect; wait; g_campaign 0; menu_cmd rpn /_menu_
 // ==========================================================
 // commented out commands are really only intended for internal use
 alias blurtest             "qc_cmd_cl     blurtest             ${* ?}" // Feature for testing blur postprocessing
+alias create_scrshot_ent   "qc_cmd_cl     create_scrshot_ent   ${* ?}" // Create an entity at this location for automatic screenshots
 alias debugmodel           "qc_cmd_cl     debugmodel           ${* ?}" // Spawn a debug model manually
 //alias handlevote         "qc_cmd_cl     handlevote           ${* ?}" // System to handle selecting a vote or option
 alias hud                  "qc_cmd_cl     hud                  ${* ?}" // Commands regarding/controlling the HUD system
diff --git a/cpp.cfg b/cpp.cfg
new file mode 100644 (file)
index 0000000..3a19053
--- /dev/null
+++ b/cpp.cfg
@@ -0,0 +1,42 @@
+alias _dont        ""
+alias _do          "$*"
+                  
+set _ifstack       ""
+alias #            "$_ifstack $*"
+
+alias #ifeq        "set \"_ifnew_$1\" _dont; set \"_ifnew_$2\" _do; _ifeq_2 \"_ifnew_$1\""
+alias #ifneq       "set \"_ifnew_$1\" _do; set \"_ifnew_$2\" _dont; _ifeq_2 \"_ifnew_$1\""
+alias _ifeq_2      "set _ifstack \"${$1} $_ifstack\""
+alias #else        "_else_2$_ifstack"
+alias _else_2_do   "set _ifstack \"_dont ${* q?}\""
+alias _else_2_dont "set _ifstack \"_do ${* q?}\""
+alias #endif       "_endif_2 $_ifstack"
+alias _endif_2     "set _ifstack \"${2- q?}\""
+
+alias #ifdef       "#ifneq \"${$1 ?}\" \"\""
+alias #ifndef      "#ifeq \"${$1 ?}\" \"\""
+
+alias #include     "# exec $*"
+alias #define      "# set $*"
+alias #undef       "# unset $*"
+alias #error       "# echo ERROR: $*; # quit"
+alias #warning     "# echo WARNING: $*"
+
+// EXAMPLE:
+//     #ifeq "$a" "$b"
+//     #ifeq "$a" "$c"
+//     #       echo "a == b == c"
+//     #else
+//     #       echo "a == b != c"
+//     #endif
+//     #else
+//     #ifeq "$a" "$c"
+//     #       echo "a == c != b"
+//     #else
+//     #ifeq "$b" "$c"
+//     #       echo "b == c != a"
+//     #else
+//     #       echo "a != b != c != a"
+//     #endif
+//     #endif
+//     #endif
index 7385853b1680a83916053e07d3335a2ed798e2bd..5e071a5874416416844c13623393c3dcd5ffb726 100644 (file)
@@ -27,6 +27,7 @@ seta crosshair_pickup_speed 4
 // hit indication animation settings
 seta crosshair_hitindication 0.5
 seta crosshair_hitindication_color "10 -10 -10"
+seta crosshair_hitindication_per_weapon_color "10 10 10"
 seta crosshair_hitindication_speed 5
 
 // hit testing/tracing for special effects for the crosshair
@@ -152,4 +153,4 @@ seta crosshair_ring_hagar_alpha 0.15
 // reload ring
 seta crosshair_ring_reload 1 "main cvar to enable or disable ammo crosshair rings"
 seta crosshair_ring_reload_size 2.5    "reload ring size"
-seta crosshair_ring_reload_alpha 0.2   "reload ring alpha"
\ No newline at end of file
+seta crosshair_ring_reload_alpha 0.2   "reload ring alpha"
index 711373647864160c111a36aec9e7295409e1a6bd..284aff7c5b00ce286893dea912bffc3fcb718d28 100644 (file)
@@ -1,19 +1,10 @@
-set g_ctf_personalscore_pickup_base                   1
-set g_ctf_personalscore_pickup_dropped_early          0
-set g_ctf_personalscore_pickup_dropped_late           1
-set g_ctf_personalscore_capture                      20
-set g_ctf_personalscore_kill                          5
-set g_ctf_personalpenalty_drop                        0
-set g_ctf_personalpenalty_suicidedrop                 1
-set g_ctf_personalpenalty_returned                    1
-set g_ctf_personalscore_return                        5
-set g_ctf_personalscore_return_rogue                  3
-set g_ctf_personalscore_return_by_killer              5
-set g_ctf_personalscore_return_rogue_by_killer        5
-// AWIN = 21
-// AFAIL = 0
-// AFAILVOID = 1
-// DWIN = 10
-// ARETRY = -1..0
-// DRETRY = 5
-// ATAKE = 1
+set g_ctf_score_capture 20
+set g_ctf_score_capture_assist 0
+set g_ctf_score_kill 5
+set g_ctf_score_penalty_drop 0
+set g_ctf_score_penalty_suicidedrop 1
+set g_ctf_score_penalty_returned 1
+set g_ctf_score_pickup_base 1
+set g_ctf_score_pickup_dropped_early 0
+set g_ctf_score_pickup_dropped_late 1
+set g_ctf_score_return 5
diff --git a/ctfscoring-alien.cfg b/ctfscoring-alien.cfg
deleted file mode 100644 (file)
index dd5758d..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-set g_ctf_personalscore_pickup_base                   1
-set g_ctf_personalscore_pickup_dropped_early          1
-set g_ctf_personalscore_pickup_dropped_late           1
-set g_ctf_personalscore_capture                      30
-set g_ctf_personalscore_kill                          1
-set g_ctf_personalpenalty_drop                        2
-set g_ctf_personalpenalty_suicidedrop                 2
-set g_ctf_personalpenalty_returned                    0
-set g_ctf_personalscore_return                        5
-set g_ctf_personalscore_return_rogue                 10
-set g_ctf_personalscore_return_by_killer              6
-set g_ctf_personalscore_return_rogue_by_killer       11
-// AWIN = 31
-// AFAIL = -1
-// AFAILVOID = 1
-// DWIN = 6..7
-// ARETRY = -1
-// DRETRY = 1
-// ATAKE = 1
diff --git a/ctfscoring-alpha.cfg b/ctfscoring-alpha.cfg
deleted file mode 100644 (file)
index c15cee8..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-set g_ctf_personalscore_pickup_base                   0
-set g_ctf_personalscore_pickup_dropped_early          0
-set g_ctf_personalscore_pickup_dropped_late           0
-set g_ctf_personalscore_capture                      20
-set g_ctf_personalscore_kill                          0
-set g_ctf_personalpenalty_drop                        0
-set g_ctf_personalpenalty_suicidedrop                 0
-set g_ctf_personalpenalty_returned                    0
-set g_ctf_personalscore_return                        5
-set g_ctf_personalscore_return_rogue                 10
-set g_ctf_personalscore_return_by_killer              5
-set g_ctf_personalscore_return_rogue_by_killer       10
-// AWIN = 20
-// AFAIL = 0
-// AFAILVOID = 0
-// DWIN = 5
-// ARETRY = 0
-// DRETRY = 0
-// ATAKE = 0
index 33c8ab1b72bd9f7d7b7c0da352db959719a74a43..f361d189523a5a5a894b7f9080ed8794ac8fa545 100644 (file)
@@ -1,19 +1,10 @@
-set g_ctf_personalscore_pickup_base                   0
-set g_ctf_personalscore_pickup_dropped_early          1
-set g_ctf_personalscore_pickup_dropped_late           1
-set g_ctf_personalscore_capture                      25
-set g_ctf_personalscore_kill                          3
-set g_ctf_personalpenalty_drop                        2
-set g_ctf_personalpenalty_suicidedrop                 2
-set g_ctf_personalpenalty_returned                    1
-set g_ctf_personalscore_return                        2 // lowered so it's better if the killer does the return
-set g_ctf_personalscore_return_rogue                 10
-set g_ctf_personalscore_return_by_killer              5
-set g_ctf_personalscore_return_rogue_by_killer       10
-// AWIN = 25
-// AFAIL = -3
-// AFAILVOID = -2
-// DWIN = 8 (5 if someone else returned)
-// ARETRY = -1
-// DRETRY = 3
-// ATAKE = 0
+set g_ctf_score_capture 25
+set g_ctf_score_capture_assist 0
+set g_ctf_score_kill 3
+set g_ctf_score_penalty_drop 2
+set g_ctf_score_penalty_suicidedrop 2
+set g_ctf_score_penalty_returned 1
+set g_ctf_score_pickup_base 0
+set g_ctf_score_pickup_dropped_early 1
+set g_ctf_score_pickup_dropped_late 1
+set g_ctf_score_return 2 // lowered so it's better if the killer does the return
index 861ead4613d165ecef7681adb90dc87f691dc2bb..7461dffc003347e7ba5da83b0e2273fbe8d8476a 100644 (file)
@@ -1,19 +1,10 @@
-set g_ctf_personalscore_pickup_base                   1
-set g_ctf_personalscore_pickup_dropped_early          1
-set g_ctf_personalscore_pickup_dropped_late           1
-set g_ctf_personalscore_capture                      20
-set g_ctf_personalscore_kill                          1
-set g_ctf_personalpenalty_drop                        0
-set g_ctf_personalpenalty_suicidedrop                 1
-set g_ctf_personalpenalty_returned                    0
-set g_ctf_personalscore_return                        5
-set g_ctf_personalscore_return_rogue                 10
-set g_ctf_personalscore_return_by_killer              5
-set g_ctf_personalscore_return_rogue_by_killer       10
-// AWIN = 21
-// AFAIL = 1
-// AFAILVOID = 1
-// DWIN = 6
-// ARETRY = 1
-// DRETRY = 1
-// ATAKE = 1
+set g_ctf_score_capture 20
+set g_ctf_score_capture_assist 0
+set g_ctf_score_kill 1
+set g_ctf_score_penalty_drop 0
+set g_ctf_score_penalty_suicidedrop 1
+set g_ctf_score_penalty_returned 0
+set g_ctf_score_pickup_base 1
+set g_ctf_score_pickup_dropped_early 1
+set g_ctf_score_pickup_dropped_late 1
+set g_ctf_score_return 5
diff --git a/ctfscoring-samual.cfg b/ctfscoring-samual.cfg
new file mode 100644 (file)
index 0000000..9bc87c2
--- /dev/null
@@ -0,0 +1,10 @@
+set g_ctf_score_capture 20
+set g_ctf_score_capture_assist 10
+set g_ctf_score_kill 5
+set g_ctf_score_penalty_drop 1
+set g_ctf_score_penalty_suicidedrop 1
+set g_ctf_score_penalty_returned 1
+set g_ctf_score_pickup_base 1
+set g_ctf_score_pickup_dropped_early 1
+set g_ctf_score_pickup_dropped_late 1
+set g_ctf_score_return 10
diff --git a/ctfscoring-z-lowdeposit.cfg b/ctfscoring-z-lowdeposit.cfg
deleted file mode 100644 (file)
index c75b306..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-set g_ctf_personalscore_pickup_base                  -1
-set g_ctf_personalscore_pickup_dropped_early          5
-set g_ctf_personalscore_pickup_dropped_late           9
-set g_ctf_personalscore_capture                      26
-set g_ctf_personalscore_kill                          5
-set g_ctf_personalpenalty_drop                        9
-set g_ctf_personalpenalty_suicidedrop                 9
-set g_ctf_personalpenalty_returned                    0
-set g_ctf_personalscore_return                        3
-set g_ctf_personalscore_return_rogue                 10
-set g_ctf_personalscore_return_by_killer              3
-set g_ctf_personalscore_return_rogue_by_killer       10
-// AWIN = 25
-// AFAIL = -10
-// AFAILVOID = -10
-// DWIN = 8
-// ARETRY = -1..-4
-// DRETRY = 5
-// ATAKE = -1
diff --git a/ctfscoring-z.cfg b/ctfscoring-z.cfg
deleted file mode 100644 (file)
index e45bdd9..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-set g_ctf_personalscore_pickup_base                  -5
-set g_ctf_personalscore_pickup_dropped_early          1
-set g_ctf_personalscore_pickup_dropped_late           5
-set g_ctf_personalscore_capture                      30
-set g_ctf_personalscore_kill                          5
-set g_ctf_personalpenalty_drop                        5
-set g_ctf_personalpenalty_suicidedrop                 5
-set g_ctf_personalpenalty_returned                    0
-set g_ctf_personalscore_return                        3
-set g_ctf_personalscore_return_rogue                 10
-set g_ctf_personalscore_return_by_killer              3
-set g_ctf_personalscore_return_rogue_by_killer       10
-// AWIN = 25
-// AFAIL = -10
-// AFAILVOID = -10
-// DWIN = 8
-// ARETRY = -1..-4
-// DRETRY = 5
-// ATAKE = -5
diff --git a/defaultXDF.cfg b/defaultXDF.cfg
new file mode 100644 (file)
index 0000000..f5a5442
--- /dev/null
@@ -0,0 +1,28 @@
+// ================
+//  Xonotic Defrag
+// ================
+
+exec defaultXonotic.cfg
+exec balanceXDF.cfg
+exec physicsXDF.cfg
+
+// general gameplay
+set g_jump_grunt 1 // make enemies even easier to hear when they're jumping around
+set g_shootfromcenter 1 // hit where you point at with the crosshair (almost so, no shooteye because it's really ugly)
+set g_balance_kill_antispam 0 
+set g_forced_respawn 1
+set g_jump_grunt 1
+// g_playerclip_collisions 0 // do not check playerclips
+set g_powerups 0  // set to -1 or patch xonotic
+set g_spawnpoints_auto_move_out_of_solid 1
+set g_start_delay 3
+set g_use_ammunition 0 "if set to 0 all weapons have unlimited ammunition"
+set g_weapon_stay 1 "1: ghost weapons can be picked up too but give no ammo, 2: ghost weapons refill ammo to one pickup size, thrown guns have no ammo"
+set teamplay_mode 2 // friendly fire and self damage
+set sv_vote_nospectators 1
+set timelimit_override 20
+
+// game mode settings
+set g_cts_finish_kill_delay 2
+set g_cts_respawn_delay 0
+set g_cts_selfdamage 0
index 38d74c7c990f1dd7ed047c427b108e4e7f83944c..0e96e08848587709836ed01eec9f470f187a4814 100644 (file)
@@ -1,31 +1,21 @@
-// Xonotic ProMode
-exec defaultXonotic.cfg
-
-//==============
-// pure changes
-//==============
-
-// players
-sv_fbskin_green // visible playermodel forced on everyone
-set teamplay_mode 2 // friendly fire and self damage
-
-//================
-// impure changes
-//================
+// ==================
+//  Xonotic Pro-Mode
+// ==================
 
-// players
-g_jump_grunt 1 // make enemies even easier to hear when they're jumping around
-
-// physics
-exec physicsXPM.cfg // XPM physics. Similar to vanilla Xonotic physics, with a different way to accelerate: through strafejumping
+exec defaultXonotic.cfg
+exec balanceXPM.cfg
 
-// balance
-set g_balance_weaponswitchdelay 0 // no switch animation, this is standard in "pro modes" ;)
+// general gameplay
+set g_norecoil 1 
 set g_shootfromcenter 1 // hit where you point at with the crosshair (almost so, no shooteye because it's really ugly)
-
-// match rules
-set timelimit_overtimes 1 // overtimes on, draw matches are less interesting! :)
-set g_forced_respawn 1 // no delaying/cheating a match by not spawning
-
-// info
-set sv_fraginfo_stats 0 // don't reveal how much health/armor the attacker had
+set g_balance_kill_antispam 0
+set g_forced_respawn 1
+set teamplay_mode 2 // friendly fire and self damage
+set sv_vote_nospectators 1
+set g_chat_nospectators 2
+set g_warmup 1
+set g_balance_teams 0
+set g_spawnshieldtime 0
+set sv_autoscreenshot 1
+set sv_ready_restart 1
+set sv_ready_restart_after_countdown 1
index b93457ac2d6cc57d66e46f15fe4ef1b57ef731b9..3c269391e8d5d656d93d0b25a0245fe5ec9bc3e3 100644 (file)
@@ -31,15 +31,6 @@ seta cl_startcount 0 "how many times the client has been run"
 
 seta g_configversion 0 "Configuration file version (used to upgrade settings) 0: first run, or previous start was <2.4.1  Later, it's overridden by config.cfg, version ranges are defined in config_update.cfg"
 
-// say aliases
-alias asay_ctf_flagcarrier "say_team flag carrier at %y"
-alias asay_ctf_haveflag "say_team (%l) have the flag"
-alias asay_willgo "say_team will go to %y"
-alias asay_support "say_team (%l) need help, %h%%"
-alias asay_killed "say_team got killed at %d"
-alias asay_noammo "say_team (%l) need %W for %w"
-alias asay_drop "say_team (%l) dropped %w ; impulse 17"
-
 // other aliases
 alias +hook +button6
 alias -hook -button6
@@ -57,7 +48,7 @@ bind f6 team_auto
 mod_q3bsp_lightmapmergepower 4
 
 // player defaults
-_cl_color 112
+_cl_color "112.211" // same effect as 112, but menuqc can detect this as the default and not intentionally set
 _cl_name Player
 _cl_playermodel models/player/erebus.iqm
 _cl_playerskin 0
@@ -146,6 +137,14 @@ gl_polyblend 0 // whether to use screen tints, this has now been replaced by a b
 r_motionblur 0 // motion blur value, default is 0
 r_damageblur 0 // motion blur when damaged, default is 0 (removed in Xonotic)
 
+r_bloom_blur 4
+r_bloom_brighten 2
+r_bloom_colorexponent 1
+r_bloom_colorscale 1
+r_bloom_colorsubtract 0.125
+r_bloom_resolution 320
+r_bloom_scenebrightness 0.85
+
 seta vid_x11_display ""        "xonotic-linux-*.sh will use this to start xonotic on an other/new X display"
 // This can have three possible settings:
 //     ""              run as usual
@@ -455,45 +454,6 @@ set sv_dodging_height_threshold 10 "the maximum height above ground where to all
 set sv_dodging_wall_distance_threshold 10 "the maximum distance from a wall that still allows dodging"
 set sv_dodging_sound 1 "if 1 dodging makes a sound. if 0 dodging is silent"
 
-set leadlimit 0
-set leadlimit_and_fraglimit 0 "if set, leadlimit is ANDed with fraglimit (otherwise ORed)"
-
-// this means that timelimit can be overidden globally and fraglimit can be overidden for each game mode: DM/TDM, Domination, CTF, and Runematch.
-seta timelimit_override -1     "Time limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta fraglimit_override -1     "Frag limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta leadlimit_override -1     "Lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta capturelimit_override -1  "Capture limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta captureleadlimit_override -1      "Capture llead imit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta g_arena_point_limit -1    "Arena point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta g_arena_point_leadlimit -1        "Arena point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta g_domination_point_limit -1       "Domination point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta g_domination_point_leadlimit -1   "Domination point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta g_runematch_point_limit -1        "Runematch point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta g_runematch_point_leadlimit -1    "Runematch point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta g_keyhunt_point_limit -1  "Keyhunt point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta g_keyhunt_point_leadlimit -1      "Keyhunt point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta g_race_laps_limit -1      "Race laps limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta g_nexball_goallimit -1 "Nexball goal limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta g_nexball_goalleadlimit -1 "Nexball goal lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta g_nexball_safepass_maxdist 5000    // Max distance to allow save fassping (0 to turn off safe passing)
-seta g_nexball_safepass_turnrate 0.1    // How fast the safe-pass ball can habge direction
-seta g_nexball_safepass_holdtime 0.75   // How long to remeber last teammate you pointed at
-seta g_nexball_viewmodel_scale 0.25     // How large the ball for the carrier
-seta g_nexball_viewmodel_offset "8 8 0" // Where the ball is located on carrier "forward right up"
-seta g_nexball_tackling 1               // Allow ball theft?
-
-
-seta g_ctf_ignore_frags 0      "1: regular frags give no points"
-
-set g_freezetag 0 "Freeze Tag: Freeze the opposing team(s) to win, unfreeze teammates by standing next to them"
-seta g_freezetag_warmup 5 "Time players get to run around before the round starts"
-seta g_freezetag_point_limit -1        "Freeze Tag point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta g_freezetag_point_leadlimit -1    "Freeze Tag point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta g_freezetag_revive_speed 0.4 "Speed for reviving a frozen teammate"
-seta g_freezetag_revive_clearspeed 1.6 "Speed at which reviving progress gets lost when out of range"
-seta g_freezetag_revive_extra_size 100 "Distance in qu that you can stand from a frozen teammate to keep reviving him"
-seta g_freezetag_frozen_force 0.6 "How much to multiply the force on a frozen player with"
-
 set g_spawn_furthest 0.5 "this amount of the spawns shall be far away from any players"
 set g_spawn_useallspawns 0 "use all spawns, e.g. also team spawns in non-teamplay, and all spawns, even enemy spawns, in teamplay"
 set g_spawn_near_teammate 0 "if set, players prefer spawns near a team mate"
@@ -501,60 +461,6 @@ set g_spawn_near_teammate_distance 640 "max distance to consider a spawn to be n
 // respawn delay
 set g_respawn_delay 2 "number of seconds you have to wait before you can respawn again"
 set g_respawn_waves 0 "respawn in waves (every n seconds), intended to decrease overwhelming base attacks"
-// when variables are set to 0, they take over the global setting...
-// to force disable delay or waves, set them to 0.125
-set g_ctf_respawn_delay 0
-set g_ctf_respawn_waves 0
-set g_ctf_weapon_stay 0
-set g_dm_respawn_delay 0
-set g_dm_respawn_waves 0
-set g_dm_weapon_stay 0
-set g_dom_respawn_delay 0
-set g_dom_respawn_waves 0
-set g_dom_weapon_stay 0
-set g_lms_respawn_delay 0
-set g_lms_respawn_waves 0
-set g_lms_weapon_stay 0
-set g_rune_respawn_delay 0
-set g_rune_respawn_waves 0
-set g_rune_weapon_stay 0
-set g_tdm_respawn_delay 0
-set g_tdm_respawn_waves 0
-set g_tdm_weapon_stay 0
-set g_ka_respawn_delay 0
-set g_ka_respawn_waves 0
-set g_ka_weapon_stay 0
-set g_kh_respawn_delay 0
-set g_kh_respawn_waves 0
-set g_kh_weapon_stay 0
-set g_arena_respawn_delay 0
-set g_arena_respawn_waves 0
-set g_arena_weapon_stay 0
-set g_ca_respawn_delay 0
-set g_ca_respawn_waves 0
-set g_ca_weapon_stay 0
-set g_ca_damage2score_multiplier 0.01
-set g_ca_round_timelimit 180
-set g_nb_respawn_delay 0
-set g_nb_respawn_waves 0
-set g_nb_weapon_stay 0
-set g_as_respawn_delay 0
-set g_as_respawn_waves 0
-set g_as_weapon_stay 0
-set g_ons_respawn_delay 0
-set g_ons_respawn_waves 0
-set g_ons_weapon_stay 0
-set g_rc_respawn_waves 0
-set g_rc_respawn_delay 0
-set g_rc_weapon_stay 0
-set g_cts_respawn_waves 0
-set g_cts_respawn_delay 0
-set g_cts_selfdamage 1 "0 = disable all selfdamage and falldamage in cts"
-set g_cts_finish_kill_delay 10 "prevent cheating by running back to the start line, and starting out with more speed than otherwise possible"
-set g_cts_weapon_stay 2
-set g_ft_respawn_waves 0
-set g_ft_respawn_delay 0
-set g_ft_weapon_stay 0
 
 // overtime
 seta timelimit_overtime 2 "duration in minutes of one added overtime, added to the timelimit"
@@ -574,215 +480,14 @@ seta g_friendlyfire_virtual_force 1      "for teamplay 4: apply force even though dam
 seta g_teamdamage_threshold 40 "for teamplay 4: threshold over which to apply mirror damage"
 seta g_teamdamage_resetspeed 20        "for teamplay 4: how fast player's teamdamage count decreases"
 
-seta g_balance_teams 0 "automatically balance out players entering instead of asking them for their preferred team"
-seta g_balance_teams_force 0   "automatically balance out teams when players move or disconnect"
-seta g_balance_teams_prevent_imbalance 0       "prevent players from changing to larger teams"
-set g_tdm_teams 2 "how many teams are in team deathmatch (set by mapinfo)"
-seta g_tdm_teams_override 0    "how many teams are in team deathmatch"
-set g_tdm_team_spawns 0 "when 1, a map can define team spawnpoints for TDM"
+seta g_balance_teams 1 "automatically balance out players entering instead of asking them for their preferred team"
+seta g_balance_teams_prevent_imbalance 1       "prevent players from changing to larger teams"
+set g_balance_teams_scorefactor 0.34 "at the end of the game, take score into account instead of team size by this amount (beware: values over 0.5 mean that a x:0 score imbalance will cause ALL new players to prefer the losing team at the end, despite numbers)"
 set g_changeteam_banned 0      "not allowed to change team"
 set g_changeteam_fragtransfer 0        "% of frags you get to keep when you change teams (rounded down)"
 
 set sv_teamnagger 1 "enable a nag message when the teams are unbalanced"
 
-// dm
-set g_dm 1 "Deathmatch: killing any other player is one frag, player with most frags wins"
-set gamecfg 1  // "deathmatch"
-
-// ctf
-set g_ctf 0 "Capture The Flag: take the enemy flag and bring it to yours at your base to score"
-set g_ctf_flag_returntime 30
-set g_ctf_flagcarrier_selfdamage 1
-set g_ctf_flagcarrier_selfforce 1
-set g_ctf_fullbrightflags 0
-set g_ctf_dynamiclights 0
-set g_ctf_allow_drop 1 "dropping allows circumventing carrierkill score, so enable this with care!"
-set g_ctf_reverse 0    "if enabled, flags positions are switched: you have to capture the enemy's flag from your own base by bringing it to your own flag in the enemy base"
-set g_balance_ctf_delay_collect 1.0
-set g_balance_ctf_damageforcescale 1
-
-set g_ctf_shield_max_ratio 0   "shield at most this percentage of a team from the enemy flag (try: 0.4 for 40%)"
-set g_ctf_shield_min_negscore 20       "shield the player from the flag if he's got this negative amount of points or less"
-set g_ctf_shield_force 100     "push force of the shield"
-
-// fun for server admins
-set g_ctf_flag_red_model "models/ctf/flags.md3"
-set g_ctf_flag_red_skin 0
-set g_ctf_flag_blue_model "models/ctf/flags.md3"
-set g_ctf_flag_blue_skin 1
-set g_ctf_flag_glowtrails 0
-set g_ctf_flag_pickup_effects 1
-set g_ctf_flag_capture_effects 1
-set g_ctf_captimerecord_always 0 "if enabled, assisted CTF records (with other players on the server) are recorded too"
-
-// runematch
-set g_runematch                                                0 "Runematch: pick up and hold the runes, special items that give you points, a special power (rune) and a disadvantage (curse)"
-set g_runematch_pointrate                              5
-set g_runematch_fixedspawns                            1 "use fixed runematch spawns if available"
-set g_runematch_pointamt                                       1
-set g_runematch_shuffletime                            30 "how often runes change position"
-set g_runematch_respawntime                            15 "how soon after being dropped to respawn"
-set g_runematch_frags_killedby_runeholder              4
-set g_runematch_frags_killed_runeholder                        5
-set g_runematch_frags_norune                           0
-set g_runematch_drop_runes_max                         2 "only drop up to 2 runes, the rest should respawn"
-set g_runematch_allow_same                             0 "allow matching rune-curse pairs"
-set g_runematch_rune_alpha                             0.78
-set g_runematch_rune_effects                           544 "EF_ADDITIVE + EF_FULLBRIGHT = 544"
-set g_runematch_rune_glow_size                         0
-set g_runematch_rune_glow_color                                0
-set g_runematch_rune_color_strength                    1.0
-// strength/weakness
-set g_balance_rune_strength_damage                     2.0
-set g_balance_rune_strength_force                      1.5
-set g_balance_curse_weak_damage                                0.5
-set g_balance_curse_weak_force                         0.6
-set g_balance_rune_strength_combo_damage       0.9
-set g_balance_rune_strength_combo_force                        1.0
-// defense/vulner
-set g_balance_rune_defense_takedamage                  0.5
-set g_balance_curse_vulner_takedamage                  2.0
-set g_balance_rune_defense_combo_takedamage            1.0
-// vampire/empathy
-set g_balance_rune_vampire_absorb                      0.4
-set g_balance_curse_empathy_takedamage                 -0.4
-set g_balance_rune_vampire_combo_absorb                        -0.1
-set g_balance_rune_vampire_maxhealth                   500
-set g_balance_curse_empathy_minhealth                  20
-set g_balance_rune_vampire_combo_minhealth             40
-// regen/venom
-set g_balance_rune_regen_hpmod                         1.75
-set g_balance_curse_venom_hpmod                                0.6
-set g_balance_rune_regen_combo_hpmod                   0.9
-set g_balance_rune_regen_regenrate                     3.0
-set g_balance_curse_venom_rotrate                      3.0
-set g_balance_rune_regen_combo_regenrate       0.5
-set g_balance_rune_regen_combo_rotrate                 1.5
-set g_balance_rune_regen_limitmod                      1
-set g_balance_curse_venom_limitmod                     1
-set g_balance_rune_regen_combo_limitmod                        1
-// speed/slow
-set g_balance_rune_speed_atkrate                               0.66
-set g_balance_curse_slow_atkrate                               1.5
-set g_balance_rune_speed_combo_atkrate                 1.2
-set g_balance_rune_speed_highspeed                     1.5
-set g_balance_curse_slow_highspeed                     0.6
-set g_balance_rune_speed_combo_highspeed                       0.9
-
-// domination
-set g_domination                       0 "Domination: capture and hold control points to gain points"
-set g_domination_default_teams         2 "default number of teams for maps that aren't domination-specific"
-seta g_domination_teams_override               0 "use a specific number of teams in domination games (minimum 2), disables dom_team entities"
-set g_domination_disable_frags         0 "players can't get frags normally, only get points from kills"
-set g_domination_point_amt             0 "override: how many points to get per ping"
-set g_domination_point_fullbright      0 "domination point fullbright"
-set g_domination_point_rate            0 "override: how often to give those points"
-set g_domination_point_capturetime     0.1 "how long it takes to capture a point (given no interference)"
-set g_domination_point_glow            0 "domination point glow (warning, slow)"
-//set g_domination_balance_team_points 1 "# of points received is based on team sizes"
-
-// last man standing
-set g_lms 0 "Last Man Standing: everyone starts with a certain amount of lives, and the survivor wins"
-set g_lms_lives_override -1
-set g_lms_regenerate 0
-set g_lms_campcheck_interval 10
-set g_lms_campcheck_message "^1Don't camp!"
-set g_lms_campcheck_damage 100
-set g_lms_campcheck_distance 1800
-set g_lms_last_join 3  "if g_lms_join_anytime is false, new players can only join if the worst active player has more than (fraglimit - g_lms_last_join) lives"
-set g_lms_join_anytime 1       "if true, new players can join, but get same amount of lives as the worst player"
-
-// arena
-set g_arena 0 "Arena: many one-on-one rounds are played to find the winner"
-set g_arena_maxspawned 2       "maximum number of players to spawn at once (the rest is spectating, waiting for their turn)"
-set g_arena_roundbased 1       "if disabled, the next player will spawn as soon as someone dies"
-set g_arena_warmup 5   "time, newly spawned players have to prepare themselves in round based matches"
-
-// ca
-set g_ca 0 "Clan Arena: Played in rounds, once you're dead you're out! The team with survivors wins the round."
-set g_ca_point_limit 10 "point limit 10 is standard for clan arena"
-set g_ca_point_leadlimit 0
-set g_ca_spectate_enemies 0 "Allow spectating enemy player by dead player during clan arena games."
-set g_ca_warmup 10 "how long the players will have time to run around the map before the round starts"
-
-// onslaught
-set g_onslaught 0 "Onslaught: take control points towards the enemy generator and then destroy it"
-set g_onslaught_gen_health 2500
-set g_onslaught_cp_health 1000
-set g_onslaught_cp_buildhealth 100
-set g_onslaught_cp_buildtime 5
-set g_onslaught_cp_regen 20
-
-// assault
-set g_assault 0 "Assault: attack the enemy base as fast as you can, then defend the base against the enemy for that time to win"
-
-// race
-set g_race 0 "Race: be faster than your opponents"
-set g_race_qualifying_timelimit 0
-set g_race_qualifying_timelimit_override -1
-set g_race_teams 0     "when 2, 3, or 4, the race is played as a team game (the team members can add up their laps)"
-
-// cts
-set g_cts 0 "CTS: complete the stage"
-
-// nexball
-set g_nexball 0 "Nexball: Basketball and Soccer go Xonotic"
-
-set g_nexball_basketball_effects_default     8    "default: dim light. The original version used 1024 (fire) but it gives bad performance"
-set g_balance_nexball_primary_speed       1000    "launching speed"
-set g_balance_nexball_primary_refire         0.7  "launching refire"
-set g_balance_nexball_primary_animtime       0.3  "launching animtime"
-set g_balance_nexball_secondary_animtime     0.3  "launching animtime"
-set g_balance_nexball_secondary_speed     3000    "stealing projectile speed"
-set g_balance_nexball_secondary_lifetime     0.15 "stealing projectile lifetime"
-set g_balance_nexball_secondary_force      500    "stealing projectile force"
-set g_balance_nexball_secondary_refire       0.6  "stealing projectile refire"
-set g_balance_nexball_secondary_animtime     0.3  "stealing projectile animtime"
-
-// -1: MrBougo's first try, not very playable but working...
-//     The ball gets the player's velocity * 1.5 + a vertical boost
-//  0: Revenant style
-//     Player's velocity + a boost where he's looking at + a boost
-//     perpendicularly to the first boost, that is upwards relatively
-//     to the view angle
-//  1: MrBougo's modded Rev style 1
-//     The 2nd Rev boost is always vertical
-//  2: MrBougo's modded Rev style 2
-//     The 1st Rev boost is always horizontal
-//     The 2nd Rev boost is always vertical
-set g_nexball_football_physics  2  "0: Revenant's original movement, 1: 0 but half independant of aiming height, 2: 1 fully independant, -1: first recode try"
-set g_nexball_basketball_bouncefactor 0.6    "velocity loss when the ball bounces"
-set g_nexball_basketball_bouncestop   0.075  "speed at which the ball stops when it hits the ground (multiplied by sv_gravity)"
-set g_nexball_football_bouncefactor   0.6    "velocity loss when the ball bounces"
-set g_nexball_football_bouncestop     0.075  "speed at which the ball stops when it hits the ground (multiplied by sv_gravity)"
-
-set g_nexball_football_boost_forward      100   "forward velocity boost when the ball is touched"
-set g_nexball_football_boost_up           200   "vertical velocity boost when the ball is touched"
-
-set g_nexball_basketball_delay_hold           20    "time before a player who caught the ball loses it (anti-ballcamp)"
-set g_nexball_basketball_delay_hold_forteam   60    "time before a ball reset when a team holds the ball for too long"
-set g_nexball_basketball_teamsteal             1    "1 to allow players to steal from teammates, 0 to disallow"
-
-set g_nexball_basketball_carrier_highspeed         0.8  "speed multiplier for the ballcarrier"
-
-set g_nexball_meter_period                  1    "time to make a full cycle on the power meter"
-set g_nexball_basketball_meter              1    "use the power meter for basketball"
-set g_nexball_basketball_meter_minpower   0.5    "minimal multiplier to the launching speed when using the power meter"
-set g_nexball_basketball_meter_maxpower   1.2    "maximal multiplier to the launching speed when using the power meter"
-
-set g_nexball_delay_goal     3    "delay between a goal and a ball reset"
-set g_nexball_delay_idle     10   "maximal idle time before a reset"
-set g_nexball_delay_start    3    "time the ball stands on its spawn before being released"
-set g_nexball_delay_collect  0.5  "time before the same player can catch the ball he launched"
-
-set g_nexball_sound_bounce   1    "bouncing sound (0: off)"
-
-set g_nexball_basketball_trail  1  "1 to leave a trail"
-set g_nexball_football_trail    0  "1 to leave a trail"
-set g_nexball_trail_color     254  "1-256 for different colors (Quake palette, 254 is white)"
-
-set g_nexball_radar_showallplayers 1  "1: show every player and the ball on the radar  0: only show teammates and the ball on the radar"
-
 set g_bloodloss 0   "amount of health below which blood loss occurs"
 
 set g_footsteps 1      "serverside footstep sounds"
@@ -871,7 +576,6 @@ sv_sound_watersplash ""
 seta cl_announcer default "name of the announcer you wish to use from data/sound/announcer"
 seta cl_announcer_antispam 2 "number of seconds before an announcement of the same sound can be played again"
 seta cl_announcer_maptime 3 "play announcer sound telling you the remaining maptime - 0: do not play at all, 1: play at one minute, 2: play at five minutes, 3: play both"
-seta cl_notify_carried_items "3" "notify you of carried items when you obtain them (e.g. flags in CTF) - 0: disabled, 1: notify of taken items, 2: notify of picking up dropped items, 3: notify of both"
 
 // startmap_dm is used when running with the -listen or -dedicated commandline options
 set serverconfig server.cfg
@@ -1159,53 +863,9 @@ alias "g_waypointsprite_toggle"   "toggle cl_hidewaypoints"
 // key for that?
 seta cl_hidewaypoints 0 "disable static waypoints, only show team waypoints"
 
-seta g_waypointsprites_turrets 1 "disable turret waypoints"
-seta g_waypointsprites_turrets_maxdist 4000 "max distace for turret sprites"
-
-// key hunt
-set g_keyhunt 0 "Key Hunt: collect all keys from the enemies and bring them together to score"
-set g_balance_keyhunt_delay_return 60
-set g_balance_keyhunt_delay_round 5
-set g_balance_keyhunt_delay_tracking 10
-set g_balance_keyhunt_delay_fadeout 2
-set g_balance_keyhunt_delay_collect 1.5
-set g_balance_keyhunt_maxdist 150
-set g_balance_keyhunt_score_collect 3
-set g_balance_keyhunt_score_carrierfrag 2
-set g_balance_keyhunt_score_capture 100
-set g_balance_keyhunt_score_push 60
-set g_balance_keyhunt_score_destroyed 50
-set g_balance_keyhunt_score_destroyed_ownfactor 1
-set g_balance_keyhunt_dropvelocity 300
-set g_balance_keyhunt_throwvelocity 400
-set g_balance_keyhunt_protecttime 0.8
-set g_balance_keyhunt_damageforcescale 1
-seta g_keyhunt_teams_override 0
-set g_keyhunt_teams 0
-
-// keepaway
-set g_keepaway 0 "game mode which focuses around a ball, look at g_keepaway_win_mode for further details"
-set g_keepaway_score_bckill 1 "enable scoring points (y/n) for ball carrier kills (value is how many points to award)"
-set g_keepaway_score_killac 1 "amount of points to give when you kill someone while you have the ball"
-set g_keepaway_score_timeinterval 1 "amount of time it takes between intervals for timepoints to be added to the score"
-set g_keepaway_score_timepoints 0 "points to add to score per timeinterval, 0 for no points"
-set g_keepaway_ballcarrier_effects 8 "Add together the numbers you want: EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)"
-set g_keepaway_ballcarrier_highspeed 1 "speed multiplier done to the person holding the ball (recommended when used with some mutators)"
-set g_keepaway_ballcarrier_damage      1       "damage multiplier while holding the ball"
-set g_keepaway_ballcarrier_force       1       "force multiplier while holding the ball"
-set g_keepaway_ballcarrier_selfdamage  1       "self damage multiplier while holding the ball"
-set g_keepaway_ballcarrier_selfforce   1       "self force multiplier while holding the ball"
-set g_keepaway_noncarrier_warn 1       "warn players when they kill without holding the ball"
-set g_keepaway_noncarrier_damage       1       "damage done to other players if both you and they don't have the ball"
-set g_keepaway_noncarrier_force        1       "force done to other players if both you and they don't have the ball"
-set g_keepaway_noncarrier_selfdamage   1       "self damage if you don't have the ball"
-set g_keepaway_noncarrier_selfforce    1       "self force if you don't have the ball"
-set g_keepawayball_effects 0 "Add together the numbers you want: EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)"
-set g_keepawayball_trail_color 254     "particle trail color from player/ball"
-set g_keepawayball_damageforcescale    3 "Scale of force which is applied to the ball by weapons/explosions/etc"
-set g_keepawayball_respawntime 10      "if no one picks up the ball, how long to wait until the ball respawns"
-seta g_keepaway_teams_override 0
-set g_keepaway_teams 0
+seta g_waypointsprite_turrets 1 "disable turret waypoints"
+seta g_waypointsprite_turrets_maxdist 5000 "max distace for turret sprites"
+seta g_waypointsprite_tactical 1 "tactical overlay on turrets when in a vehicle"
 
 // so it can be stuffcmd-ed still
 set cl_gravity 800     "but ignored anyway"
@@ -1429,9 +1089,6 @@ set sv_maxidle 0
 // when sv_maxidle is not 0, assume spectators are idle too
 set sv_maxidle_spectatorsareidle 0
 
-// CTF capture limit placeholder cvar
-set capturelimit 0
-
 // these entities are not referenced by anything directly, they just represent
 // teams and are found by find() when needed
 prvm_leaktest_ignore_classnames "ctf_team dom_team tdm_team"
@@ -1597,52 +1254,6 @@ seta cl_modeldetailreduction 1   "the higher, the less detailed certain map models
 
 set g_mapinfo_settemp_acl "+*" "ACL for mapinfo setting cvars"
 
-// hooks
-alias _cl_hook_gamestart "set _cl_hook_gametype $1; _cl_hook_gamestart_stage2"
-alias _cl_hook_gamestart_stage2 "cl_hook_gamestart_all; cl_hook_gamestart_${_cl_hook_gametype}"
-alias cl_hook_gamestart_all
-alias cl_hook_gamestart_nop  //is only called when CSQC unloads before knowing the gametype, very unlikely
-alias cl_hook_gamestart_dm
-alias cl_hook_gamestart_tdm
-alias cl_hook_gamestart_dom
-alias cl_hook_gamestart_ctf
-alias cl_hook_gamestart_rune
-alias cl_hook_gamestart_lms
-alias cl_hook_gamestart_arena
-alias cl_hook_gamestart_ca
-alias cl_hook_gamestart_kh
-alias cl_hook_gamestart_ons
-alias cl_hook_gamestart_as
-alias cl_hook_gamestart_rc
-alias cl_hook_gamestart_nb
-alias cl_hook_gamestart_cts
-alias cl_hook_gamestart_ka
-alias cl_hook_gamestart_ft
-alias cl_hook_gameend
-alias cl_hook_activeweapon
-
-alias _sv_hook_gamestart "set _sv_hook_gametype $1; _sv_hook_gamestart_stage2"
-alias _sv_hook_gamestart_stage2 "sv_hook_gamestart_all; sv_hook_gamestart_${_sv_hook_gametype}"
-alias sv_hook_gamestart_all
-alias sv_hook_gamestart_dm
-alias sv_hook_gamestart_tdm
-alias sv_hook_gamestart_dom
-alias sv_hook_gamestart_ctf
-alias sv_hook_gamestart_rune
-alias sv_hook_gamestart_lms
-alias sv_hook_gamestart_arena
-alias sv_hook_gamestart_ca
-alias sv_hook_gamestart_kh
-alias sv_hook_gamestart_ons
-alias sv_hook_gamestart_as
-alias sv_hook_gamestart_rc
-alias sv_hook_gamestart_nb
-alias sv_hook_gamestart_cts
-alias sv_hook_gamestart_ka
-alias sv_hook_gamestart_ft
-alias sv_hook_gamerestart
-alias sv_hook_gameend
-
 seta cl_casings_maxcount 100 "maximum amount of shell casings (must be at least 1)"
 seta cl_gibs_maxcount 100 "maximum amount of gibs (must be at least 1)"
 seta cl_vehicle_spiderbot_cross_alpha 0.6
@@ -1697,14 +1308,14 @@ set loddebug 0 "force this LOD level"
 set spawn_debugview 0 "display spawnpoints and their rating on spawn to debug spawnpoint rating calculation"
 set g_mutatormsg "" "mutator message"
 set speedmeter 0 "print landing speeds"
-set developer_shtest 0 "experimental speedhack detection"
+set developer_csqcentities 0 "csqc entity spam"
 set waypoint_benchmark 0 "quit after waypoint loading to benchmark bot navigation code"
 set g_debug_bot_commands 0 "print scripted bot commands before executing"
 set g_debug_defaultsounds 0 "always use default sounds"
 set sv_use_csqc_players 1 "set to 0 to disable CSQC players for better Xonotic 0.5 compat"
 set cl_precacheplayermodels 0 "TODO please check if this needs to be 1 or if precaching a model the server already requested is fast enough to do it at runtime"
 seta cl_forceplayermodels 0 "make everyone look like your own model (requires server to have sv_use_csqc_players 1 and sv_defaultcharacter 0)"
-seta cl_forceplayercolors 0 "make everyone look like your own color (requires server to have sv_use_csqc_players 1 and sv_defaultcharacter 0, and is ignored in teamplay)"
+seta cl_forceplayercolors 0 "make everyone look like your own color (requires server to have sv_use_csqc_players 1 and sv_defaultcharacter 0, and is ignored in teamplay with more than two teams)"
 seta cl_forcemyplayermodel "" "set to the model file name you want to show yourself as (requires server to have sv_use_csqc_players 1; does not affect how enemies look with cl_forceplayermodels)"
 seta cl_forcemyplayerskin 0 "set to the skin number you want to show yourself as (requires server to have sv_use_csqc_players 1; does not affect how enemies look with cl_forceplayermodels)"
 seta cl_forcemyplayercolors 0 "set to the color value (encoding is same as _cl_color) for your own player model (requires server to have sv_use_csqc_players 1, and is ignored in teamplay; does not affect how enemies look with cl_forceplayermodels)"
@@ -1882,8 +1493,7 @@ set g_weapon_charge_colormod_green_full -0.5
 set g_weapon_charge_colormod_blue_full -1
 
 // player statistics server URI
-set g_playerstats_uri ""
-set g_playerstats_debug 0 "when 1, player stats are dumped to the console too"
+set g_playerstats_uri "" "Output player statistics information to either: URL (with ://), console (with a dash like this: -), or supply a filename to output to data directory."
 
 // autoscreenshots
 set g_max_info_autoscreenshot 3 "how many info_autoscreenshot entities are allowed"
@@ -1929,12 +1539,12 @@ scr_loadingscreen_scale_limit 2
 // other config files
 exec mutator_new_toys.cfg // run BEFORE balance to make sure balance wins
 exec balanceXonotic.cfg
-exec ctfscoring-ai.cfg
 exec effects-normal.cfg
 exec physicsX.cfg
 exec turrets.cfg
 exec vehicles.cfg
 exec crosshairs.cfg
+exec gamemodes.cfg
 
 // load console command aliases and settings
 exec commands.cfg
@@ -1963,6 +1573,9 @@ set cl_ghost_items 0.45 "enable ghosted items (when between 0 and 1, overrides t
 set cl_ghost_items_color "-1 -1 -1" "color of ghosted items, 0 0 0 leaves the color unchanged"
 set sv_simple_items 1 "allow or forbid client use of simple items"
 set cl_simple_items 0 "enable simple items (if server allows)"
+set cl_simpleitems_postfix "_simple" "posfix to add fo model name when simple items are enabled"
 set cl_fullbright_items 0 "enable fullbright items (if server allows, controled by g_fullbrightitems)"
 set cl_weapon_stay_color "2 0.5 0.5" "Color of picked up weapons when g_weapon_stay > 0"
 set cl_weapon_stay_alpha 0.75 "Alpha of picked up weapons when g_weapon_stay > 0"
+
+seta g_superspectate 0 "server side, allows extended spectator functions through the cmd interface. followpowerup, followstrength, followstshield or followfc [red|blue] will transfer spectation to the relevent player, if any"
index 38413d3c622aa32af70926abdb2e1b8649458327..d346a30efbe930804ce535b3fe9b24b31e686fb0 100644 (file)
@@ -775,7 +775,7 @@ effect teleport
 count 500
 type spark
 tex 64 64
-color 0xff8400 0xff2a00
+color 0x807aff 0x4463d5
 size 1 1
 alpha 0 256 100
 stretchfactor 2
@@ -793,7 +793,7 @@ tex 65 65
 size 150 150
 alpha 190 190 180
 sizeincrease -80
-color 0xff8400 0xff2a00
+color 0x807aff 0x4463d5
 
 
 
@@ -7636,4 +7636,145 @@ velocityjitter 10 10 10
 originjitter 80 80 80
 sizeincrease -10
 airfriction 0.04
-gravity -0.2
\ No newline at end of file
+gravity -0.2
+
+// redflag_touch -- effects for touching the red flag
+// used nowhere in code
+effect redflag_touch
+count 35
+type spark
+tex 40 40
+color 0xFF0000 0x970000
+size 1 3
+alpha 0 256 556
+gravity 1
+bounce 1.5
+originjitter 1 1 1
+velocityjitter 300 300 300
+velocitymultiplier 0.5
+airfriction 3
+
+// blueflag_touch -- effects for touching the blue flag
+// used nowhere in code
+effect blueflag_touch
+count 35
+type spark
+tex 40 40
+color 0x0000FF 0x000097
+size 1 3
+alpha 0 256 556
+gravity 1
+bounce 1.5
+originjitter 1 1 1
+velocityjitter 300 300 300
+velocitymultiplier 0.5
+airfriction 3
+
+// red_pass
+// used nowhere in code
+effect red_pass
+trailspacing 64
+color 0xFF0000 0x970000
+size 2 2
+tex 32 32
+alpha 64 128 64
+airfriction 5
+sizeincrease 2
+type static
+effect red_pass
+trailspacing 12
+color 0xFF0000 0x970000
+size 1 1
+tex 0 8
+alpha 32 64 32
+airfriction 9
+sizeincrease 8
+velocityjitter 64 64 64
+type static
+effect red_pass
+trailspacing 12
+color 0xFF0000 0x970000
+size 4 4
+//tex 48 55
+alpha 256 256 1280
+type static
+
+// blue_pass
+// used nowhere in code
+effect blue_pass
+trailspacing 64
+color 0x0000FF 0x000097
+size 2 2
+tex 32 32
+alpha 64 128 64
+airfriction 5
+sizeincrease 2
+type static
+effect blue_pass
+trailspacing 12
+color 0x0000FF 0x000097
+size 1 1
+tex 0 8
+alpha 32 64 32
+airfriction 9
+sizeincrease 8
+velocityjitter 64 64 64
+type static
+effect blue_pass
+trailspacing 12
+color 0x0000FF 0x000097
+size 4 4
+//tex 48 55
+alpha 256 256 1280
+type static
+
+// red_cap -- red team capture effect
+effect red_cap
+count 500
+type spark
+tex 64 64
+color 0xFF0000 0x970000
+size 1 1
+alpha 0 256 100
+stretchfactor 2
+//gravity 1
+bounce 1.5
+originjitter 1 1 1
+velocityjitter 1000 1000 1500
+velocitymultiplier 0.5
+airfriction 2
+stretchfactor 0.6
+effect red_cap
+countabsolute 1
+type smoke
+tex 65 65
+size 150 150
+alpha 190 190 180
+sizeincrease -80
+color 0xFF0000 0x970000
+
+// blue_cap -- blue team capture effect
+effect blue_cap
+count 500
+type spark
+tex 64 64
+color 0x0000FF 0x000097
+size 1 1
+alpha 0 256 100
+stretchfactor 2
+//gravity 1
+bounce 1.5
+originjitter 1 1 1
+velocityjitter 1000 1000 1500
+velocitymultiplier 0.5
+airfriction 2
+stretchfactor 0.6
+effect blue_cap
+countabsolute 1
+type smoke
+tex 65 65
+size 150 150
+alpha 190 190 180
+sizeincrease -80
+color 0x0000FF 0x000097
+
diff --git a/gamemodes.cfg b/gamemodes.cfg
new file mode 100644 (file)
index 0000000..6cff7e5
--- /dev/null
@@ -0,0 +1,474 @@
+// ===================================
+//  Master config for core game modes
+// ===================================
+
+// global gametype setting (1 = deathmatch)
+set gamecfg 1
+
+// say aliases
+alias asay_ctf_flagcarrier "say_team flag carrier at %y"
+alias asay_ctf_haveflag "say_team (%l) have the flag"
+alias asay_willgo "say_team will go to %y"
+alias asay_support "say_team (%l) need help, %h%%"
+alias asay_killed "say_team got killed at %d"
+alias asay_noammo "say_team (%l) need %W for %w"
+alias asay_drop "say_team (%l) dropped %w ; impulse 17"
+
+
+// =================
+//  gamestart hooks
+// =================
+alias _cl_hook_gamestart "set _cl_hook_gametype $1; _cl_hook_gamestart_stage2"
+alias _cl_hook_gamestart_stage2 "cl_hook_gamestart_all; cl_hook_gamestart_${_cl_hook_gametype}"
+alias cl_hook_gamestart_all
+alias cl_hook_gamestart_nop  //is only called when CSQC unloads before knowing the gametype, very unlikely
+alias cl_hook_gamestart_dm
+alias cl_hook_gamestart_tdm
+alias cl_hook_gamestart_dom
+alias cl_hook_gamestart_ctf
+alias cl_hook_gamestart_rune
+alias cl_hook_gamestart_lms
+alias cl_hook_gamestart_arena
+alias cl_hook_gamestart_ca
+alias cl_hook_gamestart_kh
+alias cl_hook_gamestart_ons
+alias cl_hook_gamestart_as
+alias cl_hook_gamestart_rc
+alias cl_hook_gamestart_nb
+alias cl_hook_gamestart_cts
+alias cl_hook_gamestart_ka
+alias cl_hook_gamestart_ft
+alias cl_hook_gameend
+alias cl_hook_activeweapon
+
+alias _sv_hook_gamestart "set _sv_hook_gametype $1; _sv_hook_gamestart_stage2"
+alias _sv_hook_gamestart_stage2 "sv_hook_gamestart_all; sv_hook_gamestart_${_sv_hook_gametype}"
+alias sv_hook_gamestart_all
+alias sv_hook_gamestart_dm
+alias sv_hook_gamestart_tdm
+alias sv_hook_gamestart_dom
+alias sv_hook_gamestart_ctf
+alias sv_hook_gamestart_rune
+alias sv_hook_gamestart_lms
+alias sv_hook_gamestart_arena
+alias sv_hook_gamestart_ca
+alias sv_hook_gamestart_kh
+alias sv_hook_gamestart_ons
+alias sv_hook_gamestart_as
+alias sv_hook_gamestart_rc
+alias sv_hook_gamestart_nb
+alias sv_hook_gamestart_cts
+alias sv_hook_gamestart_ka
+alias sv_hook_gamestart_ft
+alias sv_hook_gamerestart
+alias sv_hook_gameend
+
+
+// ===========
+//  leadlimit
+// ===========
+// this means that timelimit can be overidden globally and fraglimit can be overidden for each game mode: DM/TDM, Domination, CTF, and Runematch.
+set leadlimit 0
+set leadlimit_and_fraglimit 0 "if set, leadlimit is ANDed with fraglimit (otherwise ORed)"
+seta timelimit_override -1     "Time limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta fraglimit_override -1     "Frag limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta leadlimit_override -1     "Lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta capturelimit_override -1  "Capture limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta captureleadlimit_override -1      "Capture llead imit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_arena_point_limit -1    "Arena point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_arena_point_leadlimit -1        "Arena point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_domination_point_limit -1       "Domination point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_domination_point_leadlimit -1   "Domination point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_runematch_point_limit -1        "Runematch point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_runematch_point_leadlimit -1    "Runematch point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_keyhunt_point_limit -1  "Keyhunt point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_keyhunt_point_leadlimit -1      "Keyhunt point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_race_laps_limit -1      "Race laps limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_nexball_goallimit -1 "Nexball goal limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_nexball_goalleadlimit -1 "Nexball goal lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+
+
+// =================================
+//  respawn delay/waves/weapon_stay
+// =================================
+// when variables are set to anything other than 0, they take over the global setting...
+// to force disable delay or waves, set them to 0.125
+set g_ctf_respawn_delay 0
+set g_ctf_respawn_waves 0
+set g_ctf_weapon_stay 0
+set g_dm_respawn_delay 0
+set g_dm_respawn_waves 0
+set g_dm_weapon_stay 0
+set g_dom_respawn_delay 0
+set g_dom_respawn_waves 0
+set g_dom_weapon_stay 0
+set g_lms_respawn_delay 0
+set g_lms_respawn_waves 0
+set g_lms_weapon_stay 0
+set g_rune_respawn_delay 0
+set g_rune_respawn_waves 0
+set g_rune_weapon_stay 0
+set g_tdm_respawn_delay 0
+set g_tdm_respawn_waves 0
+set g_tdm_weapon_stay 0
+set g_ka_respawn_delay 0
+set g_ka_respawn_waves 0
+set g_ka_weapon_stay 0
+set g_kh_respawn_delay 0
+set g_kh_respawn_waves 0
+set g_kh_weapon_stay 0
+set g_arena_respawn_delay 0
+set g_arena_respawn_waves 0
+set g_arena_weapon_stay 0
+set g_ca_respawn_delay 0
+set g_ca_respawn_waves 0
+set g_ca_weapon_stay 0
+set g_nb_respawn_delay 0
+set g_nb_respawn_waves 0
+set g_nb_weapon_stay 0
+set g_as_respawn_delay 0
+set g_as_respawn_waves 0
+set g_as_weapon_stay 0
+set g_ons_respawn_delay 0
+set g_ons_respawn_waves 0
+set g_ons_weapon_stay 0
+set g_rc_respawn_waves 0
+set g_rc_respawn_delay 0
+set g_rc_weapon_stay 0
+set g_cts_respawn_waves 0
+set g_cts_respawn_delay 0
+set g_cts_weapon_stay 2
+set g_ft_respawn_waves 0
+set g_ft_respawn_delay 0
+set g_ft_weapon_stay 0
+
+
+// =======
+//  arena
+// =======
+set g_arena 0 "Arena: many one-on-one rounds are played to find the winner"
+set g_arena_maxspawned 2       "maximum number of players to spawn at once (the rest is spectating, waiting for their turn)"
+set g_arena_roundbased 1       "if disabled, the next player will spawn as soon as someone dies"
+set g_arena_warmup 5   "time, newly spawned players have to prepare themselves in round based matches"
+
+
+// =========
+//  assault
+// =========
+set g_assault 0 "Assault: attack the enemy base as fast as you can, then defend the base against the enemy for that time to win"
+
+
+// ============
+//  clan arena
+// ============
+set g_ca 0 "Clan Arena: Played in rounds, once you're dead you're out! The team with survivors wins the round."
+set g_ca_point_limit 10 "point limit 10 is standard for clan arena"
+set g_ca_point_leadlimit 0
+set g_ca_spectate_enemies 0 "Allow spectating enemy player by dead player during clan arena games."
+set g_ca_warmup 10 "how long the players will have time to run around the map before the round starts"
+set g_ca_damage2score_multiplier 0.01
+set g_ca_round_timelimit 180
+
+
+// ==================
+//  capture the flag
+// ==================
+set g_ctf 0 "Capture The Flag: take the enemy flag and bring it to yours at your base to score"
+set g_ctf_flag_return_time 15
+set g_ctf_flag_return_dropped 100
+set g_ctf_flag_return_damage 0
+set g_ctf_flag_return_when_unreachable 1 "automatically return the flag if it falls into lava/slime/trigger hurt"
+set g_ctf_flagcarrier_auto_helpme_damage 100 "automatically place a helpme notification on flag carrier waypointsprite if they get hit and their health dips below this value"
+set g_ctf_flagcarrier_auto_helpme_time 2 "antispam time for the helpme notification"
+set g_ctf_flagcarrier_selfdamagefactor 1
+set g_ctf_flagcarrier_selfforcefactor 1
+set g_ctf_flagcarrier_damagefactor 1
+set g_ctf_flagcarrier_forcefactor 1
+set g_ctf_stalemate 1 "show the enemy flagcarrier location after both teams have held the flags a certain amount of time"
+set g_ctf_stalemate_endcondition 1 "condition for stalemate mode to be finished: 1 = If ONE flag is no longer stale, 2 = If BOTH flags are no longer stale"
+set g_ctf_stalemate_time 60 "time for each flag until stalemate mode is activated"
+set g_ctf_flagcarrier_waypointforenemy_spotting 1 "show the enemy flagcarrier location if a team mate presses +use to spot them"
+set g_ctf_dropped_capture_delay 1 "dropped capture delay"
+set g_ctf_dropped_capture_radius 100 "allow dropped flags to be automatically captured by base flags if the dropped flag is within this radius of it"
+set g_ctf_flag_damageforcescale 2
+set g_ctf_portalteleport 0 "allow flag carriers to go through portals made in portal gun without dropping the flag"
+set g_ctf_reverse 0 "if enabled, flags positions are switched: you have to capture the enemy's flag from your own base by bringing it to your own flag in the enemy base"
+set g_ctf_flag_collect_delay 1
+set g_ctf_flag_health 0
+set g_ctf_flag_dropped_waypoint 2 "show dropped flag waypointsprite when a flag is lost. 1 = team only, 2 = for all players"
+set g_ctf_flag_dropped_floatinwater 200 "move upwards while in water at this velocity"
+set g_ctf_flag_pickup_verbosename 0 "show the name of the person who picked up the flag too"
+set g_ctf_throw 1 "throwing allows circumventing carrierkill score, so enable this with care!"
+set g_ctf_throw_angle_max 90 "maximum upwards angle you can throw the flag"
+set g_ctf_throw_angle_min -90 "minimum downwards angle you can throw the flag"
+set g_ctf_throw_punish_count 2
+set g_ctf_throw_punish_delay 30
+set g_ctf_throw_punish_time 5
+set g_ctf_throw_strengthmultiplier 2 "multiplier for velocity when you have the strength... essentially, throw the flag REALLY hard when you have the strength :D"
+set g_ctf_throw_velocity_forward 500 "how fast or far a player can throw the flag"
+set g_ctf_throw_velocity_up 200 "upwards velocity added upon initial throw"
+set g_ctf_drop_velocity_up 200 "upwards velocity when a flag is dropped (i.e. when a flag carrier dies)"
+set g_ctf_drop_velocity_side 100 "randomized sideways velocity when a flag is dropped"
+set g_ctf_pass 1 "allow passing of flags to nearby team mates"
+set g_ctf_pass_arc 20 "upwards arcing of the flag path to look more like a throw"
+set g_ctf_pass_arc_max 200 "maximum height for upwards arcing of the flag path to look more like a throw"
+set g_ctf_pass_directional_max 200 "maximum radius from crosshair for line of sight selection when passing"
+set g_ctf_pass_directional_min 50 "minimum radius from crosshair for line of sight selection when passing"
+set g_ctf_pass_radius 500 "maximum radius that you can pass to a team mate in"
+set g_ctf_pass_wait 2 "delay in seconds between how often players can pass the flag (antispam, essentially)"
+set g_ctf_pass_request 1 "allow players to request the flag carrier to pass the flag to them"
+set g_ctf_pass_turnrate 50 "how well the flag follows the best direction to its target while passing"
+set g_ctf_pass_timelimit 2 "how long a flag can stay trying to pass before it gives up and just becomes dropped"
+set g_ctf_pass_velocity 750 "how fast or far a player can pass the flag"
+set g_ctf_allow_vehicle_touch 0 "allow flags to be picked up/captured/returned without even leaving the vehicle"
+set g_ctf_allow_vehicle_carry 1 "allow players to hold flags inside a vehicle"
+
+set g_ctf_shield_max_ratio 0   "shield at most this percentage of a team from the enemy flag (try: 0.4 for 40%)"
+set g_ctf_shield_min_negscore 20       "shield the player from the flag if he's got this negative amount of points or less"
+set g_ctf_shield_force 100     "push force of the shield"
+
+set g_ctf_flag_red_model "models/ctf/flags.md3"
+set g_ctf_flag_red_skin 0
+set g_ctf_flag_blue_model "models/ctf/flags.md3"
+set g_ctf_flag_blue_skin 1
+set g_ctf_flag_glowtrails 1
+set g_ctf_fullbrightflags 0
+set g_ctf_dynamiclights 0
+set g_ctf_captimerecord_always 0 "always show capture time information when someone captures the flag"
+
+seta g_ctf_ignore_frags 0      "1: regular frags give no points"
+exec ctfscoring-samual.cfg
+
+
+// ====================
+//  complete the stage
+// ====================
+set g_cts 0 "CTS: complete the stage"
+set g_cts_selfdamage 1 "0 = disable all selfdamage and falldamage in cts"
+set g_cts_finish_kill_delay 10 "prevent cheating by running back to the start line, and starting out with more speed than otherwise possible"
+
+
+// ==========================
+//  deathmatch (ffa or team)
+// ==========================
+set g_dm 1 "Deathmatch: killing any other player is one frag, player with most frags wins"
+set g_tdm_teams 2 "how many teams are in team deathmatch (set by mapinfo)"
+set g_tdm_team_spawns 0 "when 1, a map can define team spawnpoints for TDM"
+seta g_tdm_teams_override 0    "how many teams are in team deathmatch"
+
+
+// ============
+//  domination
+// ============
+set g_domination                       0 "Domination: capture and hold control points to gain points"
+set g_domination_default_teams         2 "default number of teams for maps that aren't domination-specific"
+seta g_domination_teams_override               0 "use a specific number of teams in domination games (minimum 2), disables dom_team entities"
+set g_domination_disable_frags         0 "players can't get frags normally, only get points from kills"
+set g_domination_point_amt             0 "override: how many points to get per ping"
+set g_domination_point_fullbright      0 "domination point fullbright"
+set g_domination_point_rate            0 "override: how often to give those points"
+set g_domination_point_capturetime     0.1 "how long it takes to capture a point (given no interference)"
+set g_domination_point_glow            0 "domination point glow (warning, slow)"
+//set g_domination_balance_team_points 1 "# of points received is based on team sizes"
+
+
+// ===========
+//  freezetag
+// ===========
+set g_freezetag 0 "Freeze Tag: Freeze the opposing team(s) to win, unfreeze teammates by standing next to them"
+seta g_freezetag_warmup 5 "Time players get to run around before the round starts"
+seta g_freezetag_point_limit -1        "Freeze Tag point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_freezetag_point_leadlimit -1    "Freeze Tag point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_freezetag_revive_speed 0.4 "Speed for reviving a frozen teammate"
+seta g_freezetag_revive_clearspeed 1.6 "Speed at which reviving progress gets lost when out of range"
+seta g_freezetag_revive_extra_size 100 "Distance in qu that you can stand from a frozen teammate to keep reviving him"
+seta g_freezetag_frozen_force 0.6 "How much to multiply the force on a frozen player with"
+
+
+// ==========
+//  keepaway
+// ==========
+set g_keepaway 0 "game mode which focuses around a ball, look at g_keepaway_win_mode for further details"
+set g_keepaway_score_bckill 1 "enable scoring points (y/n) for ball carrier kills (value is how many points to award)"
+set g_keepaway_score_killac 1 "amount of points to give when you kill someone while you have the ball"
+set g_keepaway_score_timeinterval 1 "amount of time it takes between intervals for timepoints to be added to the score"
+set g_keepaway_score_timepoints 0 "points to add to score per timeinterval, 0 for no points"
+set g_keepaway_ballcarrier_effects 8 "Add together the numbers you want: EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)"
+set g_keepaway_ballcarrier_highspeed 1 "speed multiplier done to the person holding the ball (recommended when used with some mutators)"
+set g_keepaway_ballcarrier_damage      1       "damage multiplier while holding the ball"
+set g_keepaway_ballcarrier_force       1       "force multiplier while holding the ball"
+set g_keepaway_ballcarrier_selfdamage  1       "self damage multiplier while holding the ball"
+set g_keepaway_ballcarrier_selfforce   1       "self force multiplier while holding the ball"
+set g_keepaway_noncarrier_warn 1       "warn players when they kill without holding the ball"
+set g_keepaway_noncarrier_damage       1       "damage done to other players if both you and they don't have the ball"
+set g_keepaway_noncarrier_force        1       "force done to other players if both you and they don't have the ball"
+set g_keepaway_noncarrier_selfdamage   1       "self damage if you don't have the ball"
+set g_keepaway_noncarrier_selfforce    1       "self force if you don't have the ball"
+set g_keepawayball_effects 0 "Add together the numbers you want: EF_ADDITIVE (32) / EF_NODEPTHTEST (8192) / EF_DIMLIGHT (8)"
+set g_keepawayball_trail_color 254     "particle trail color from player/ball"
+set g_keepawayball_damageforcescale    3 "Scale of force which is applied to the ball by weapons/explosions/etc"
+set g_keepawayball_respawntime 10      "if no one picks up the ball, how long to wait until the ball respawns"
+seta g_keepaway_teams_override 0
+set g_keepaway_teams 0
+
+
+// ==========
+//  key hunt
+// ==========
+set g_keyhunt 0 "Key Hunt: collect all keys from the enemies and bring them together to score"
+set g_balance_keyhunt_delay_return 60
+set g_balance_keyhunt_delay_round 5
+set g_balance_keyhunt_delay_tracking 10
+set g_balance_keyhunt_delay_fadeout 2
+set g_balance_keyhunt_delay_collect 1.5
+set g_balance_keyhunt_maxdist 150
+set g_balance_keyhunt_score_collect 3
+set g_balance_keyhunt_score_carrierfrag 2
+set g_balance_keyhunt_score_capture 100
+set g_balance_keyhunt_score_push 60
+set g_balance_keyhunt_score_destroyed 50
+set g_balance_keyhunt_score_destroyed_ownfactor 1
+set g_balance_keyhunt_dropvelocity 300
+set g_balance_keyhunt_throwvelocity 400
+set g_balance_keyhunt_protecttime 0.8
+set g_balance_keyhunt_damageforcescale 1
+seta g_keyhunt_teams_override 0
+set g_keyhunt_teams 0
+
+
+// ===================
+//  last man standing
+// ===================
+set g_lms 0 "Last Man Standing: everyone starts with a certain amount of lives, and the survivor wins"
+set g_lms_lives_override -1
+set g_lms_regenerate 0
+set g_lms_campcheck_interval 10
+set g_lms_campcheck_message "^1Don't camp!"
+set g_lms_campcheck_damage 100
+set g_lms_campcheck_distance 1800
+set g_lms_last_join 3  "if g_lms_join_anytime is false, new players can only join if the worst active player has more than (fraglimit - g_lms_last_join) lives"
+set g_lms_join_anytime 1       "if true, new players can join, but get same amount of lives as the worst player"
+
+
+// =========
+//  nexball
+// =========
+set g_nexball 0 "Nexball: Basketball and Soccer go Xonotic"
+set g_nexball_basketball_effects_default     8    "default: dim light. The original version used 1024 (fire) but it gives bad performance"
+set g_balance_nexball_primary_speed       1000    "launching speed"
+set g_balance_nexball_primary_refire         0.7  "launching refire"
+set g_balance_nexball_primary_animtime       0.3  "launching animtime"
+set g_balance_nexball_secondary_animtime     0.3  "launching animtime"
+set g_balance_nexball_secondary_speed     3000    "stealing projectile speed"
+set g_balance_nexball_secondary_lifetime     0.15 "stealing projectile lifetime"
+set g_balance_nexball_secondary_force      500    "stealing projectile force"
+set g_balance_nexball_secondary_refire       0.6  "stealing projectile refire"
+set g_balance_nexball_secondary_animtime     0.3  "stealing projectile animtime"
+set g_nexball_football_physics  2  "0: Revenant's original movement, 1: 0 but half independant of aiming height, 2: 1 fully independant, -1: first recode try"
+set g_nexball_basketball_bouncefactor 0.6    "velocity loss when the ball bounces"
+set g_nexball_basketball_bouncestop   0.075  "speed at which the ball stops when it hits the ground (multiplied by sv_gravity)"
+set g_nexball_football_bouncefactor   0.6    "velocity loss when the ball bounces"
+set g_nexball_football_bouncestop     0.075  "speed at which the ball stops when it hits the ground (multiplied by sv_gravity)"
+set g_nexball_football_boost_forward      100   "forward velocity boost when the ball is touched"
+set g_nexball_football_boost_up           200   "vertical velocity boost when the ball is touched"
+set g_nexball_basketball_delay_hold           20    "time before a player who caught the ball loses it (anti-ballcamp)"
+set g_nexball_basketball_delay_hold_forteam   60    "time before a ball reset when a team holds the ball for too long"
+set g_nexball_basketball_teamsteal             1    "1 to allow players to steal from teammates, 0 to disallow"
+set g_nexball_basketball_carrier_highspeed         0.8  "speed multiplier for the ballcarrier"
+set g_nexball_meter_period                  1    "time to make a full cycle on the power meter"
+set g_nexball_basketball_meter              1    "use the power meter for basketball"
+set g_nexball_basketball_meter_minpower   0.5    "minimal multiplier to the launching speed when using the power meter"
+set g_nexball_basketball_meter_maxpower   1.2    "maximal multiplier to the launching speed when using the power meter"
+set g_nexball_delay_goal     3    "delay between a goal and a ball reset"
+set g_nexball_delay_idle     10   "maximal idle time before a reset"
+set g_nexball_delay_start    3    "time the ball stands on its spawn before being released"
+set g_nexball_delay_collect  0.5  "time before the same player can catch the ball he launched"
+set g_nexball_sound_bounce   1    "bouncing sound (0: off)"
+set g_nexball_basketball_trail  1  "1 to leave a trail"
+set g_nexball_football_trail    0  "1 to leave a trail"
+set g_nexball_trail_color     254  "1-256 for different colors (Quake palette, 254 is white)"
+set g_nexball_radar_showallplayers 1  "1: show every player and the ball on the radar  0: only show teammates and the ball on the radar"
+seta g_nexball_safepass_maxdist 5000 "Max distance to allow save fassping (0 to turn off safe passing)"
+seta g_nexball_safepass_turnrate 0.1 "How fast the safe-pass ball can habge direction"
+seta g_nexball_safepass_holdtime 0.75 "How long to remeber last teammate you pointed at"
+seta g_nexball_viewmodel_scale 0.25 "How large the ball for the carrier"
+seta g_nexball_viewmodel_offset "8 8 0" "Where the ball is located on carrier forward right up"
+seta g_nexball_tackling 1 "Allow ball theft?"
+
+
+// ===========
+//  onslaught
+// ===========
+set g_onslaught 0 "Onslaught: take control points towards the enemy generator and then destroy it"
+set g_onslaught_gen_health 2500
+set g_onslaught_cp_health 1000
+set g_onslaught_cp_buildhealth 100
+set g_onslaught_cp_buildtime 5
+set g_onslaught_cp_regen 20
+
+
+// ======
+//  race
+// ======
+set g_race 0 "Race: be faster than your opponents"
+set g_race_qualifying_timelimit 0
+set g_race_qualifying_timelimit_override -1
+set g_race_teams 0     "when 2, 3, or 4, the race is played as a team game (the team members can add up their laps)"
+
+
+// ===========
+//  runematch
+// ===========
+set g_runematch                                                0 "Runematch: pick up and hold the runes, special items that give you points, a special power (rune) and a disadvantage (curse)"
+set g_runematch_pointrate                              5
+set g_runematch_fixedspawns                            1 "use fixed runematch spawns if available"
+set g_runematch_pointamt                                       1
+set g_runematch_shuffletime                            30 "how often runes change position"
+set g_runematch_respawntime                            15 "how soon after being dropped to respawn"
+set g_runematch_frags_killedby_runeholder              4
+set g_runematch_frags_killed_runeholder                        5
+set g_runematch_frags_norune                           0
+set g_runematch_drop_runes_max                         2 "only drop up to 2 runes, the rest should respawn"
+set g_runematch_allow_same                             0 "allow matching rune-curse pairs"
+set g_runematch_rune_alpha                             0.78
+set g_runematch_rune_effects                           544 "EF_ADDITIVE + EF_FULLBRIGHT = 544"
+set g_runematch_rune_glow_size                         0
+set g_runematch_rune_glow_color                                0
+set g_runematch_rune_color_strength                    1.0
+// strength/weakness
+set g_balance_rune_strength_damage                     2.0
+set g_balance_rune_strength_force                      1.5
+set g_balance_curse_weak_damage                                0.5
+set g_balance_curse_weak_force                         0.6
+set g_balance_rune_strength_combo_damage       0.9
+set g_balance_rune_strength_combo_force                        1.0
+// defense/vulner
+set g_balance_rune_defense_takedamage                  0.5
+set g_balance_curse_vulner_takedamage                  2.0
+set g_balance_rune_defense_combo_takedamage            1.0
+// vampire/empathy
+set g_balance_rune_vampire_absorb                      0.4
+set g_balance_curse_empathy_takedamage                 -0.4
+set g_balance_rune_vampire_combo_absorb                        -0.1
+set g_balance_rune_vampire_maxhealth                   500
+set g_balance_curse_empathy_minhealth                  20
+set g_balance_rune_vampire_combo_minhealth             40
+// regen/venom
+set g_balance_rune_regen_hpmod                         1.75
+set g_balance_curse_venom_hpmod                                0.6
+set g_balance_rune_regen_combo_hpmod                   0.9
+set g_balance_rune_regen_regenrate                     3.0
+set g_balance_curse_venom_rotrate                      3.0
+set g_balance_rune_regen_combo_regenrate       0.5
+set g_balance_rune_regen_combo_rotrate                 1.5
+set g_balance_rune_regen_limitmod                      1
+set g_balance_curse_venom_limitmod                     1
+set g_balance_rune_regen_combo_limitmod                        1
+// speed/slow
+set g_balance_rune_speed_atkrate                               0.66
+set g_balance_curse_slow_atkrate                               1.5
+set g_balance_rune_speed_combo_atkrate                 1.2
+set g_balance_rune_speed_highspeed                     1.5
+set g_balance_curse_slow_highspeed                     0.6
+set g_balance_rune_speed_combo_highspeed                       0.9
+
diff --git a/gfx/vehicles/axh-rings.tga b/gfx/vehicles/axh-rings.tga
new file mode 100644 (file)
index 0000000..da68ff8
Binary files /dev/null and b/gfx/vehicles/axh-rings.tga differ
diff --git a/gfx/vehicles/bumb.tga b/gfx/vehicles/bumb.tga
new file mode 100644 (file)
index 0000000..22883f4
Binary files /dev/null and b/gfx/vehicles/bumb.tga differ
diff --git a/gfx/vehicles/bumb_lgun.tga b/gfx/vehicles/bumb_lgun.tga
new file mode 100644 (file)
index 0000000..23db1da
Binary files /dev/null and b/gfx/vehicles/bumb_lgun.tga differ
diff --git a/gfx/vehicles/bumb_rgun.tga b/gfx/vehicles/bumb_rgun.tga
new file mode 100644 (file)
index 0000000..f755681
Binary files /dev/null and b/gfx/vehicles/bumb_rgun.tga differ
diff --git a/gfx/vehicles/bumb_side.tga b/gfx/vehicles/bumb_side.tga
new file mode 100644 (file)
index 0000000..69fa4d8
Binary files /dev/null and b/gfx/vehicles/bumb_side.tga differ
diff --git a/gfx/vehicles/bumb_side_gun.tga b/gfx/vehicles/bumb_side_gun.tga
new file mode 100644 (file)
index 0000000..c8fc817
Binary files /dev/null and b/gfx/vehicles/bumb_side_gun.tga differ
diff --git a/gfx/vehicles/energy.tga b/gfx/vehicles/energy.tga
new file mode 100644 (file)
index 0000000..de06ecc
Binary files /dev/null and b/gfx/vehicles/energy.tga differ
diff --git a/physicsXDF.cfg b/physicsXDF.cfg
new file mode 100644 (file)
index 0000000..0acb6e9
--- /dev/null
@@ -0,0 +1,42 @@
+g_mod_physics XDF
+
+// - Xonotic DeFrag -
+// Nexrun tweaked to suit CPM
+
+sv_gravity 800
+sv_maxspeed 320
+// CPMA: 320
+sv_maxairspeed 320
+// CPMA: 320
+sv_stopspeed 100
+sv_accelerate 15
+sv_airaccelerate 1
+sv_friction 5.8
+edgefriction 1
+sv_stepheight 26
+// CPMA: 18
+sv_jumpvelocity 270
+sv_wateraccelerate 4
+sv_waterfriction 1
+sv_airaccel_sideways_friction 0
+sv_airaccel_qw 0.95
+sv_airaccel_qw_stretchfactor 0
+// CPMA: 1
+sv_airstopaccelerate 2.5
+sv_airstrafeaccelerate 70
+sv_maxairstrafespeed 30
+sv_airstrafeaccel_qw 1
+sv_aircontrol 150
+sv_aircontrol_penalty 0
+sv_aircontrol_power 2
+sv_airspeedlimit_nonqw 0
+sv_warsowbunny_turnaccel 0
+sv_warsowbunny_accel 0.1593
+sv_warsowbunny_topspeed 925
+sv_warsowbunny_backtosideratio 0.8
+sv_friction_on_land 0
+sv_doublejump 1
+sv_jumpspeedcap_min 0
+sv_jumpspeedcap_max 0.5
+sv_jumpspeedcap_max_disable_on_ramps 1
+g_teleport_maxspeed 600
diff --git a/physicsXDFLight.cfg b/physicsXDFLight.cfg
new file mode 100644 (file)
index 0000000..cac90b4
--- /dev/null
@@ -0,0 +1,42 @@
+g_mod_physics XDFLight
+
+// - Xonotic DeFrag Light -
+// Nexrun tweaked to suit CPM
+
+sv_gravity 800
+sv_maxspeed 320
+// CPMA: 320
+sv_maxairspeed 320
+// CPMA: 320
+sv_stopspeed 100
+sv_accelerate 15
+sv_airaccelerate 1
+sv_friction 8
+edgefriction 1
+sv_stepheight 26
+// CPMA: 18
+sv_jumpvelocity 270
+sv_wateraccelerate 4
+sv_waterfriction 1
+sv_airaccel_sideways_friction 0
+sv_airaccel_qw -0.9146875
+sv_airaccel_qw_stretchfactor 0
+// CPMA: 1
+sv_airstopaccelerate 6.5625 // matches strafe-stopping speed
+sv_airstrafeaccelerate 14
+sv_maxairstrafespeed 150
+sv_airstrafeaccel_qw -0.987
+sv_aircontrol 100
+sv_aircontrol_penalty 100
+sv_aircontrol_power 2.5
+sv_airspeedlimit_nonqw 0
+sv_warsowbunny_turnaccel 0
+sv_warsowbunny_accel 0.1593
+sv_warsowbunny_topspeed 925
+sv_warsowbunny_backtosideratio 0.8
+sv_friction_on_land 0
+sv_doublejump 0
+sv_jumpspeedcap_min ""
+sv_jumpspeedcap_max ""
+sv_jumpspeedcap_max_disable_on_ramps 1
+g_teleport_maxspeed 0
diff --git a/physicsXPM.cfg b/physicsXPM.cfg
deleted file mode 100644 (file)
index 55e2ef0..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-g_mod_physics XPM
-// Nexrun tweaked to suit CPM
-
-sv_gravity 800
-sv_maxspeed 320
-// CPMA: 320
-sv_maxairspeed 320
-// CPMA: 320
-sv_stopspeed 100
-sv_accelerate 15
-sv_airaccelerate 1
-sv_friction 8
-edgefriction 1
-sv_stepheight 26
-// CPMA: 18
-sv_jumpvelocity 270
-sv_wateraccelerate 4
-sv_waterfriction 1
-sv_airaccel_sideways_friction 0
-sv_airaccel_qw 0.95
-sv_airaccel_qw_stretchfactor 0
-// CPMA: 1
-sv_airstopaccelerate 2.5
-sv_airstrafeaccelerate 70
-sv_maxairstrafespeed 30
-sv_airstrafeaccel_qw 1
-sv_aircontrol 150
-sv_aircontrol_penalty 0
-sv_aircontrol_power 2
-sv_airspeedlimit_nonqw 0
-sv_warsowbunny_turnaccel 0
-sv_warsowbunny_accel 0.1593
-sv_warsowbunny_topspeed 925
-sv_warsowbunny_backtosideratio 0.8
-sv_friction_on_land 0
-sv_doublejump 1
-sv_jumpspeedcap_min 0
-sv_jumpspeedcap_max 0.5
-sv_jumpspeedcap_max_disable_on_ramps 1
-g_teleport_maxspeed 400
diff --git a/physicsXPMLight.cfg b/physicsXPMLight.cfg
deleted file mode 100644 (file)
index d0f31c2..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-g_mod_physics XPMLight
-// Nexrun tweaked to suit CPM
-
-sv_gravity 800
-sv_maxspeed 320
-// CPMA: 320
-sv_maxairspeed 320
-// CPMA: 320
-sv_stopspeed 100
-sv_accelerate 15
-sv_airaccelerate 1
-sv_friction 8
-edgefriction 1
-sv_stepheight 26
-// CPMA: 18
-sv_jumpvelocity 270
-sv_wateraccelerate 4
-sv_waterfriction 1
-sv_airaccel_sideways_friction 0
-sv_airaccel_qw -0.9146875
-sv_airaccel_qw_stretchfactor 0
-sv_airaccel_qw_stretchfactor 0
-// CPMA: 1
-sv_airstopaccelerate 6.5625 // matches strafe-stopping speed
-sv_airstrafeaccelerate 14
-sv_maxairstrafespeed 150
-sv_airstrafeaccel_qw -0.987
-sv_aircontrol 100
-sv_aircontrol_penalty 100
-sv_aircontrol_power 2.5
-sv_airspeedlimit_nonqw 0
-sv_warsowbunny_turnaccel 0
-sv_warsowbunny_accel 0.1593
-sv_warsowbunny_topspeed 925
-sv_warsowbunny_backtosideratio 0.8
-sv_friction_on_land 0
-sv_doublejump 0
-sv_jumpspeedcap_min ""
-sv_jumpspeedcap_max ""
-sv_jumpspeedcap_max_disable_on_ramps 1
-g_teleport_maxspeed 0
index 3ac1e9cf7a67fee7eb2f82d9e8ffd184a7583b6e..c7a66a8680271478e17323116cc577a02fec1272 100644 (file)
@@ -87,7 +87,6 @@ void ConsoleCommand_macro_init();
 void CSQC_Init(void)
 {
        prvm_language = cvar_string("prvm_language");
-    cl_simple_items = autocvar_cl_simple_items;
 #ifdef USE_FTE
 #pragma target ID
        __engine_check = checkextension("DP_SV_WRITEPICTURE");
@@ -118,7 +117,7 @@ void CSQC_Init(void)
        ClientProgsDB = db_load("client.db");
        compressShortVector_init();
 
-       drawfont = FONT_USER+1;
+       draw_endBoldFont();
        menu_visible = FALSE;
        menu_show = menu_show_error;
        menu_action = menu_sub_null;
@@ -153,8 +152,8 @@ void CSQC_Init(void)
        GetTeam(COLOR_SPECTATOR, true); // add specs first
 
        // needs to be done so early because of the constants they create
-       RegisterWeapons();
-       RegisterGametypes();
+       CALL_ACCUMULATED_FUNCTION(RegisterWeapons);
+       CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
 
        WaypointSprite_Load();
 
@@ -672,7 +671,7 @@ void Ent_ReadAccuracy(void)
                return;
        }
 
-       for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w, f *= 2)
+       for(w = 0, f = 1; w <= WEP_LAST - WEP_FIRST; ++w)
        {
                if(sf & f)
                {
@@ -684,6 +683,10 @@ void Ent_ReadAccuracy(void)
                        else
                                weapon_accuracy[w] = (b - 1.0) / 100.0;
                }
+               if(f == 0x800000)
+                       f = 1;
+               else
+                       f *= 2;
        }
 }
 
@@ -698,6 +701,9 @@ void CSQC_Ent_Update(float bIsNewEntity)
        float savetime;
        t = ReadByte();
 
+       if(autocvar_developer_csqcentities)
+               print(sprintf("CSQC_Ent_Update(%d) with self=%i self.entnum=%d self.enttype=%d t=%d\n", bIsNewEntity, self, self.entnum, self.enttype, t));
+
        // set up the "time" global for received entities to be correct for interpolation purposes
        savetime = time;
        if(servertime)
@@ -768,6 +774,7 @@ void CSQC_Ent_Update(float bIsNewEntity)
                case ENT_CLIENT_TURRET: ent_turret(); break; 
                case ENT_CLIENT_MODEL: CSQCModel_Read(bIsNewEntity); break;
                case ENT_CLIENT_ITEM: ItemRead(bIsNewEntity); break;  
+               case ENT_CLIENT_BUMBLE_RAYGUN: bumble_raygun_read(bIsNewEntity); break;  
                default:
                        //error(strcat(_("unknown entity type in CSQC_Ent_Update: %d\n"), self.enttype));
                        error(sprintf(_("Unknown entity type in CSQC_Ent_Update (enttype: %d, edict: %d, classname: %s)\n"), self.enttype, num_for_edict(self), self.classname));
@@ -793,6 +800,9 @@ void Ent_Remove()
 // CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed.  Essentially call remove(self) as well.
 void CSQC_Ent_Remove()
 {
+       if(autocvar_developer_csqcentities)
+               print(sprintf("CSQC_Ent_Remove() with self=%i self.entnum=%d self.enttype=%d\n", self, self.entnum, self.enttype));
+
        if(wasfreed(self))
        {
                print("WARNING: CSQC_Ent_Remove called for already removed entity. Packet loss?\n");
@@ -815,17 +825,26 @@ void Gamemode_Init()
 // CSQC_Parse_StuffCmd : Provides the stuffcmd string in the first parameter that the server provided.  To execute standard behavior, simply execute localcmd with the string.
 void CSQC_Parse_StuffCmd(string strMessage)
 {
+       if(autocvar_developer_csqcentities)
+               print(sprintf("CSQC_Parse_StuffCmd(\"%s\")\n", strMessage));
+
        localcmd(strMessage);
 }
 // CSQC_Parse_Print : Provides the print string in the first parameter that the server provided.  To execute standard behavior, simply execute print with the string.
 void CSQC_Parse_Print(string strMessage)
 {
+       if(autocvar_developer_csqcentities)
+               print(sprintf("CSQC_Parse_Print(\"%s\")\n", strMessage));
+
        print(ColorTranslateRGB(strMessage));
 }
 
 // CSQC_Parse_CenterPrint : Provides the centerprint_hud string in the first parameter that the server provided.
 void CSQC_Parse_CenterPrint(string strMessage)
 {
+       if(autocvar_developer_csqcentities)
+               print(sprintf("CSQC_Parse_CenterPrint(\"%s\")\n", strMessage));
+
        centerprint_hud(strMessage);
 }
 
@@ -1112,6 +1131,9 @@ float CSQC_Parse_TempEntity()
        float nTEID;
                nTEID = ReadByte();
 
+       if(autocvar_developer_csqcentities)
+               print(sprintf("CSQC_Parse_TempEntity() with nTEID=%d\n", nTEID));
+
                // NOTE: Could just do return instead of break...
        switch(nTEID)
        {
index 66d7dd31c4b1ae258043002bc381a81109d9ddfc..46d116cbd74dd0d8e572973621a75e35164766e6 100644 (file)
@@ -1183,7 +1183,8 @@ void CSQC_UpdateView(float w, float h)
 
                        if(autocvar_crosshair_hitindication)
                        {
-                               vector hitindication_color = stov(autocvar_crosshair_hitindication_color);
+                               vector hitindication_color = ((autocvar_crosshair_color_per_weapon) ? stov(autocvar_crosshair_hitindication_per_weapon_color) : stov(autocvar_crosshair_hitindication_color));
+                               
                                if(hitindication_crosshair_time < hit_time)
                                {
                                        if(time - hit_time < MAX_TIME_DIFF) // don't trigger the animation if it's too old
@@ -1468,6 +1469,8 @@ void CSQC_UpdateView(float w, float h)
             CSQC_RAPTOR_HUD();
         else if(hud == HUD_BUMBLEBEE)
             CSQC_BUMBLE_HUD();
+        else if(hud == HUD_BUMBLEBEE_GUN)
+            CSQC_BUMBLE_GUN_HUD();
     }
        
        cl_notice_run();
index 3e109e78052036fd77f33f88b5138e12844f4418..db1ae41336d5e1eb54d30c437975b7323b330739 100644 (file)
@@ -134,52 +134,10 @@ void Announcer_Time()
        }
 }
 
-float redflag_prev;
-float blueflag_prev;
-void carrierAnnouncer() {
-       float stat_items, redflag, blueflag;
-       float pickup;
-       string item;
-
-       if not(autocvar_cl_notify_carried_items)
-               return;
-
-       stat_items = getstati(STAT_ITEMS);
-
-       redflag = (stat_items/IT_RED_FLAG_TAKEN) & 3;
-       blueflag = (stat_items/IT_BLUE_FLAG_TAKEN) & 3;
-
-       if (redflag == 3 && redflag != redflag_prev) {
-               item = _("^1RED^7 flag");
-               pickup = (redflag_prev == 2);
-       }
-
-       if (blueflag == 3 && blueflag != blueflag_prev) {
-               item = _("^4BLUE^7 flag");
-               pickup = (blueflag_prev == 2);
-       }
-
-       if (item)
-       {
-               if (pickup) {
-                       if (autocvar_cl_notify_carried_items & 2)
-                               centerprint_hud(sprintf(_("You picked up the %s!"), item));
-               }
-               else {
-                       if (autocvar_cl_notify_carried_items & 1)
-                               centerprint_hud(sprintf(_("You got the %s!"), item));
-               }
-       }
-
-       blueflag_prev = blueflag;
-       redflag_prev = redflag;
-}
-
 void Announcer()
 {
        Announcer_Gamestart();
        Announcer_Time();
-       carrierAnnouncer();
 }
 
 void Announcer_Precache () 
index d7055f25f7ba1978945f8d4943365b8d2f8c8be6..6214e82b4ac43b8f4ac399e8f8dde10272f5d54d 100644 (file)
@@ -54,7 +54,6 @@ float autocvar_cl_gunalign;
 float autocvar_cl_hidewaypoints;
 float autocvar_cl_lockview;
 float autocvar_cl_nogibs;
-float autocvar_cl_notify_carried_items;
 float autocvar_cl_particlegibs;
 float autocvar_cl_particles_oldnexbeam;
 float autocvar_cl_particles_quality;
@@ -97,6 +96,7 @@ float autocvar_crosshair_effect_speed;
 var float autocvar_crosshair_enabled = 1;
 float autocvar_crosshair_hitindication;
 string autocvar_crosshair_hitindication_color;
+string autocvar_crosshair_hitindication_per_weapon_color;
 float autocvar_crosshair_hitindication_speed;
 float autocvar_crosshair_hittest;
 float autocvar_crosshair_hittest_blur;
@@ -155,8 +155,8 @@ float autocvar_g_waypointsprite_normdistance;
 float autocvar_g_waypointsprite_scale;
 float autocvar_g_waypointsprite_spam;
 float autocvar_g_waypointsprite_timealphaexponent;
-//float autocvar_g_waypointsprites_turrets;
-//float autocvar_g_waypointsprites_turrets_maxdist;
+var float autocvar_g_waypointsprite_turrets = TRUE;
+var float autocvar_g_waypointsprite_turrets_maxdist = 5000;
 
 float autocvar_hud_colorflash_alpha;
 float autocvar_hud_configure_checkcollisions;
@@ -407,7 +407,9 @@ float autocvar_cl_forceplayercolors;
 string autocvar_cl_forcemyplayermodel;
 float autocvar_cl_forcemyplayerskin;
 float autocvar_cl_forcemyplayercolors;
+float autocvar__cl_color;
 float autocvar__cl_playerskin;
 string autocvar__cl_playermodel;
 float autocvar_cl_precacheplayermodels;
 float autocvar_cl_deathglow;
+float autocvar_developer_csqcentities;
index 638b7f996ce042c578b8dabaa6ff370a0ae4afeb..23b9b8c70a5b7ee2486a7a7497f13f0751396237 100644 (file)
@@ -57,6 +57,45 @@ void LocalCommand_blurtest(float request)
        #endif
 }
 
+void LocalCommand_create_scrshot_ent(float request)
+{
+       switch(request)
+       {
+               case CMD_REQUEST_COMMAND:
+               {
+                       float fh;
+                       string filename = strcat(MapInfo_Map_bspname, "_scrshot_ent.txt");
+                       fh = fopen(filename, FILE_WRITE);
+                       
+                       if(fh >= 0)
+                       {
+                               fputs(fh, "{\n");
+                               fputs(fh, strcat("\"classname\" \"info_autoscreenshot\"\n"));
+                               fputs(fh, strcat("\"origin\" \"", strcat(ftos(view_origin_x), " ", ftos(view_origin_y), " ", ftos(view_origin_z)), "\"\n"));
+                               fputs(fh, strcat("\"angles\" \"", strcat(ftos(view_angles_x), " ", ftos(view_angles_y), " ", ftos(view_angles_z)), "\"\n"));
+                               fputs(fh, "}\n");
+                               
+                               print("Completed screenshot entity dump in ^2data/data/", MapInfo_Map_bspname, "_scrshot_ent.txt^7.\n");
+                               
+                               fclose(fh);
+                       }
+                       else
+                       {
+                               print("^1Error: ^7Could not dump to file!\n");
+                       }
+                       return;
+               }
+                       
+               default:
+               case CMD_REQUEST_USAGE:
+               {
+                       print("\nUsage:^3 cl_cmd create_scrshot_ent\n");
+                       print("  No arguments required.\n");
+                       return;
+               }
+       }
+}
+
 void LocalCommand_debugmodel(float request, float argc)
 {
        switch(request)
@@ -310,6 +349,7 @@ void LocalCommand_(float request)
 // but for 0.5 compat, we need vyes and vno here as they were replaced... REMOVE THEM AFTER 0.6 RELEASE!!!!
 #define CLIENT_COMMANDS(request,arguments) \
        CLIENT_COMMAND("blurtest", LocalCommand_blurtest(request), "Feature for testing blur postprocessing") \
+       CLIENT_COMMAND("create_scrshot_ent", LocalCommand_create_scrshot_ent(request), "Create an entity at this location for automatic screenshots") \
        CLIENT_COMMAND("debugmodel", LocalCommand_debugmodel(request, arguments), "Spawn a debug model manually") \
        CLIENT_COMMAND("handlevote", LocalCommand_handlevote(request, arguments), "System to handle selecting a vote or option") \
        CLIENT_COMMAND("hud", LocalCommand_hud(request, arguments), "Commands regarding/controlling the HUD system") \
index 009e7e7e4cc46d96de0e5f1c9fd1e176014745c8..9c87d13addf9949c671a4319da1e5c91f2f69b45 100644 (file)
@@ -173,7 +173,17 @@ void CSQCPlayer_ForceModel_Apply(float islocalplayer)
        }
 
        // apply it
-       if(autocvar_cl_forcemyplayermodel != "" && forceplayermodels_myisgoodmodel && islocalplayer)
+       float isfriend;
+       float cm;
+       cm = self.forceplayermodels_savecolormap;
+       cm = (cm >= 1024) ? cm : (stof(getplayerkeyvalue(self.colormap - 1, "colors")) + 1024);
+
+       if(teamplay)
+               isfriend = (cm == 1024 + 17 * myteam);
+       else
+               isfriend = islocalplayer;
+
+       if(autocvar_cl_forcemyplayermodel != "" && forceplayermodels_myisgoodmodel && isfriend)
        {
                self.model = forceplayermodels_mymodel;
                self.modelindex = forceplayermodels_mymodelindex;
@@ -199,7 +209,56 @@ void CSQCPlayer_ForceModel_Apply(float islocalplayer)
        }
 
        // forceplayercolors too
-       if(!teamplay)
+       if(teamplay)
+       {
+               // own team's color is never forced
+               float forcecolor_friend = 0;
+               float forcecolor_enemy = 0;
+               float teams_count = 0;
+               entity tm;
+
+               for(tm = teams.sort_next; tm; tm = tm.sort_next)
+                       if(tm.team != COLOR_SPECTATOR)
+                               ++teams_count;
+
+               if(autocvar_cl_forcemyplayercolors)
+                       forcecolor_friend = 1024 + autocvar_cl_forcemyplayercolors;
+               if(autocvar_cl_forceplayercolors && teams_count == 2)
+                       forcecolor_enemy = 1024 + autocvar__cl_color;
+
+               if(forcecolor_enemy && !forcecolor_friend)
+               {
+                       // only enemy color is forced?
+                       // verify it is not equal to the friend color
+                       if(forcecolor_enemy == 1024 + 17 * myteam)
+                               forcecolor_enemy = 0;
+               }
+
+               if(forcecolor_friend && !forcecolor_enemy)
+               {
+                       // only friend color is forced?
+                       // verify it is not equal to the enemy color
+                       for(tm = teams.sort_next; tm; tm = tm.sort_next)
+                               // note: we even compare against our own team.
+                               // if we rejected because we matched our OWN team color,
+                               // this is not bad; we then simply keep our color as is
+                               // anyway.
+                               if(forcecolor_friend == 1024 + 17 * tm.team)
+                                       forcecolor_friend = 0;
+               }
+
+               if(cm == 1024 + 17 * myteam)
+               {
+                       if(forcecolor_friend)
+                               self.colormap = forcecolor_friend;
+               }
+               else
+               {
+                       if(forcecolor_enemy)
+                               self.colormap = forcecolor_enemy;
+               }
+       }
+       else
        {
                if(autocvar_cl_forcemyplayercolors && islocalplayer)
                        self.colormap = 1024 + autocvar_cl_forcemyplayercolors;
index e610c5b7dbabc0811f8a4bde86ecc281a777b479..77022635cdb245c5bac229de144cc5b300c4cc29 100644 (file)
@@ -476,6 +476,8 @@ void HUD_Weapons(void)
        HUD_Panel_UpdateCvars(weapons);
        HUD_Panel_ApplyFadeAlpha();
 
+       draw_beginBoldFont();
+
        // calculate fading effect to weapon images for when the panel is idle
        if(autocvar_hud_panel_weapons_fade)
        {
@@ -808,6 +810,8 @@ void HUD_Weapons(void)
                        ++column;
                }
        }
+
+       draw_endBoldFont();
 }
 
 // Ammo (#1)
@@ -908,6 +912,9 @@ void HUD_Ammo(void)
 
        HUD_Panel_UpdateCvars(ammo);
        HUD_Panel_ApplyFadeAlpha();
+
+       draw_beginBoldFont();
+
        vector pos, mySize;
        pos = panel_pos;
        mySize = panel_size;
@@ -958,35 +965,40 @@ void HUD_Ammo(void)
                if(autocvar__hud_configure)
                {
                        DrawAmmoItem(pos, ammo_size, 2, true, FALSE); //show rockets
-                       return;
                }
+               else
+               {
+                       stat_items = getstati(STAT_ITEMS, 0, 24);
+                       if (stat_items & IT_UNLIMITED_WEAPON_AMMO)
+                               infinite_ammo = TRUE;
+                       for (i = 0; i < AMMO_COUNT; ++i) {
+                               currently_selected = stat_items & GetAmmoItemCode(i);
+                               if (currently_selected)
+                               {
+                                       DrawAmmoItem(pos, ammo_size, i, true, infinite_ammo);
+                                       break;
+                               }
+                       }
+               }
+       }
+       else
+       {
                stat_items = getstati(STAT_ITEMS, 0, 24);
                if (stat_items & IT_UNLIMITED_WEAPON_AMMO)
                        infinite_ammo = TRUE;
                for (i = 0; i < AMMO_COUNT; ++i) {
                        currently_selected = stat_items & GetAmmoItemCode(i);
-                       if (currently_selected)
+                       DrawAmmoItem(pos + eX * column * (ammo_size_x + offset_x) + eY * row * (ammo_size_y + offset_y), ammo_size, i, currently_selected, infinite_ammo);
+                       ++row;
+                       if(row >= rows)
                        {
-                               DrawAmmoItem(pos, ammo_size, i, true, infinite_ammo);
-                               return;
+                               row = 0;
+                               column = column + 1;
                        }
                }
-               return; // nothing to display
        }
 
-       stat_items = getstati(STAT_ITEMS, 0, 24);
-       if (stat_items & IT_UNLIMITED_WEAPON_AMMO)
-               infinite_ammo = TRUE;
-       for (i = 0; i < AMMO_COUNT; ++i) {
-               currently_selected = stat_items & GetAmmoItemCode(i);
-               DrawAmmoItem(pos + eX * column * (ammo_size_x + offset_x) + eY * row * (ammo_size_y + offset_y), ammo_size, i, currently_selected, infinite_ammo);
-               ++row;
-               if(row >= rows)
-               {
-                       row = 0;
-                       column = column + 1;
-               }
-       }
+       draw_endBoldFont();
 }
 
 void DrawNumIcon_expanding(vector myPos, vector mySize, float x, string icon, float vertical, float icon_right_align, vector color, float theAlpha, float fadelerp)
@@ -1107,6 +1119,9 @@ void HUD_Powerups(void)
 
        HUD_Panel_UpdateCvars(powerups);
        HUD_Panel_ApplyFadeAlpha();
+
+       draw_beginBoldFont();
+
        vector pos, mySize;
        pos = panel_pos;
        mySize = panel_size;
@@ -1273,6 +1288,8 @@ void HUD_Powerups(void)
                                DrawNumIcon_expanding(pos + superweapons_offset, mySize, superweapons, "superweapons", is_vertical, superweapons_iconalign, '1 1 1', 1, bound(0, (superweapons - superweapons_time) / 0.5, 1));
                }
        }
+
+       draw_endBoldFont();
 }
 
 // Health/armor (#3)
@@ -2440,6 +2457,9 @@ void HUD_Timer(void)
 
        HUD_Panel_UpdateCvars(timer);
        HUD_Panel_ApplyFadeAlpha();
+
+       draw_beginBoldFont();
+
        vector pos, mySize;
        pos = panel_pos;
        mySize = panel_size;
@@ -2482,6 +2502,8 @@ void HUD_Timer(void)
        }
 
        drawstring_aspect(pos, timer, mySize, timer_color, panel_fg_alpha, DRAWFLAG_NORMAL);
+
+       draw_endBoldFont();
 }
 
 // Radar (#6)
@@ -2806,6 +2828,7 @@ void HUD_Score(void)
                score = me.(scores[ps_primary]);
                timer = TIME_ENCODED_TOSTRING(score);
 
+               draw_beginBoldFont();
                if (pl && ((!(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)) || score)) {
                        // distribution display
                        distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]);
@@ -2826,6 +2849,7 @@ void HUD_Score(void)
                if (distribution <= 0)
                        HUD_Panel_DrawHighlight(pos, eX * 0.75 * mySize_x + eY * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
                drawstring_aspect(pos, timer, eX * 0.75 * mySize_x + eY * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+               draw_endBoldFont();
        } else if (!teamplay) { // non-teamgames
                if ((spectatee_status == -1 && !autocvar__hud_configure) || autocvar_hud_panel_score_rankings)
                {
@@ -2859,6 +2883,7 @@ void HUD_Score(void)
 
                string distribution_str;
                distribution_str = ftos(distribution);
+               draw_beginBoldFont();
                if (distribution >= 0)
                {
                        if (distribution > 0)
@@ -2867,6 +2892,7 @@ void HUD_Score(void)
                }
                drawstring_aspect(pos, ftos(score), eX * 0.75 * mySize_x + eY * mySize_y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL);
                drawstring_aspect(pos + eX * 0.75 * mySize_x, distribution_str, eX * 0.25 * mySize_x + eY * (1/3) * mySize_y, distribution_color, panel_fg_alpha, DRAWFLAG_NORMAL);
+               draw_endBoldFont();
        } else { // teamgames
                float scores_count, row, column, rows, columns;
                local noref vector offset; // fteqcc sucks
@@ -2909,6 +2935,7 @@ void HUD_Score(void)
 
                float max_fragcount;
                max_fragcount = -99;
+               draw_beginBoldFont();
                for(tm = teams.sort_next; tm; tm = tm.sort_next) {
                        if(tm.team == COLOR_SPECTATOR)
                                continue;
@@ -2943,6 +2970,7 @@ void HUD_Score(void)
                                ++rows;
                        }
                }
+               draw_endBoldFont();
        }
 }
 
@@ -2961,6 +2989,9 @@ void HUD_RaceTimer (void)
 
        HUD_Panel_UpdateCvars(racetimer);
        HUD_Panel_ApplyFadeAlpha();
+
+       draw_beginBoldFont();
+
        vector pos, mySize;
        pos = panel_pos;
        mySize = panel_size;
@@ -3089,6 +3120,8 @@ void HUD_RaceTimer (void)
                        }
                }
        }
+
+       draw_endBoldFont();
 }
 
 // Vote window (#9)
@@ -3911,6 +3944,9 @@ void HUD_ModIcons(void)
 
        HUD_Panel_UpdateCvars(modicons);
        HUD_Panel_ApplyFadeAlpha();
+
+       draw_beginBoldFont();
+
        vector pos, mySize;
        pos = panel_pos;
        mySize = panel_size;
@@ -3949,6 +3985,8 @@ void HUD_ModIcons(void)
                HUD_Mod_Dom(pos, mySize);
        else if(gametype == MAPINFO_TYPE_KEEPAWAY)
                HUD_Mod_Keepaway(pos, mySize);
+
+       draw_endBoldFont();
 }
 
 // Draw pressed keys (#11)
@@ -4371,6 +4409,8 @@ void HUD_Physics(void)
        HUD_Panel_UpdateCvars(physics);
        HUD_Panel_ApplyFadeAlpha();
 
+       draw_beginBoldFont();
+
        HUD_Panel_DrawBg(1);
        if(panel_bg_padding)
        {
@@ -4623,6 +4663,8 @@ void HUD_Physics(void)
        tmp_offset_y = (panel_size_y - tmp_size_y) / 2;
        if (autocvar_hud_panel_physics_text == 1 || autocvar_hud_panel_physics_text == 3)
                drawstring_aspect(panel_pos + acceleration_offset + tmp_offset, strcat(ftos_decimals(acceleration, 2), "g"), tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+
+       draw_endBoldFont();
 }
 
 // CenterPrint (#16)
index 4cb8dad05fff509e7a905ffc6627a850df8459fb..750bd2a75309d8774aea219b774ed1a9875c9f5d 100644 (file)
@@ -194,6 +194,7 @@ void MapVote_Draw()
        pos_y = ymin;
        pos_z = 0;
 
+       draw_beginBoldFont();
        map = _("Vote for a map");
        pos_x = center - stringwidth(map, false, '12 0 0');
        drawstring(pos, map, '24 24 0', '1 1 1', 1, DRAWFLAG_NORMAL);
@@ -205,6 +206,7 @@ void MapVote_Draw()
        drawstring(pos, map, '16 16 0', '0 1 0', 1, DRAWFLAG_NORMAL);
        pos_y += 22;
        pos_x = xmin;
+       draw_endBoldFont();
 
        // base for multi-column stuff...
        ymin = pos_y;
index 070096fbf4d6db912bb50d7f9c88f0eaa4e8de9c..72bb9c5c40a66ec0932bc1dbed0d3b2d00e8a292 100644 (file)
@@ -627,3 +627,13 @@ void URI_Get_Callback(float id, float status, string data)
                print(sprintf(_("Received HTTP request data for an invalid id %d.\n"), id));
        }
 }
+
+void draw_beginBoldFont()
+{
+       drawfont = FONT_USER+2;
+}
+
+void draw_endBoldFont()
+{
+       drawfont = FONT_USER+1;
+}
index a0b4826a6fd425ff71bd5354484e633dc274d811..0922433eebdecd2d6c3c229636162e1475c6e56b 100644 (file)
@@ -80,6 +80,7 @@ tuba.qc
 target_music.qc
 
 vehicles/vehicles.qc
+../server/vehicles/bumblebee.qc
 shownames.qh
 shownames.qc
 
index 935a0277a85abdf98ce2f4d24bbbeb002eedf76e..48a7217fc703a08b3b7f7fdfab3bc58efea36c2e 100644 (file)
@@ -19,6 +19,7 @@ string TranslateScoresLabel(string l)
                case "bckills": return CTX(_("SCO^bckills"));
                case "bctime": return CTX(_("SCO^bctime"));
                case "caps": return CTX(_("SCO^caps"));
+               case "captime": return CTX(_("SCO^captime"));
                case "deaths": return CTX(_("SCO^deaths"));
                case "destroyed": return CTX(_("SCO^destroyed"));
                case "drops": return CTX(_("SCO^drops"));
@@ -254,6 +255,7 @@ void Cmd_HUD_Help()
        print(_("^3kd^7                       The kill-death ratio\n"));
        print(_("^3caps^7                     How often a flag (CTF) or a key (KeyHunt) was captured\n"));
        print(_("^3pickups^7                  How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
+       print(_("^3captime^7                  Time of fastest cap (CTF)\n"));
        print(_("^3fckills^7                  Number of flag carrier kills\n"));
        print(_("^3returns^7                  Number of flag returns\n"));
        print(_("^3drops^7                    Number of flag drops\n"));
@@ -341,6 +343,7 @@ void Cmd_HUD_SetFields(float argc)
 
        hud_fontsize = HUD_GetFontsize("hud_fontsize"); 
 
+       draw_beginBoldFont();
        for(i = 0; i < argc - 1; ++i)
        {
                float nocomplain;
@@ -477,6 +480,7 @@ void Cmd_HUD_SetFields(float argc)
        }
 
        hud_field[hud_num_fields] = SP_END;
+       draw_endBoldFont();
 }
 
 // MOVEUP::
@@ -840,6 +844,7 @@ vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_siz
        }
 
        // print the strings of the columns headers and draw the columns
+       draw_beginBoldFont();
        for(i = 0; i < hud_num_fields; ++i)
        {
                if(hud_field[i] == SP_SEPARATOR)
@@ -882,6 +887,7 @@ vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_siz
                        pos_x -= hud_fontsize_x;
                }
        }
+       draw_endBoldFont();
 
        pos_x = xmin;
        pos_y += 1.25 * hud_fontsize_y; // skip the header
@@ -1220,7 +1226,9 @@ void HUD_DrawScoreboard()
        // Heading
        vector sb_heading_fontsize;
        sb_heading_fontsize = hud_fontsize * 2;
+       draw_beginBoldFont();
        drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
+       draw_endBoldFont();
 
        pos_y += sb_heading_fontsize_y + hud_fontsize_y * 0.25;
 
@@ -1236,6 +1244,7 @@ void HUD_DrawScoreboard()
                        if(tm.team == COLOR_SPECTATOR)
                                continue;
 
+                       draw_beginBoldFont();
                        rgb = GetTeamRGB(tm.team);
                        str = ftos(tm.(teamscores[ts_primary]));
                        drawstring(pos + team_score_baseoffset - eX * stringwidth(str, FALSE, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
@@ -1245,6 +1254,8 @@ void HUD_DrawScoreboard()
                                str = ftos(tm.(teamscores[ts_secondary]));
                                drawstring(pos + team_score_baseoffset - eX * stringwidth(str, FALSE, hud_fontsize) + eY * hud_fontsize_y * 1.5, str, hud_fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
                        }
+                       draw_endBoldFont();
+
                        pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
                }
        }
@@ -1302,7 +1313,9 @@ void HUD_DrawScoreboard()
 
        if(specs)
        {
+               draw_beginBoldFont();
                drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
+               draw_endBoldFont();
                pos_y += 1.25 * hud_fontsize_y;
        }
 
index 3aa3c3b2bbe6074ccfffc0588921abf688e8b56b..272a24610204128247559cf76ecd6fca41bef53b 100644 (file)
@@ -231,7 +231,10 @@ void turret_draw2d()
        if(self.netname == "")
            return;
        
-    if(autocvar_cl_hidewaypoints) // also check g_waypointsprites_turrets after next release (needs changed to clients default/config .cfg)
+       if(!autocvar_g_waypointsprite_turrets)
+               return;
+               
+    if(autocvar_cl_hidewaypoints)
         return; 
 
        float dist = vlen(self.origin - view_origin);
@@ -259,6 +262,7 @@ void turret_draw2d()
                 case HUD_SPIDERBOT:
                 case HUD_WAKIZASHI:
                 case HUD_RAPTOR:
+                case HUD_BUMBLEBEE:
                     if(self.turret_type == TID_EWHEEL || self.turret_type == TID_WALKER)
                         txt = "gfx/vehicles/vth-mover.tga";
                     else
@@ -453,7 +457,7 @@ void turret_construct()
     self.tur_head.drawmask      = MASK_NORMAL;
     self.anim_start_time        = 0;    
     self.draw2d = turret_draw2d;
-    self.maxdistance = 4000; // use g_waypointsprites_turrets_maxdist after next release (needs changed to cleint's default)
+    self.maxdistance = autocvar_g_waypointsprite_turrets_maxdist;
     self.teamradar_color = '1 0 0';
     self.alpha = 1;
     
index 62ba5304d26e59c7d06a27ae81c268d992ccc434..3a4597ab2e4be241d5b16d7dc8ec35a8f788dd38 100644 (file)
@@ -10,6 +10,7 @@
 #define hud_ammo1_ico "gfx/vehicles/bullets.tga"
 #define hud_ammo2_bar "gfx/vehicles/bar_dwn_right.tga"
 #define hud_ammo2_ico "gfx/vehicles/rocket.tga"
+#define hud_energy "gfx/vehicles/energy.tga"
 
 #define SBRM_FIRST 1
 #define SBRM_VOLLY 1
@@ -23,8 +24,8 @@
 #define RSM_LAST 2
 
 entity dropmark;
-float autocvar_cl_vehicles_hudscale;
-float autocvar_cl_vehicles_hudalpha;
+var float autocvar_cl_vehicles_hudscale = 0.5;
+var float autocvar_cl_vehicles_hudalpha = 0.75;
 
 #define raptor_ico  "gfx/vehicles/raptor.tga"
 #define raptor_gun  "gfx/vehicles/raptor_guns.tga"
@@ -36,6 +37,7 @@ void CSQC_WAKIZASHI_HUD();
 void CSQC_SPIDER_HUD();
 void CSQC_RAPTOR_HUD();
 void CSQC_BUMBLE_HUD();
+void CSQC_BUMBLE_GUN_HUD();
 
 #define MAX_AXH 4
 entity AuxiliaryXhair[MAX_AXH];
@@ -46,6 +48,13 @@ const var void Draw_Not();
 .float  axh_drawflag;
 .float  axh_scale;
 
+#define bumb_ico  "gfx/vehicles/bumb.tga"
+#define bumb_lgun  "gfx/vehicles/bumb_lgun.tga"
+#define bumb_rgun  "gfx/vehicles/bumb_rgun.tga"
+
+#define bumb_gun_ico  "gfx/vehicles/bumb_side.tga"
+#define bumb_gun_gun  "gfx/vehicles/bumb_side_gun.tga"
+
 #define spider_ico  "gfx/vehicles/sbot.tga"
 #define spider_rkt  "gfx/vehicles/sbot_rpods.tga"
 #define spider_mgun "gfx/vehicles/sbot_mguns.tga"
@@ -59,13 +68,14 @@ string spider_xhair; // = "gfx/vehicles/axh-special1.tga";
 
 float alarm1time;
 float alarm2time;
+float weapon2mode;
 
 void AuxiliaryXhair_Draw2D()
 {
     vector loc, psize;
 
     psize = self.axh_scale * draw_getimagesize(self.axh_image);
-    loc = project_3d_to_2d(self.origin) - 0.5 * psize;
+    loc = project_3d_to_2d(self.move_origin) - 0.5 * psize;
     if not (loc_z < 0 || loc_x < 0 || loc_y < 0 || loc_x > vid_conwidth || loc_y > vid_conheight)
     {
         loc_z = 0;
@@ -79,44 +89,38 @@ void AuxiliaryXhair_Draw2D()
 
 void Net_AuXair2(float bIsNew)
 {
-    float axh_id;
-    entity axh;
-
-    axh_id = bound(0, ReadByte(), MAX_AXH);
-    axh = AuxiliaryXhair[axh_id];
+    float axh_id       = bound(0, ReadByte(), MAX_AXH);
+    entity axh                 = AuxiliaryXhair[axh_id];
 
     if(axh == world || wasfreed(axh))  // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?)
     {
-        axh               = spawn();
-               axh.draw2d        = Draw_Not;
-               axh.drawmask      = MASK_NORMAL;
-               axh.axh_drawflag  = DRAWFLAG_ADDITIVE;
-               axh.axh_fadetime  = 0.1;
-               axh.axh_image     = "gfx/vehicles/axh-ring.tga";
-               axh.axh_scale     = 1;
-        axh.alpha         = 1;
+        axh                                    = spawn();
+               axh.draw2d                      = Draw_Not;
+               axh.drawmask            = MASK_NORMAL;
+               axh.axh_drawflag        = DRAWFLAG_ADDITIVE;
+               axh.axh_fadetime        = 0.1;
+               axh.axh_image           = "gfx/vehicles/axh-ring.tga";
+               axh.axh_scale           = 1;
+        axh.alpha                      = 1;
                AuxiliaryXhair[axh_id] = axh;
     }
-
-    axh.draw2d   = AuxiliaryXhair_Draw2D;
-
-       axh.origin_x = ReadCoord();
-       axh.origin_y = ReadCoord();
-       axh.origin_z = ReadCoord();
-
-       axh.colormod_x = ReadByte() / 255;
-       axh.colormod_y = ReadByte() / 255;
-       axh.colormod_z = ReadByte() / 255;
-    axh.cnt = time;
+    
+       axh.move_origin_x       = ReadCoord();
+       axh.move_origin_y       = ReadCoord();
+       axh.move_origin_z       = ReadCoord();
+       axh.colormod_x          = ReadByte() / 255;
+       axh.colormod_y          = ReadByte() / 255;
+       axh.colormod_z          = ReadByte() / 255;
+    axh.cnt                    = time;
+    axh.draw2d                 = AuxiliaryXhair_Draw2D;        
 }
 
-float weapon2mode;
 void Net_VehicleSetup()
 {
 
-    float hud_id, i;
+    float i;
     
-    hud_id = ReadByte();
+    float hud_id = ReadByte();
     
     // Weapon update?
     if(hud_id > HUD_VEHICLE_LAST)
@@ -130,11 +134,10 @@ void Net_VehicleSetup()
     {
         sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTN_NONE);
         sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTN_NONE);    
-            
         return;
     }
     
-    hud_id  = bound(HUD_SPIDERBOT, hud_id, HUD_RAPTOR);
+    hud_id  = bound(HUD_VEHICLE_FIRST, hud_id, HUD_VEHICLE_LAST);
 
     // Init auxiliary crosshairs
     entity axh;
@@ -144,15 +147,15 @@ void Net_VehicleSetup()
         if(axh != world && !wasfreed(axh))  // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?)
             remove(axh);
 
-        axh               = spawn();
-               axh.draw2d        = Draw_Not;
-               axh.drawmask      = MASK_NORMAL;
-               axh.axh_drawflag  = DRAWFLAG_NORMAL;
-               axh.axh_fadetime  = 0.1;
-               axh.axh_image     = "gfx/vehicles/axh-ring.tga";
-               axh.axh_scale     = 1;
-        axh.alpha         = 1;
-               AuxiliaryXhair[i] = axh;
+        axh                                    = spawn();
+               axh.draw2d                      = Draw_Not;
+               axh.drawmask            = MASK_NORMAL;
+               axh.axh_drawflag        = DRAWFLAG_NORMAL;
+               axh.axh_fadetime        = 0.1;
+               axh.axh_image           = "gfx/vehicles/axh-ring.tga";
+               axh.axh_scale           = 1;
+        axh.alpha                      = 1;
+               AuxiliaryXhair[i]       = axh;
     }
 
     switch(hud_id)
@@ -186,11 +189,24 @@ void Net_VehicleSetup()
             break;
 
         case HUD_BUMBLEBEE:
+            // Raygun-locked
+            AuxiliaryXhair[0].axh_image   = "gfx/vehicles/axh-bracket.tga";
+            AuxiliaryXhair[0].axh_scale   = 0.5;
+            
+            // Gunner1
+            AuxiliaryXhair[1].axh_image   = "gfx/vehicles/axh-target.tga";
+            AuxiliaryXhair[1].axh_scale   = 0.75;
+            
+            // Gunner2
+            AuxiliaryXhair[2].axh_image   = "gfx/vehicles/axh-target.tga";
+            AuxiliaryXhair[2].axh_scale   = 0.75;
+            break;        
+        case HUD_BUMBLEBEE_GUN:
             // Plasma cannons
-            AuxiliaryXhair[0].axh_image   = "gfx/vehicles/axh-ring.tga";
+            AuxiliaryXhair[0].axh_image   = "gfx/vehicles/axh-bracket.tga";
             AuxiliaryXhair[0].axh_scale   = 0.25;
             // Raygun
-            AuxiliaryXhair[1].axh_image   = "gfx/vehicles/axh-special1.tga";
+            AuxiliaryXhair[1].axh_image   = "gfx/vehicles/axh-bracket.tga";
             AuxiliaryXhair[1].axh_scale   = 0.25;
             break;
     }
@@ -231,15 +247,16 @@ void CSQC_BUMBLE_HUD()
     energy  *= 0.01;
     reload1 *= 0.01;
 
-    pic2size = draw_getimagesize(spider_ico) * (autocvar_cl_vehicles_hudscale * 0.8);
+    pic2size = draw_getimagesize(bumb_ico) * (autocvar_cl_vehicles_hudscale * 0.8);
     picloc = picsize * 0.5 - pic2size * 0.5;
+    
     if(vh_health < 0.25)
-        drawpic(hudloc + picloc, waki_ico, pic2size,  '1 0 0' + '0 1 1' * sin(time * 8),  1, DRAWFLAG_NORMAL);
+        drawpic(hudloc + picloc, bumb_ico, pic2size,  '1 0 0' + '0 1 1' * sin(time * 8),  1, DRAWFLAG_NORMAL);
     else
-        drawpic(hudloc + picloc, waki_ico, pic2size,  '1 1 1' * vh_health  + '1 0 0' * (1 - vh_health),  1, DRAWFLAG_NORMAL);
-    drawpic(hudloc + picloc, waki_eng, pic2size, '1 1 1' * energy   + '1 0 0' * (1 - energy),   1, DRAWFLAG_NORMAL);
-    drawpic(hudloc + picloc, waki_gun, pic2size, '1 1 1' * energy   + '1 0 0' * (1 - energy),   1, DRAWFLAG_NORMAL);
-    drawpic(hudloc + picloc, waki_rkt, pic2size,  '1 1 1' * reload1 + '1 0 0' * (1 - reload1), 1, DRAWFLAG_NORMAL);
+        drawpic(hudloc + picloc, bumb_ico, pic2size,  '1 1 1' * vh_health  + '1 0 0' * (1 - vh_health),  1, DRAWFLAG_NORMAL);
+    
+    drawpic(hudloc + picloc, bumb_lgun, pic2size, '1 1 1' * energy   + '1 0 0' * (1 - energy),   1, DRAWFLAG_NORMAL);
+    drawpic(hudloc + picloc, bumb_lgun, pic2size, '1 1 1' * energy   + '1 0 0' * (1 - energy),   1, DRAWFLAG_NORMAL);
     drawpic(hudloc + picloc, hud_sh, pic2size,  '1 1 1', shield, DRAWFLAG_NORMAL);
 
 // Health bar
@@ -270,7 +287,6 @@ void CSQC_BUMBLE_HUD()
             alarm1time = 0;
         }        
     }
-        
 
 // Shield bar
     picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale;
@@ -300,36 +316,174 @@ void CSQC_BUMBLE_HUD()
         }
     }
     
-// Gun bar
+       ammo1 *= 0.01;
+       ammo2 *= 0.01;
+       
+// Gunner1 bar
     picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale;
     picloc = '450 69 0' * autocvar_cl_vehicles_hudscale;
-    drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * energy, vid_conheight);
+    drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * ammo1, vid_conheight);
     drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
     drawresetcliparea();
+
+// Right gunner slot occupied?
+       if(AuxiliaryXhair[1].draw2d == Draw_Not)
+       {
+               shield = (picsize_x * 0.5) - (0.5 * stringwidth(_("No right gunner!"), FALSE, '1 0 0' * picsize_y + '0 1 0' * picsize_y));                              
+               drawfill(hudloc + picloc - '0.2 0.2 0', picsize + '0.4 0.4 0', '0.25 0.25 0.25', 0.75, DRAWFLAG_NORMAL);
+               drawstring(hudloc + picloc + '1 0 0' * shield, _("No right gunner!"), '1 0 0' * picsize_y + '0 1 0' * picsize_y, '1 0 0' + '0 1 1' * sin(time * 10), 1, DRAWFLAG_NORMAL);
+       }
+       
 // ..  and icon
-    picsize = draw_getimagesize(hud_ammo1_ico) * autocvar_cl_vehicles_hudscale;
+    picsize = 1.5 * draw_getimagesize(hud_energy) * autocvar_cl_vehicles_hudscale;
     picloc = '664 60 0' * autocvar_cl_vehicles_hudscale;
-    if(energy < 0.2)
-        drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL);
+    if(ammo1 < 0.2)
+        drawpic(hudloc + picloc, hud_energy, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL);
     else
-        drawpic(hudloc + picloc, hud_ammo1_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
-
-// Bomb bar
+        drawpic(hudloc + picloc, hud_energy, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
+       
+// Gunner2 bar
     picsize = draw_getimagesize(hud_ammo2_bar) * autocvar_cl_vehicles_hudscale;
     picloc = '450 140 0' * autocvar_cl_vehicles_hudscale;
-    drawsetcliparea(hudloc_x + picloc_x, hudloc_y + picloc_y, picsize_x * reload1, vid_conheight);
+    drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * ammo2, vid_conheight);
     drawpic(hudloc + picloc, hud_ammo2_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
     drawresetcliparea();
+// Left gunner slot occupied?
+       if(AuxiliaryXhair[2].draw2d == Draw_Not)
+       {
+               shield = (picsize_x * 0.5) - (0.5 * stringwidth(_("No left gunner!"), FALSE, '1 0 0' * picsize_y + '0 1 0' * picsize_y));                               
+               drawfill(hudloc + picloc - '0.2 0.2 0', picsize + '0.4 0.4 0', '0.25 0.25 0.25', 0.75, DRAWFLAG_NORMAL);
+               drawstring(hudloc + picloc + '1 0 0' * shield, _("No left gunner!"), '1 0 0' * picsize_y + '0 1 0' * picsize_y, '1 0 0' + '0 1 1' * sin(time * 10), 1, DRAWFLAG_NORMAL);
+       }
+
 // ..  and icon
-    pic2size = draw_getimagesize(hud_ammo2_ico) * autocvar_cl_vehicles_hudscale;
+    picsize = 1.5 * draw_getimagesize(hud_energy) * autocvar_cl_vehicles_hudscale;
     picloc = '664 130 0' * autocvar_cl_vehicles_hudscale;
-    if(reload1 != 1)
-        drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL);
+    if(ammo2 < 0.2)
+        drawpic(hudloc + picloc, hud_energy, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL);
     else
-        drawpic(hudloc + picloc, hud_ammo2_ico, pic2size, '1 1 1', 1, DRAWFLAG_NORMAL);
+        drawpic(hudloc + picloc, hud_energy, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
+
+       if (scoreboard_showscores)
+               HUD_DrawScoreboard();
+    else
+    {
+        picsize = draw_getimagesize(waki_xhair);
+        picsize_x *= 0.5;
+        picsize_y *= 0.5;
+        drawpic('0.5 0 0' * (vid_conwidth - picsize_x) + '0 0.5 0' * (vid_conheight - picsize_y), waki_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
+    }
+    
+}
+
+void CSQC_BUMBLE_GUN_HUD()
+{
+
+       if(autocvar_r_letterbox)
+        return;
+
+    vector picsize, hudloc, pic2size, picloc;
+
+    // Fetch health & ammo stats
+       HUD_GETSTATS
+
+    picsize = draw_getimagesize(hud_bg) * autocvar_cl_vehicles_hudscale;
+    hudloc_y = vid_conheight - picsize_y;
+    hudloc_x = vid_conwidth * 0.5 - picsize_x * 0.5;
+
+    drawpic(hudloc, hud_bg, picsize, '1 1 1', autocvar_cl_vehicles_hudalpha, DRAWFLAG_NORMAL);
+
+    shield  *= 0.01;
+    vh_health  *= 0.01;
+    energy  *= 0.01;
+    reload1 *= 0.01;
+
+    pic2size = draw_getimagesize(bumb_gun_ico) * (autocvar_cl_vehicles_hudscale * 0.8);
+    picloc = picsize * 0.5 - pic2size * 0.5;
+    
+    if(vh_health < 0.25)
+        drawpic(hudloc + picloc, bumb_gun_ico, pic2size,  '1 0 0' + '0 1 1' * sin(time * 8),  1, DRAWFLAG_NORMAL);
+    else
+        drawpic(hudloc + picloc, bumb_gun_ico, pic2size,  '1 1 1' * vh_health  + '1 0 0' * (1 - vh_health),  1, DRAWFLAG_NORMAL);
+    
+    drawpic(hudloc + picloc, bumb_gun_gun, pic2size, '1 1 1' * energy   + '1 0 0' * (1 - energy),   1, DRAWFLAG_NORMAL);
+    drawpic(hudloc + picloc, hud_sh, pic2size,  '1 1 1', shield, DRAWFLAG_NORMAL);
+
+// Health bar
+    picsize = draw_getimagesize(hud_hp_bar) * autocvar_cl_vehicles_hudscale;
+    picloc = '69 69 0' * autocvar_cl_vehicles_hudscale;
+    drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - vh_health)), 0, vid_conwidth, vid_conheight);
+    drawpic(hudloc + picloc, hud_hp_bar, picsize, '1 1 1', 1 , DRAWFLAG_NORMAL);
+    drawresetcliparea();
+// ..  and icon
+    picsize = draw_getimagesize(hud_hp_ico) * autocvar_cl_vehicles_hudscale;
+    picloc = '37 65 0' * autocvar_cl_vehicles_hudscale;
+    if(vh_health < 0.25)
+    {
+        if(alarm1time < time)
+        {
+            alarm1time = time + 2;
+            sound(self, CH_PAIN_SINGLE, "vehicles/alarm.wav", VOL_BASEVOICE, ATTN_NONE);
+        }
+        
+        drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL);
+    }        
+    else
+    {
+        drawpic(hudloc + picloc, hud_hp_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
+        if(alarm1time)
+        {
+            sound(self, CH_PAIN_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTN_NONE);
+            alarm1time = 0;
+        }        
+    }
+
+// Shield bar
+    picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale;
+    picloc = '69 140 0' * autocvar_cl_vehicles_hudscale;
+    drawsetcliparea(hudloc_x + picloc_x + (picsize_x * (1 - shield)), 0, vid_conwidth, vid_conheight);
+    drawpic(hudloc + picloc, hud_sh_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
+    drawresetcliparea();
+// ..  and icon
+    picloc = '40 136 0' * autocvar_cl_vehicles_hudscale;
+    picsize = draw_getimagesize(hud_sh_ico) * autocvar_cl_vehicles_hudscale;
+    if(shield < 0.25)
+    {
+        if(alarm2time < time)
+        {
+            alarm2time = time + 1;
+            sound(self, CH_TRIGGER_SINGLE, "vehicles/alarm_shield.wav", VOL_BASEVOICE, ATTN_NONE);
+        }
+        drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL);
+    }
+    else
+    {
+        drawpic(hudloc + picloc, hud_sh_ico, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
+        if(alarm2time)
+        {            
+            sound(self, CH_TRIGGER_SINGLE, "misc/null.wav", VOL_BASEVOICE, ATTN_NONE);
+            alarm2time = 0;
+        }
+    }
+    
+// Gun bar
+    picsize = draw_getimagesize(hud_ammo1_bar) * autocvar_cl_vehicles_hudscale;
+    picloc = '450 69 0' * autocvar_cl_vehicles_hudscale;
+    drawsetcliparea(hudloc_x + picloc_x, picloc_y, picsize_x * energy, vid_conheight);
+    drawpic(hudloc + picloc, hud_ammo1_bar, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
+    drawresetcliparea();
+    
+// ..  and icon
+    picsize = 1.5 * draw_getimagesize(hud_energy) * autocvar_cl_vehicles_hudscale;
+    picloc = '664 60 0' * autocvar_cl_vehicles_hudscale;
+    if(energy < 0.2)
+        drawpic(hudloc + picloc, hud_energy, picsize, '1 0 0' + '0 1 1' * sin(time * 8), 1, DRAWFLAG_NORMAL);
+    else
+        drawpic(hudloc + picloc, hud_energy, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
 
        if (scoreboard_showscores)
                HUD_DrawScoreboard();
+    /*
     else
     {
         picsize = draw_getimagesize(waki_xhair);
@@ -339,11 +493,11 @@ void CSQC_BUMBLE_HUD()
 
         drawpic('0.5 0 0' * (vid_conwidth - picsize_x) + '0 0.5 0' * (vid_conheight - picsize_y), waki_xhair, picsize, '1 1 1', 1, DRAWFLAG_NORMAL);
     }
+    */
 }
 
 
 
-
 void CSQC_SPIDER_HUD()
 {
        if(autocvar_r_letterbox)
@@ -834,15 +988,6 @@ void CSQC_WAKIZASHI_HUD()
 
 void Vehicles_Precache()
 {
-// fixme: HAAAAKKKZZZ!!!!!!!!!!!! (this belongs as a setting in default.cfg)
-    if(!autocvar_cl_vehicles_hudscale )
-        autocvar_cl_vehicles_hudscale = 0.5;
-    
-    if(!autocvar_cl_vehicles_hudalpha)
-        autocvar_cl_vehicles_hudalpha = 0.75;
-
-       //precache_model("models/vehicles/wakizashi.dpm");
-
        precache_model("models/vehicles/bomblet.md3");
        precache_model("models/vehicles/clusterbomb.md3");
        precache_model("models/vehicles/clusterbomb_fragment.md3");
@@ -855,15 +1000,15 @@ void Vehicles_Precache()
 
 void RaptorCBShellfragDraw()
 {
-       
-       Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy);
        if(wasfreed(self))
-               return;     
-
+               return;   
+               
+       Movetype_Physics_MatchTicrate(autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy);
        self.move_avelocity += randomvec() * 15;
        self.renderflags = 0;
+       
        if(self.cnt < time)
-       self.alpha = bound(0, self.nextthink - time, 1);
+               self.alpha = bound(0, self.nextthink - time, 1);
 
        if(self.alpha < ALPHA_MIN_VISIBLE)
         remove(self);
index c923aafb06cc9e917c6063adcd770b14a3e55864..07fe7c24e07e12589650329c5d7bb8f06941442b 100644 (file)
@@ -261,6 +261,7 @@ string spritelookuptext(string s)
                case "as-defend": return _("Defend");
                case "bluebase": return _("Blue base");
                case "danger": return _("DANGER");
+               case "enemyflagcarrier": return _("Enemy carrier");
                case "flagcarrier": return _("Flag carrier");
                case "flagdropped": return _("Dropped flag");
                case "helpme": return _("Help me!");
@@ -608,6 +609,7 @@ void Draw_WaypointSprite()
        if(autocvar_g_waypointsprite_uppercase)
                txt = strtoupper(txt);
 
+       draw_beginBoldFont();
        if(self.health >= 0)
        {
                o = drawspritetext(o, ang, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
@@ -643,6 +645,7 @@ void Draw_WaypointSprite()
        {
                o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
        }
+       draw_endBoldFont();
 }
 
 void Ent_RemoveWaypointSprite()
index 6d4b7db692c5904ddecbd335f97d2e8e5dfc3235..833216307876f0f25b5f29c71b27fe3d468ed304 100644 (file)
@@ -98,6 +98,7 @@ const float ENT_CLIENT_SHOWNAMES = 31;
 const float ENT_CLIENT_WARPZONE_TELEPORTED = 32;
 const float ENT_CLIENT_MODEL = 33;
 const float ENT_CLIENT_ITEM = 34;
+const float ENT_CLIENT_BUMBLE_RAYGUN = 35;
 
 const float ENT_CLIENT_TURRET = 40;
 const float ENT_CLIENT_AUXILIARYXHAIR = 50;
@@ -168,28 +169,6 @@ const float STAT_HAGAR_LOAD = 57;
 const float STAT_SWITCHINGWEAPON = 58;
 const float STAT_SUPERWEAPONS_FINISHED = 59;
 
-// see DP source, quakedef.h
-const float STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW = 222;
-const float STAT_MOVEVARS_AIRSTRAFEACCEL_QW = 223;
-const float STAT_MOVEVARS_MAXSPEED = 244;
-const float STAT_MOVEVARS_AIRACCEL_QW = 254;
-
-const float CTF_STATE_ATTACK = 1;
-const float CTF_STATE_DEFEND = 2;
-const float CTF_STATE_COMMANDER = 3;
-
-const float HUD_NORMAL = 0;
-const float HUD_VEHICLE_FIRST   = 10;
-const float HUD_SPIDERBOT       = 10;
-const float HUD_WAKIZASHI       = 11;
-const float HUD_RAPTOR          = 12;
-const float HUD_BUMBLEBEE       = 13;
-const float HUD_VEHICLE_LAST    = 13;
-
-const vector eX = '1 0 0';
-const vector eY = '0 1 0';
-const vector eZ = '0 0 1';
-
 const float STAT_VEHICLESTAT_HEALTH  = 60;
 const float STAT_VEHICLESTAT_SHIELD  = 61;
 const float STAT_VEHICLESTAT_ENERGY  = 62;
@@ -222,7 +201,7 @@ const float STAT_PINKALIVE = 103;
 const float STAT_FROZEN = 104;
 const float STAT_REVIVE_PROGRESS = 105;
 
-
+// domination
 const float STAT_DOM_TOTAL_PPS = 100;
 const float STAT_DOM_PPS_RED = 101;
 const float STAT_DOM_PPS_BLUE = 102;
@@ -232,6 +211,29 @@ const float STAT_DOM_PPS_YELLOW = 104;
 //const float STAT_SPIDERBOT_AIM     53 // compressShotOrigin
 //const float STAT_SPIDERBOT_TARGET  54 // compressShotOrigin
 
+// see DP source, quakedef.h
+const float STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW = 222;
+const float STAT_MOVEVARS_AIRSTRAFEACCEL_QW = 223;
+const float STAT_MOVEVARS_MAXSPEED = 244;
+const float STAT_MOVEVARS_AIRACCEL_QW = 254;
+
+const float CTF_STATE_ATTACK = 1;
+const float CTF_STATE_DEFEND = 2;
+const float CTF_STATE_COMMANDER = 3;
+
+const float HUD_NORMAL = 0;
+const float HUD_VEHICLE_FIRST   = 10;
+const float HUD_SPIDERBOT       = 10;
+const float HUD_WAKIZASHI       = 11;
+const float HUD_RAPTOR          = 12;
+const float HUD_BUMBLEBEE       = 13;
+const float HUD_BUMBLEBEE_GUN   = 14;
+const float HUD_VEHICLE_LAST    = 14;
+
+const vector eX = '1 0 0';
+const vector eY = '0 1 0';
+const vector eZ = '0 0 1';
+
 // moved that here so the client knows the max.
 // # of maps, I'll use arrays for them :P
 #define MAPVOTE_COUNT 10
index 33fa21f7eeef5fb96bb0986971ce5223f27d0979..c20715d5852c3cbf09eae967424bf50d2a6754b2 100644 (file)
@@ -31,16 +31,16 @@ float   IT_STRENGTH                  = 8192;
 float   IT_INVINCIBLE                = 16384;
 float   IT_HEALTH                    = 32768;
 // union:
-        // for items:
-        float   IT_KEY1              = 131072;
-        float   IT_KEY2              = 262144;
-        // for players:
-        float   IT_RED_FLAG_TAKEN    = 32768;
-        float   IT_RED_FLAG_LOST     = 65536;
-        float   IT_RED_FLAG_CARRING  = 98304;
-        float   IT_BLUE_FLAG_TAKEN   = 131072;
-        float   IT_BLUE_FLAG_LOST    = 262144;
-        float   IT_BLUE_FLAG_CARRING = 393216;
+       // for items:
+       float   IT_KEY1                                 = 131072;
+       float   IT_KEY2                                 = 262144;
+       // for players:
+       float   IT_RED_FLAG_TAKEN               = 32768;
+       float   IT_RED_FLAG_LOST                = 65536;
+       float   IT_RED_FLAG_CARRYING            = 98304;
+       float   IT_BLUE_FLAG_TAKEN              = 131072;
+       float   IT_BLUE_FLAG_LOST               = 262144;
+       float   IT_BLUE_FLAG_CARRYING   = 393216;
 // end
 float   IT_5HP                       = 524288;
 float   IT_25HP                      = 1048576;
index b65193d44ce26e6341b042b09c24c6283cf1da59..da86e98014a9e26fe625d23e2f5e95e8f65ae999 100644 (file)
@@ -1,6 +1,9 @@
 #pragma flag enable subscope
 #pragma flag enable lo
 
+// FTEQCC can do this
+#define HAVE_YO_DAWG_CPP
+
 #ifndef NOCOMPAT
 //# define WORKAROUND_XON010
 //# define COMPAT_XON010_CHANNELS
index 6e534ffc19464a92777b956353b926f48ddd578e..53c14cfb0cd1798a706bb96cb36bce8afd924ba8 100644 (file)
@@ -463,6 +463,11 @@ string ScoreString(float pFlags, float pValue)
        return valstr;
 }
 
+float dotproduct(vector a, vector b)
+{
+       return a_x * b_x + a_y * b_y + a_z * b_z;
+}
+
 vector cross(vector a, vector b)
 {
        return
@@ -2416,3 +2421,45 @@ float cubic_speedfunc_is_sane(float startspeedfactor, float endspeedfactor)
        // (3.5, [0.2..2.3])
        // (4, 1)
 }
+
+.float FindConnectedComponent_processing;
+void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass)
+{
+       entity queue_start, queue_end;
+
+       // we build a queue of to-be-processed entities.
+       // queue_start is the next entity to be checked for neighbors
+       // queue_end is the last entity added
+
+       if(e.FindConnectedComponent_processing)
+               error("recursion or broken cleanup");
+
+       // start with a 1-element queue
+       queue_start = queue_end = e;
+       queue_end.fld = world;
+       queue_end.FindConnectedComponent_processing = 1;
+
+       // for each queued item:
+       for(; queue_start; queue_start = queue_start.fld)
+       {
+               // find all neighbors of queue_start
+               entity t;
+               for(t = world; (t = nxt(t, queue_start, pass)); )
+               {
+                       if(t.FindConnectedComponent_processing)
+                               continue;
+                       if(iscon(t, queue_start, pass))
+                       {
+                               // it is connected? ADD IT. It will look for neighbors soon too.
+                               queue_end.fld = t;
+                               queue_end = t;
+                               queue_end.fld = world;
+                               queue_end.FindConnectedComponent_processing = 1;
+                       }
+               }
+       }
+
+       // unmark
+       for(queue_start = e; queue_start; queue_start = queue_start.fld)
+               queue_start.FindConnectedComponent_processing = 0;
+}
index 8bc78fcdc7635cfe4703523c0b49f872fe36fcec..81d5dae2148e645a4acf35de8152fb3fa7616c03 100644 (file)
@@ -1,12 +1,13 @@
 // a dummy macro that prevents the "hanging ;" warning
 #define ENDS_WITH_CURLY_BRACE
 
+#ifdef HAVE_YO_DAWG_CPP
 // TODO make ascii art pic of xzibit
 // YO DAWG!
 // I HERD YO LIEK MACROS
 // SO I PUT A MACRO DEFINITION IN YO MACRO DEFINITION
 // SO YO CAN EXPAND MACROS WHILE YO EXPAND MACROS
-#define ACCUMULATE_FUNCTION(func,otherfunc) \
+# define ACCUMULATE_FUNCTION(func,otherfunc) \
        #ifdef func \
        void __merge__##otherfunc() { func(); otherfunc(); } \
        #undef func \
        #else \
        #define func otherfunc \
        #endif
+# define CALL_ACCUMULATED_FUNCTION(func) \
+       func()
+#else
+# define ACCUMULATE_FUNCTION(func,otherfunc) \
+       .void _ACCUMULATE_##func##__##otherfunc;
+void ACCUMULATE_call(string func)
+{
+       float i;
+       float n = numentityfields();
+       string funcprefix = strcat("_ACCUMULATE_", func, "__");
+       float funcprefixlen = strlen(funcprefix);
+       for(i = 0; i < n; ++i)
+       {
+               string name = entityfieldname(i);
+               if(substring(name, 0, funcprefixlen) == funcprefix)
+                       callfunction(substring(name, funcprefixlen, -1));
+       }
+}
+# define CALL_ACCUMULATED_FUNCTION(func) \
+       ACCUMULATE_call(#func)
+#endif
 
 // this returns a tempstring containing a copy of s with additional \n newlines added, it also replaces \n in the text with a real newline
 // NOTE: s IS allowed to be a tempstring
@@ -79,6 +101,7 @@ string mmssss(float t);
 
 string ScoreString(float vflags, float value);
 
+float dotproduct(vector a, vector b);
 vector cross(vector a, vector b);
 
 void compressShortVector_init();
@@ -335,3 +358,7 @@ float cubic_speedfunc(float startspeedfactor, float endspeedfactor, float x);
 // because if this is the case, the function is not usable for platforms
 // as it may exceed 0..1 bounds, or go in reverse
 float cubic_speedfunc_is_sane(float startspeedfactor, float endspeedfactor);
+
+typedef entity(entity cur, entity near, entity pass) findNextEntityNearFunction_t;
+typedef float(entity a, entity b, entity pass) isConnectedFunction_t;
+void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass);
index 7b0644e3e83ebee82c888d3ad6ce4140a90e7c74..c9b9ce9cbc358056fa13b34169ac04c5c015559c 100644 (file)
@@ -1403,6 +1403,39 @@ void() example_skel_player_delete =
 // END OF EXAMPLES FOR FTE_CSQC_SKELETONOBJECTS
 //
 
+//DP_QC_ENTITYDATA
+//idea: KrimZon
+//darkplaces implementation: KrimZon
+//builtin definitions:
+float() numentityfields = #496;
+string(float fieldnum) entityfieldname = #497;
+float(float fieldnum) entityfieldtype = #498;
+string(float fieldnum, entity ent) getentityfieldstring = #499;
+float(float fieldnum, entity ent, string s) putentityfieldstring = #500;
+//constants:
+//Returned by entityfieldtype
+float FIELD_STRING   = 1;
+float FIELD_FLOAT    = 2;
+float FIELD_VECTOR   = 3;
+float FIELD_ENTITY   = 4;
+float FIELD_FUNCTION = 6;
+//description:
+//Versatile functions intended for storing data from specific entities between level changes, but can be customized for some kind of partial savegame.
+//WARNING: .entity fields cannot be saved and restored between map loads as they will leave dangling pointers.
+//numentityfields returns the number of entity fields. NOT offsets. Vectors comprise 4 fields: v, v_x, v_y and v_z.
+//entityfieldname returns the name as a string, eg. "origin" or "classname" or whatever.
+//entityfieldtype returns a value that the constants represent, but the field may be of another type in more exotic progs.dat formats or compilers.
+//getentityfieldstring returns data as would be written to a savegame, eg... "0.05" (float), "0 0 1" (vector), or "Hello World!" (string). Function names can also be returned.
+//putentityfieldstring puts the data returned by getentityfieldstring back into the entity.
+
+//DP_QC_ENTITYSTRING
+void(string s) loadfromdata = #529;
+void(string s) loadfromfile = #530;
+void(string s) callfunction = #605;
+void(float fh, entity e) writetofile = #606;
+float(string s) isfunction = #607;
+void(entity e, string s) parseentitydata = #608;
+
 // assorted builtins
 const float            STAT_MOVEVARS_TICRATE           = 240;
 const float            STAT_MOVEVARS_TIMESCALE         = 241;
index ad8666ed3548786759d2faeb6b9c59dcd5711377..c1198746171f79dd03925f9c2bbac36261717732 100644 (file)
@@ -504,6 +504,31 @@ float(string url, float id) uri_get = #513;
 float(string url, float id, string content_type, string data) uri_post = #513;
 float(string url, float id, string content_type, string delim, float buf) uri_postbuf = #513;
 
+//DP_QC_ENTITYDATA
+//idea: KrimZon
+//darkplaces implementation: KrimZon
+//builtin definitions:
+float() numentityfields = #496;
+string(float fieldnum) entityfieldname = #497;
+float(float fieldnum) entityfieldtype = #498;
+string(float fieldnum, entity ent) getentityfieldstring = #499;
+float(float fieldnum, entity ent, string s) putentityfieldstring = #500;
+//constants:
+//Returned by entityfieldtype
+float FIELD_STRING   = 1;
+float FIELD_FLOAT    = 2;
+float FIELD_VECTOR   = 3;
+float FIELD_ENTITY   = 4;
+float FIELD_FUNCTION = 6;
+//description:
+//Versatile functions intended for storing data from specific entities between level changes, but can be customized for some kind of partial savegame.
+//WARNING: .entity fields cannot be saved and restored between map loads as they will leave dangling pointers.
+//numentityfields returns the number of entity fields. NOT offsets. Vectors comprise 4 fields: v, v_x, v_y and v_z.
+//entityfieldname returns the name as a string, eg. "origin" or "classname" or whatever.
+//entityfieldtype returns a value that the constants represent, but the field may be of another type in more exotic progs.dat formats or compilers.
+//getentityfieldstring returns data as would be written to a savegame, eg... "0.05" (float), "0 0 1" (vector), or "Hello World!" (string). Function names can also be returned.
+//putentityfieldstring puts the data returned by getentityfieldstring back into the entity.
+
 // assorted undocumented extensions
 string(string, float) netaddress_resolve = #625;
 string(string search, string replace, string subject) strreplace = #484;
index 1f223f281affc2bc9ba5a42e90c1b3b3160f8981..9c0d032432f384a60299fddc697c5b2df918ee06 100644 (file)
@@ -16,11 +16,21 @@ void draw_drawMousePointer(vector where)
 
 void draw_reset(float cw, float ch, float ox, float oy)
 {
-       drawfont = FONT_USER+0;
        draw_shift = '1 0 0' * ox + '0 1 0' * oy;
        draw_scale = '1 0 0' * cw + '0 1 0' * ch;
        draw_alpha = 1;
        draw_fontscale = '1 1 0';
+       draw_endBoldFont();
+}
+
+void draw_beginBoldFont()
+{
+       drawfont = FONT_USER+3;
+}
+
+void draw_endBoldFont()
+{
+       drawfont = FONT_USER+0;
 }
 
 vector globalToBox(vector v, vector theOrigin, vector theScale)
index 301a5f440f80fbf768c77182b3f24e2207c64f5a..7178628d8b2f6e093ecc1116e976e6343244266f 100644 (file)
@@ -11,6 +11,8 @@ float draw_alpha;
 vector draw_fontscale;
 
 void draw_reset(float cw, float ch, float ox, float oy);
+void draw_beginBoldFont();
+void draw_endBoldFont();
 void draw_setMousePointer(string pic, vector theSize, vector theOffset);
 void draw_drawMousePointer(vector where);
 
index 4cacfca6b9f42477d52e5af360b6554f7c831c2f..3a345a43dfa1b6f4849cc017d26469f5b0aa5d03 100644 (file)
@@ -3,6 +3,7 @@ CLASS(BorderImage) EXTENDS(Label)
        METHOD(BorderImage, configureBorderImage, void(entity, string, float, vector, string, float))
        METHOD(BorderImage, resizeNotify, void(entity, vector, vector, vector, vector))
        METHOD(BorderImage, recalcPositionWithText, void(entity, string))
+       ATTRIB(BorderImage, isBold, float, 1)
        METHOD(BorderImage, draw, void(entity))
        ATTRIB(BorderImage, src, string, string_null)
        ATTRIB(BorderImage, borderHeight, float, 0)
index e1543ce2756f60579cf1eeb1b5c79be924bc5a7b..378e74adbcb1268ddaa34d7c33af0bb9031bce2e 100644 (file)
@@ -6,6 +6,7 @@ CLASS(Label) EXTENDS(Item)
        METHOD(Label, setText, void(entity, string))
        METHOD(Label, toString, string(entity))
        METHOD(Label, recalcPositionWithText, void(entity, string))
+       ATTRIB(Label, isBold, float, 0)
        ATTRIB(Label, text, string, string_null)
        ATTRIB(Label, fontSize, float, 8)
        ATTRIB(Label, align, float, 0.5)
@@ -45,6 +46,9 @@ void Label_recalcPositionWithText(entity me, string t)
        float spaceAvail;
        spaceAvail = 1 - me.keepspaceLeft - me.keepspaceRight;
 
+       if(me.isBold)
+               draw_beginBoldFont();
+
        float spaceUsed;
        spaceUsed = draw_TextWidth(t, me.allowColors, me.realFontSize);
 
@@ -107,6 +111,9 @@ void Label_recalcPositionWithText(entity me, string t)
                me.realOrigin_y = 0.5 * (1 - lines * me.realFontSize_y);
        }
 
+       if(me.isBold)
+               draw_endBoldFont();
+
        me.recalcPos = 0;
 }
 void Label_resizeNotify(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
@@ -153,6 +160,9 @@ void Label_draw(entity me)
                        vector dfs;
                        vector fs;
 
+                       if(me.isBold)
+                               draw_beginBoldFont();
+
                        // set up variables to draw in condensed size, but use hinting for original size
                        fs = me.realFontSize;
                        fs_x *= me.condenseFactor;
@@ -180,6 +190,9 @@ void Label_draw(entity me)
                                draw_Text(me.realOrigin, t, fs, me.colorL, me.alpha, me.allowColors);
 
                        draw_fontscale = dfs;
+
+                       if(me.isBold)
+                               draw_endBoldFont();
                }
 
        SUPER(Label).draw(me);
index 0c4f349029236d78c365cc6afdc99df033809ac4..60da88a09ca957307f3616e57526f2e3b19a0e19 100644 (file)
@@ -167,7 +167,6 @@ float ListBox_mousePress(entity me, vector pos)
 }
 float ListBox_mouseRelease(entity me, vector pos)
 {
-       vector absSize;
        if(me.pressed == 1)
        {
                // slider dragging mode
index b4c7ce746a8cd749a1eeed4cfd8d2d9b08b23da3..d3bf2ec6298996246eeabdf0c3424f51ca790187 100644 (file)
@@ -73,8 +73,8 @@ void m_init()
        }
 
        // needs to be done so early because of the constants they create
-       RegisterWeapons();
-       RegisterGametypes();
+       CALL_ACCUMULATED_FUNCTION(RegisterWeapons);
+       CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
 
        float ddsload = cvar("r_texture_dds_load");
        float texcomp = cvar("gl_texturecompression");
index 4e81a4acc24d2aa7d5ecce525e54d15090e307ce..e0f4c5b2e4c616e02ea7eca35595969d5953618a 100644 (file)
@@ -70,6 +70,9 @@ void XonoticColorButton_loadCvars(entity me)
        if not(me.cvarName)
                return;
 
+       if(cvar_string(me.cvarName) == cvar_defstring(me.cvarName))
+               cvar_set(me.cvarName, ftos(16 * floor(random() * 15) + floor(random() * 15)));
+
        if(me.cvarPart == 1)
                me.checked = (cvar(me.cvarName) & 240) == me.cvarValueFloat * 16;
        else
index 2d642728e6be854dcdce36c9aa29ec83a077b1a3..e4128235f2aece53fd9eb666c84db928668381c1 100644 (file)
@@ -7,7 +7,7 @@ CLASS(XonoticMutatorsDialog) EXTENDS(XonoticDialog)
        ATTRIB(XonoticMutatorsDialog, title, string, _("Mutators"))
        ATTRIB(XonoticMutatorsDialog, color, vector, SKINCOLOR_DIALOG_MUTATORS)
        ATTRIB(XonoticMutatorsDialog, intendedWidth, float, 0.9)
-       ATTRIB(XonoticMutatorsDialog, rows, float, 17)
+       ATTRIB(XonoticMutatorsDialog, rows, float, 19)
        ATTRIB(XonoticMutatorsDialog, columns, float, 6)
        ATTRIB(XonoticMutatorsDialog, refilterEntity, entity, NULL)
 ENDCLASS(XonoticMutatorsDialog)
@@ -54,7 +54,7 @@ string WeaponArenaString()
                }
        }
        s = sprintf(_("%s Arena"), substring(s, 3, strlen(s) - 3));
-       
+
        weaponarenastring = strzone(s);
 
        return weaponarenastring;
@@ -197,59 +197,59 @@ void XonoticMutatorsDialog_fill(entity me)
                me.TD(me, 1, 2, makeXonoticTextLabel(0, _("Gameplay mutators:")));
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "g_dodging", _("Dodging")));
+               me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_dodging", _("Dodging")));
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "g_cloaked", _("Cloaked")));
+               me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_cloaked", _("Cloaked")));
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "g_midair", _("Midair")));
+               me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_midair", _("Midair")));
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "g_vampire", _("Vampire")));
+               me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_vampire", _("Vampire")));
        me.TR(me);
                me.TDempty(me, 0.2);
                s = makeXonoticSlider(10, 50, 1, "g_bloodloss");
-               me.TD(me, 1, 2, e = makeXonoticSliderCheckBox(0, 1, s, _("Blood loss")));
+               me.TD(me, 1, 1.8, e = makeXonoticSliderCheckBox(0, 1, s, _("Blood loss")));
                        setDependent(e, "g_minstagib", 0, 0);
        me.TR(me);
                me.TDempty(me, 0.4);
-               me.TD(me, 1, 1.8, s);
+               me.TD(me, 1, 1.6, s);
        me.TR(me);
                me.TDempty(me, 0.2);
                s = makeXonoticSlider(80, 400, 8, "sv_gravity");
                        s.valueDigits = 0;
                        s.valueDisplayMultiplier = 0.125; // show gravity in percent
-               me.TD(me, 1, 2, e = makeXonoticSliderCheckBox(800, 1, s, _("Low gravity")));
+               me.TD(me, 1, 1.8, e = makeXonoticSliderCheckBox(800, 1, s, _("Low gravity")));
                        e.savedValue = 200; // good on silvercity
        me.TR(me);
                me.TDempty(me, 0.4);
-               me.TD(me, 1, 1.8, s);
+               me.TD(me, 1, 1.6, s);
        me.TR(me);
                me.TD(me, 1, 2, makeXonoticTextLabel(0, _("Weapon & item mutators:")));
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "g_grappling_hook", _("Grappling hook")));
+               me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_grappling_hook", _("Grappling hook")));
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "g_jetpack", _("Jet pack")));
+               me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_jetpack", _("Jet pack")));
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "g_invincible_projectiles", _("Invincible Projectiles")));
+               me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_invincible_projectiles", _("Invincible Projectiles")));
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "g_new_toys", _("New Toys")));
+               me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_new_toys", _("New Toys")));
                        setDependentWeird(e, checkCompatibility_newtoys);
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "g_rocket_flying", _("Rocket Flying")));
+               me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_rocket_flying", _("Rocket Flying")));
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "g_pinata", _("Piñata")));
+               me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_pinata", _("Piñata")));
                        setDependentWeird(e, checkCompatibility_pinata);
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticCheckBox(0, "g_weapon_stay", _("Weapons stay")));
+               me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_weapon_stay", _("Weapons stay")));
                        setDependentWeird(e, checkCompatibility_weaponstay);
        me.TR(me);
 
@@ -257,7 +257,7 @@ void XonoticMutatorsDialog_fill(entity me)
                me.TD(me, 1, 4, makeXonoticTextLabel(0, _("Weapon arenas:")));
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticRadioButton(1, string_null, string_null, _("Regular (no arena)")));
+               me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, string_null, string_null, _("Regular (no arena)")));
        for(i = WEP_FIRST, j = 0; i <= WEP_LAST; ++i)
        {
                w = get_weaponinfo(i);
@@ -268,7 +268,7 @@ void XonoticMutatorsDialog_fill(entity me)
                str = w.netname;
                hstr = w.message;
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticRadioButton(1, "g_weaponarena", strzone(str), strzone(hstr)));
+               me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_weaponarena", strzone(str), strzone(hstr)));
                        e.cvarOffValue = "0";
                        // custom load/save logic that ignores a " laser" suffix, or adds it 
                        e.loadCvars = loadCvarsLaserWeaponArenaWeaponButton;
@@ -278,7 +278,7 @@ void XonoticMutatorsDialog_fill(entity me)
        }
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 1, e = makeXonoticCheckBox(0, "menu_weaponarena_with_laser", _("with laser")));
+               me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "menu_weaponarena_with_laser", _("with laser")));
                        // hook the draw function to gray it out
                        e.draw_weaponarena = e.draw;
                        e.draw = preDrawLaserWeaponArenaLaserButton;
@@ -289,21 +289,21 @@ void XonoticMutatorsDialog_fill(entity me)
                me.TD(me, 1, 4, makeXonoticTextLabel(0, _("Special arenas:")));
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticRadioButton(1, "g_minstagib", string_null, _("MinstaGib")));
+               me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_minstagib", string_null, _("MinstaGib")));
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticRadioButton(1, "g_nix", string_null, _("NIX")));
+               me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_nix", string_null, _("NIX")));
        me.TR(me);
                me.TDempty(me, 0.4);
-               me.TD(me, 1, 1, e = makeXonoticCheckBox(0, "g_nix_with_laser", _("with laser")));
+               me.TD(me, 1, 1.6, e = makeXonoticCheckBox(0, "g_nix_with_laser", _("with laser")));
                        setDependent(e, "g_nix", 1, 1);
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticRadioButton(1, "g_weaponarena", "most", _("Most weapons")));
+               me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_weaponarena", "most", _("Most weapons")));
                        e.cvarOffValue = "0";
        me.TR(me);
                me.TDempty(me, 0.2);
-               me.TD(me, 1, 2, e = makeXonoticRadioButton(1, "g_start_weapon_laser", "0", _("No start weapons")));
+               me.TD(me, 1, 1.8, e = makeXonoticRadioButton(1, "g_start_weapon_laser", "0", _("No start weapons")));
                        e.cvarOffValue = "-1";
                        makeMulti(e, "g_start_weapon_shotgun g_start_weapon_uzi g_start_weapon_grenadelauncher g_start_weapon_minelayer g_start_weapon_electro g_start_weapon_crylink g_start_weapon_nex g_start_weapon_hagar g_start_weapon_rocketlauncher g_start_weapon_rifle g_start_weapon_hlac g_start_weapon_seeker g_start_weapon_minstanex g_start_weapon_hook g_start_weapon_porto g_start_weapon_tuba g_start_weapon_minelayer");
 
index d33a1eab12d5439e5435280fd40cd3439f3503f9..ef930b4e1490fe879c28ca7a83a191f191610777 100644 (file)
@@ -31,7 +31,7 @@ void XonoticPlayerSettingsTab_draw(entity me)
 void XonoticPlayerSettingsTab_fill(entity me)
 {
        entity e, pms, label, box;
-       float i, r, m, n;
+       float i;
 
        me.TR(me);
                me.TD(me, 1, 0.5, me.playerNameLabel = makeXonoticTextLabel(0, _("Name:")));
@@ -54,34 +54,38 @@ void XonoticPlayerSettingsTab_fill(entity me)
        me.TR(me);
        me.TR(me);
        me.TR(me);
+
+       me.TR(me);
+               me.TDempty(me, 1);
+               me.TD(me, 1, 2, e = makeXonoticTextLabel(0.5, _("Model:")));
        me.TR(me);
-       me.gotoRC(me, 8, 0.0);
+               me.TDempty(me, 1);
                pms = makeXonoticPlayerModelSelector();
-               me.TD(me, 1, 0.6, e = makeXonoticTextLabel(1, _("Model:")));
                me.TD(me, 1, 0.3, e = makeXonoticButton("<<", '0 0 0'));
                        e.onClick = PlayerModelSelector_Prev_Click;
                        e.onClickEntity = pms;
-               me.TD(me, me.rows - (me.currentRow + 2), 1.8, pms);
+               me.TD(me, me.rows - (me.currentRow + 2), 1.4, pms);
                me.TD(me, 1, 0.3, e = makeXonoticButton(">>", '0 0 0'));
                        e.onClick = PlayerModelSelector_Next_Click;
                        e.onClickEntity = pms;
        me.TR(me);
-               r = me.currentRow;
-               m = me.rows - (r + 3);
-               n = 16 - !cvar("developer");
-               m = m / (n - 1);
-               for(i = 0; i < n; ++i)
+               me.TD(me, 1, 1, e = makeXonoticTextLabel(0.5, _("Glowing color:")));
+               for(i = 0; i < 15; ++i)
                {
-                       me.gotoRC(me, r + i * m, 0.1);
-                       me.TDNoMargin(me, m, 0.2, e = makeXonoticColorButton(1, 0, i), '0 1 0');
+                       if(mod(i, 5) == 0)
+                               me.TR(me);
+                       me.TDNoMargin(me, 1, 0.2, e = makeXonoticColorButton(1, 0, i), '0 1 0');
                }
-               for(i = 0; i < n; ++i)
+       me.TR(me);
+       me.TR(me);
+               me.TD(me, 1, 1, e = makeXonoticTextLabel(0.5, _("Detail color:")));
+               for(i = 0; i < 15; ++i)
                {
-                       me.gotoRC(me, r + i * m, 0.4);
-                       me.TDNoMargin(me, m, 0.2, e = makeXonoticColorButton(2, 1, i), '0 1 0');
+                       if(mod(i, 5) == 0)
+                               me.TR(me);
+                       me.TDNoMargin(me, 1, 0.2, e = makeXonoticColorButton(2, 1, i), '0 1 0');
                }
 
-
        // crosshair_enabled: 0 = no crosshair options, 1 = no crosshair selection, but everything else enabled, 2 = all crosshair options enabled
        // FIXME: In the future, perhaps make one global crosshair_type cvar which has 0 for disabled, 1 for custom, 2 for per weapon, etc?
        me.gotoRC(me, 0, 3.2); me.setFirstColumn(me, me.currentColumn);
index 61eb4a681651f6cd0d82ec7cac6274c26df3e499..f37aaeb0750d8568dea1d8cf79b9a6f738bb71ff 100644 (file)
@@ -180,8 +180,13 @@ void XonoticPlayerModelSelector_draw(entity me)
 
        SUPER(XonoticPlayerModelSelector).draw(me);
        // draw text on the image, handle \n in the description
+
+       draw_beginBoldFont();
+
        draw_CenterText('0.5 0 0', me.currentModelTitle, me.realFontSize * (me.titleFontSize / me.fontSize), SKINCOLOR_MODELTITLE, SKINALPHA_MODELTITLE, FALSE);
 
+       draw_endBoldFont();
+
        o = '0.5 1 0' - eY * me.realFontSize_y * ((n = tokenizebyseparator(me.currentModelDescription, "\n")) + 0.5);
        for(i = 0; i < n; ++i)
        {
index f17593af95c8cb54fc85ccb33073ddef8ce193d2..9271e03d0ad20ef4b7e8e9a6536d68b723b40348 100644 (file)
@@ -32,10 +32,14 @@ float accuracy_send(entity to, float 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, f *= 2)
+       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;
 }
@@ -95,7 +99,7 @@ void accuracy_add(entity e, float w, float fired, float hit)
 
        if(b == accuracy_byte(a.(accuracy_hit[w]), a.(accuracy_fired[w])))
                return;
-       w = pow(2, w);
+       w = pow(2, mod(w, 24));
        a.SendFlags |= w;
        FOR_EACH_CLIENT(a)
                if(a.classname == "spectator")
index 4fd165b308c871617a2ccc42551ea7e434f6a125..73025f1f61004ed50996fc1dfa692aafac943895 100644 (file)
@@ -15,6 +15,9 @@ void antilag_dummy()
 
 void antilag_record(entity e, float t)
 {
+    if (e.vehicle && e.vehicle.vehicle_flags == VHF_PLAYERSLOT)
+        return;
+        
     if(e.vehicle)
         antilag_record(e.vehicle, t);
 
@@ -92,6 +95,10 @@ vector antilag_takebackavgvelocity(entity e, float t0, float t1)
 
 void antilag_takeback(entity e, float t)
 {
+
+    if (e.vehicle && e.vehicle.vehicle_flags == VHF_PLAYERSLOT)
+        return;
+
        if(e.vehicle)
                antilag_takeback(e.vehicle, t);
 
@@ -104,6 +111,9 @@ void antilag_takeback(entity e, float t)
 
 void antilag_restore(entity e)
 {
+    if (e.vehicle && e.vehicle.vehicle_flags == VHF_PLAYERSLOT)
+        return;
+
        if(e.vehicle)
                antilag_restore(e.vehicle);
 
diff --git a/qcsrc/server/attic/bot/havocbot/role_ctf.qc b/qcsrc/server/attic/bot/havocbot/role_ctf.qc
new file mode 100644 (file)
index 0000000..0946f43
--- /dev/null
@@ -0,0 +1,684 @@
+#define HAVOCBOT_CTF_ROLE_NONE                 0
+#define HAVOCBOT_CTF_ROLE_DEFENSE      2
+#define HAVOCBOT_CTF_ROLE_MIDDLE       4
+#define HAVOCBOT_CTF_ROLE_OFFENSE      8
+#define HAVOCBOT_CTF_ROLE_CARRIER      16
+#define HAVOCBOT_CTF_ROLE_RETRIEVER    32
+#define HAVOCBOT_CTF_ROLE_ESCORT       64
+
+.void() havocbot_role;
+.void() havocbot_previous_role;
+
+void() havocbot_role_ctf_middle;
+void() havocbot_role_ctf_defense;
+void() havocbot_role_ctf_offense;
+void() havocbot_role_ctf_carrier;
+void() havocbot_role_ctf_retriever;
+void() havocbot_role_ctf_escort;
+
+void(entity bot) havocbot_ctf_reset_role;
+void(float ratingscale, vector org, float sradius) havocbot_goalrating_items;
+void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
+
+.float havocbot_cantfindflag;
+.float havocbot_role_timeout;
+.entity ctf_worldflagnext;
+.entity bot_basewaypoint;
+
+entity ctf_worldflaglist;
+vector havocbot_ctf_middlepoint;
+float havocbot_ctf_middlepoint_radius;
+
+entity havocbot_ctf_find_flag(entity bot)
+{
+       entity f;
+       f = ctf_worldflaglist;
+       while (f)
+       {
+               if (bot.team == f.team)
+                       return f;
+               f = f.ctf_worldflagnext;
+       }
+       return world;
+}
+
+entity havocbot_ctf_find_enemy_flag(entity bot)
+{
+       entity f;
+       f = ctf_worldflaglist;
+       while (f)
+       {
+               if (bot.team != f.team)
+                       return f;
+               f = f.ctf_worldflagnext;
+       }
+       return world;
+}
+
+float havocbot_ctf_teamcount(entity bot, vector org, float radius)
+{
+       if not(teamplay)
+               return 0;
+
+       float c = 0;
+       entity head;
+
+       FOR_EACH_PLAYER(head)
+       {
+               if(head.team!=bot.team || head.deadflag != DEAD_NO || head == bot)
+                       continue;
+
+               if(vlen(head.origin - org) < radius)
+                       ++c;
+       }
+
+       return c;
+}
+
+void havocbot_goalrating_ctf_ourflag(float ratingscale)
+{
+       entity head;
+       head = ctf_worldflaglist;
+       while (head)
+       {
+               if (self.team == head.team)
+                       break;
+               head = head.ctf_worldflagnext;
+       }
+       if (head)
+               navigation_routerating(head, ratingscale, 10000);
+}
+
+void havocbot_goalrating_ctf_ourbase(float ratingscale)
+{
+       entity head;
+       head = ctf_worldflaglist;
+       while (head)
+       {
+               if (self.team == head.team)
+                       break;
+               head = head.ctf_worldflagnext;
+       }
+       if not(head)
+               return;
+
+       navigation_routerating(head.bot_basewaypoint, ratingscale, 10000);
+}
+
+void havocbot_goalrating_ctf_enemyflag(float ratingscale)
+{
+       entity head;
+       head = ctf_worldflaglist;
+       while (head)
+       {
+               if (self.team != head.team)
+                       break;
+               head = head.ctf_worldflagnext;
+       }
+       if (head)
+               navigation_routerating(head, ratingscale, 10000);
+}
+
+void havocbot_goalrating_ctf_enemybase(float ratingscale)
+{
+       if not(bot_waypoints_for_items)
+       {
+               havocbot_goalrating_ctf_enemyflag(ratingscale);
+               return;
+       }
+
+       entity head;
+
+       head = havocbot_ctf_find_enemy_flag(self);
+
+       if not(head)
+               return;
+
+       navigation_routerating(head.bot_basewaypoint, ratingscale, 10000);
+}
+
+void havocbot_goalrating_ctf_ourstolenflag(float ratingscale)
+{
+       entity mf;
+
+       mf = havocbot_ctf_find_flag(self);
+
+       if(mf.ctf_status == FLAG_BASE)
+               return;
+
+       if(mf.tag_entity)
+               navigation_routerating(mf.tag_entity, ratingscale, 10000);
+}
+
+void havocbot_goalrating_ctf_droppedflags(float ratingscale, vector org, float radius)
+{
+       entity head;
+       head = ctf_worldflaglist;
+       while (head)
+       {
+               // flag is out in the field
+               if(head.ctf_status != FLAG_BASE)
+               if(head.tag_entity==world)      // dropped
+               {
+                       if(radius)
+                       {
+                               if(vlen(org-head.origin)<radius)
+                                       navigation_routerating(head, ratingscale, 10000);
+                       }
+                       else
+                               navigation_routerating(head, ratingscale, 10000);
+               }
+
+               head = head.ctf_worldflagnext;
+       }
+}
+
+void havocbot_goalrating_ctf_carrieritems(float ratingscale, vector org, float sradius)
+{
+       entity head;
+       float t;
+       head = findchainfloat(bot_pickup, TRUE);
+       while (head)
+       {
+               // gather health and armor only
+               if (head.solid)
+               if (head.health || head.armorvalue)
+               if (vlen(head.origin - org) < sradius)
+               {
+                       // get the value of the item
+                       t = head.bot_pickupevalfunc(self, head) * 0.0001;
+                       if (t > 0)
+                               navigation_routerating(head, t * ratingscale, 500);
+               }
+               head = head.chain;
+       }
+}
+
+void havocbot_role_ctf_setrole(entity bot, float role)
+{
+       dprint(strcat(bot.netname," switched to "));
+       switch(role)
+       {
+               case HAVOCBOT_CTF_ROLE_CARRIER:
+                       dprint("carrier");
+                       bot.havocbot_role = havocbot_role_ctf_carrier;
+                       bot.havocbot_role_timeout = 0;
+                       bot.havocbot_cantfindflag = time + 10;
+                       bot.bot_strategytime = 0;
+                       break;
+               case HAVOCBOT_CTF_ROLE_DEFENSE:
+                       dprint("defense");
+                       bot.havocbot_role = havocbot_role_ctf_defense;
+                       bot.havocbot_role_timeout = 0;
+                       break;
+               case HAVOCBOT_CTF_ROLE_MIDDLE:
+                       dprint("middle");
+                       bot.havocbot_role = havocbot_role_ctf_middle;
+                       bot.havocbot_role_timeout = 0;
+                       break;
+               case HAVOCBOT_CTF_ROLE_OFFENSE:
+                       dprint("offense");
+                       bot.havocbot_role = havocbot_role_ctf_offense;
+                       bot.havocbot_role_timeout = 0;
+                       break;
+               case HAVOCBOT_CTF_ROLE_RETRIEVER:
+                       dprint("retriever");
+                       bot.havocbot_previous_role = bot.havocbot_role;
+                       bot.havocbot_role = havocbot_role_ctf_retriever;
+                       bot.havocbot_role_timeout = time + 10;
+                       bot.bot_strategytime = 0;
+                       break;
+               case HAVOCBOT_CTF_ROLE_ESCORT:
+                       dprint("escort");
+                       bot.havocbot_previous_role = bot.havocbot_role;
+                       bot.havocbot_role = havocbot_role_ctf_escort;
+                       bot.havocbot_role_timeout = time + 30;
+                       bot.bot_strategytime = 0;
+                       break;
+       }
+       dprint("\n");
+}
+
+void havocbot_role_ctf_carrier()
+{
+       if(self.deadflag != DEAD_NO)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.flagcarried == world)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.bot_strategytime < time)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+
+               navigation_goalrating_start();
+               havocbot_goalrating_ctf_ourbase(50000);
+
+               if(self.health<100)
+                       havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000);
+
+               navigation_goalrating_end();
+
+               if (self.navigation_hasgoals)
+                       self.havocbot_cantfindflag = time + 10;
+               else if (time > self.havocbot_cantfindflag)
+               {
+                       // Can't navigate to my own base, suicide!
+                       // TODO: drop it and wander around
+                       Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
+                       return;
+               }
+       }
+}
+
+void havocbot_role_ctf_escort()
+{
+       entity mf, ef;
+
+       if(self.deadflag != DEAD_NO)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.flagcarried)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
+               return;
+       }
+
+       // If enemy flag is back on the base switch to previous role
+       ef = havocbot_ctf_find_enemy_flag(self);
+       if(ef.ctf_status==FLAG_BASE)
+       {
+               self.havocbot_role = self.havocbot_previous_role;
+               self.havocbot_role_timeout = 0;
+               return;
+       }
+
+       // If the flag carrier reached the base switch to defense
+       mf = havocbot_ctf_find_flag(self);
+       if(mf.ctf_status!=FLAG_BASE)
+       if(vlen(ef.origin - mf.dropped_origin) < 300)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_DEFENSE);
+               return;
+       }
+
+       // Set the role timeout if necessary
+       if (!self.havocbot_role_timeout)
+       {
+               self.havocbot_role_timeout = time + random() * 30 + 60;
+       }
+
+       // If nothing happened just switch to previous role
+       if (time > self.havocbot_role_timeout)
+       {
+               self.havocbot_role = self.havocbot_previous_role;
+               self.havocbot_role_timeout = 0;
+               return;
+       }
+
+       // Chase the flag carrier
+       if (self.bot_strategytime < time)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+               navigation_goalrating_start();
+               havocbot_goalrating_ctf_enemyflag(30000);
+               havocbot_goalrating_ctf_ourstolenflag(40000);
+               havocbot_goalrating_items(10000, self.origin, 10000);
+               navigation_goalrating_end();
+       }
+}
+
+void havocbot_role_ctf_offense()
+{
+       entity mf, ef;
+       vector pos;
+
+       if(self.deadflag != DEAD_NO)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.flagcarried)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
+               return;
+       }
+
+       // Check flags
+       mf = havocbot_ctf_find_flag(self);
+       ef = havocbot_ctf_find_enemy_flag(self);
+
+       // Own flag stolen
+       if(mf.ctf_status!=FLAG_BASE)
+       {
+               if(mf.tag_entity)
+                       pos = mf.tag_entity.origin;
+               else
+                       pos = mf.origin;
+
+               // Try to get it if closer than the enemy base
+               if(vlen(self.origin-ef.dropped_origin)>vlen(self.origin-pos))
+               {
+                       havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
+                       return;
+               }
+       }
+
+       // Escort flag carrier
+       if(ef.ctf_status!=FLAG_BASE)
+       {
+               if(ef.tag_entity)
+                       pos = ef.tag_entity.origin;
+               else
+                       pos = ef.origin;
+
+               if(vlen(pos-mf.dropped_origin)>700)
+               {
+                       havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_ESCORT);
+                       return;
+               }
+       }
+
+       // About to fail, switch to middlefield
+       if(self.health<50)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_MIDDLE);
+               return;
+       }
+
+       // Set the role timeout if necessary
+       if (!self.havocbot_role_timeout)
+               self.havocbot_role_timeout = time + 120;
+
+       if (time > self.havocbot_role_timeout)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.bot_strategytime < time)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+               navigation_goalrating_start();
+               havocbot_goalrating_ctf_ourstolenflag(50000);
+               havocbot_goalrating_ctf_enemybase(20000);
+               havocbot_goalrating_items(5000, self.origin, 1000);
+               havocbot_goalrating_items(1000, self.origin, 10000);
+               navigation_goalrating_end();
+       }
+}
+
+// Retriever (temporary role):
+void havocbot_role_ctf_retriever()
+{
+       entity mf;
+
+       if(self.deadflag != DEAD_NO)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.flagcarried)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
+               return;
+       }
+
+       // If flag is back on the base switch to previous role
+       mf = havocbot_ctf_find_flag(self);
+       if(mf.ctf_status==FLAG_BASE)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (!self.havocbot_role_timeout)
+               self.havocbot_role_timeout = time + 20;
+
+       if (time > self.havocbot_role_timeout)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.bot_strategytime < time)
+       {
+               float radius;
+               radius = 10000;
+
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+               navigation_goalrating_start();
+               havocbot_goalrating_ctf_ourstolenflag(50000);
+               havocbot_goalrating_ctf_droppedflags(40000, self.origin, radius);
+               havocbot_goalrating_ctf_enemybase(30000);
+               havocbot_goalrating_items(500, self.origin, radius);
+               navigation_goalrating_end();
+       }
+}
+
+void havocbot_role_ctf_middle()
+{
+       entity mf;
+
+       if(self.deadflag != DEAD_NO)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.flagcarried)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
+               return;
+       }
+
+       mf = havocbot_ctf_find_flag(self);
+       if(mf.ctf_status!=FLAG_BASE)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
+               return;
+       }
+
+       if (!self.havocbot_role_timeout)
+               self.havocbot_role_timeout = time + 10;
+
+       if (time > self.havocbot_role_timeout)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.bot_strategytime < time)
+       {
+               vector org;
+
+               org = havocbot_ctf_middlepoint;
+               org_z = self.origin_z;
+
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+               navigation_goalrating_start();
+               havocbot_goalrating_ctf_ourstolenflag(50000);
+               havocbot_goalrating_ctf_droppedflags(30000, self.origin, 10000);
+               havocbot_goalrating_enemyplayers(10000, org, havocbot_ctf_middlepoint_radius * 0.5);
+               havocbot_goalrating_items(5000, org, havocbot_ctf_middlepoint_radius * 0.5);
+               havocbot_goalrating_items(2500, self.origin, 10000);
+               havocbot_goalrating_ctf_enemybase(2500);
+               navigation_goalrating_end();
+       }
+}
+
+void havocbot_role_ctf_defense()
+{
+       entity mf;
+
+       if(self.deadflag != DEAD_NO)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.flagcarried)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
+               return;
+       }
+
+       // If own flag was captured
+       mf = havocbot_ctf_find_flag(self);
+       if(mf.ctf_status!=FLAG_BASE)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
+               return;
+       }
+
+       if (!self.havocbot_role_timeout)
+               self.havocbot_role_timeout = time + 30;
+
+       if (time > self.havocbot_role_timeout)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+       if (self.bot_strategytime < time)
+       {
+               float radius;
+               vector org;
+
+               org = mf.dropped_origin;
+               radius = havocbot_ctf_middlepoint_radius;
+
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+               navigation_goalrating_start();
+
+               // if enemies are closer to our base, go there
+               entity head, closestplayer = world;
+               float distance, bestdistance = 10000;
+               FOR_EACH_PLAYER(head)
+               {
+                       if(head.deadflag!=DEAD_NO)
+                               continue;
+
+                       distance = vlen(org - head.origin);
+                       if(distance<bestdistance)
+                       {
+                               closestplayer = head;
+                               bestdistance = distance;
+                       }
+               }
+
+               if(closestplayer)
+               if(closestplayer.team!=self.team)
+               if(vlen(org - self.origin)>1000)
+               if(checkpvs(self.origin,closestplayer)||random()<0.5)
+                       havocbot_goalrating_ctf_ourbase(30000);
+
+               havocbot_goalrating_ctf_ourstolenflag(20000);
+               havocbot_goalrating_ctf_droppedflags(20000, org, radius);
+               havocbot_goalrating_enemyplayers(15000, org, radius);
+               havocbot_goalrating_items(10000, org, radius);
+               havocbot_goalrating_items(5000, self.origin, 10000);
+               navigation_goalrating_end();
+       }
+}
+
+void havocbot_calculate_middlepoint()
+{
+       entity f;
+       vector s = '0 0 0';
+       vector fo = '0 0 0';
+       float n = 0;
+
+       f = ctf_worldflaglist;
+       while (f)
+       {
+               fo = f.origin;
+               s = s + fo;
+               f = f.ctf_worldflagnext;
+       }
+       if(!n)
+               return;
+       havocbot_ctf_middlepoint = s * (1.0 / n);
+       havocbot_ctf_middlepoint_radius  = vlen(fo - havocbot_ctf_middlepoint);
+}
+
+void havocbot_ctf_reset_role(entity bot)
+{
+       float cdefense, cmiddle, coffense;
+       entity mf, ef, head;
+       float c;
+
+       if(bot.deadflag != DEAD_NO)
+               return;
+
+       if(vlen(havocbot_ctf_middlepoint)==0)
+               havocbot_calculate_middlepoint();
+
+       // Check ctf flags
+       if (bot.flagcarried)
+       {
+               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_CARRIER);
+               return;
+       }
+
+       mf = havocbot_ctf_find_flag(bot);
+       ef = havocbot_ctf_find_enemy_flag(bot);
+
+       // Retrieve stolen flag
+       if(mf.ctf_status!=FLAG_BASE)
+       {
+               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_RETRIEVER);
+               return;
+       }
+
+       // If enemy flag is taken go to the middle to intercept pursuers
+       if(ef.ctf_status!=FLAG_BASE)
+       {
+               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);
+               return;
+       }
+
+       // if there is only me on the team switch to offense
+       c = 0;
+       FOR_EACH_PLAYER(head)
+       if(head.team==bot.team)
+               ++c;
+
+       if(c==1)
+       {
+               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE);
+               return;
+       }
+
+       // Evaluate best position to take
+       // Count mates on middle position
+       cmiddle = havocbot_ctf_teamcount(bot, havocbot_ctf_middlepoint, havocbot_ctf_middlepoint_radius * 0.5);
+
+       // Count mates on defense position
+       cdefense = havocbot_ctf_teamcount(bot, mf.dropped_origin, havocbot_ctf_middlepoint_radius * 0.5);
+
+       // Count mates on offense position
+       coffense = havocbot_ctf_teamcount(bot, ef.dropped_origin, havocbot_ctf_middlepoint_radius);
+
+       if(cdefense<=coffense)
+               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_DEFENSE);
+       else if(coffense<=cmiddle)
+               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE);
+       else
+               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);
+}
+
+void havocbot_chooserole_ctf()
+{
+       havocbot_ctf_reset_role(self);
+}
diff --git a/qcsrc/server/attic/bot/havocbot/role_freezetag.qc b/qcsrc/server/attic/bot/havocbot/role_freezetag.qc
new file mode 100644 (file)
index 0000000..4e5669e
--- /dev/null
@@ -0,0 +1,109 @@
+void() havocbot_role_ft_freeing;
+void() havocbot_role_ft_offense;
+
+void havocbot_goalrating_freeplayers(float ratingscale, vector org, float sradius)
+{
+       entity head;
+       float distance;
+
+       FOR_EACH_PLAYER(head)
+       {
+               if ((head != self) && (head.team == self.team))
+               {
+                       if (head.freezetag_frozen)
+                       {
+                               distance = vlen(head.origin - org);
+                               if (distance > sradius)
+                                       continue;
+                               navigation_routerating(head, ratingscale, 2000);
+                       }
+                       else
+                       {
+                               // If teamate is not frozen still seek them out as fight better
+                               // in a group.
+                               navigation_routerating(head, ratingscale/3, 2000);
+                       }
+               }
+       }
+}
+
+void havocbot_role_ft_offense()
+{
+       entity head;
+       float unfrozen;
+
+       if(self.deadflag != DEAD_NO)
+               return;
+
+       if (!self.havocbot_role_timeout)
+               self.havocbot_role_timeout = time + random() * 10 + 20;
+
+       // Count how many players on team are unfrozen.
+       unfrozen = 0;
+       FOR_EACH_PLAYER(head)
+       {
+               if ((head.team == self.team) && (!head.freezetag_frozen))
+                       unfrozen++;
+       }
+
+       // If only one left on team or if role has timed out then start trying to free players.
+       if (((unfrozen == 0) && (!self.freezetag_frozen)) || (time > self.havocbot_role_timeout))
+       {
+               dprint("changing role to freeing\n");
+               self.havocbot_role = havocbot_role_ft_freeing;
+               self.havocbot_role_timeout = 0;
+               return;
+       }
+
+       if (time > self.bot_strategytime)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+
+               navigation_goalrating_start();
+               havocbot_goalrating_items(10000, self.origin, 10000);
+               havocbot_goalrating_enemyplayers(20000, self.origin, 10000);
+               havocbot_goalrating_freeplayers(9000, self.origin, 10000);
+               //havocbot_goalrating_waypoints(1, self.origin, 1000);
+               navigation_goalrating_end();
+       }
+}
+
+void havocbot_role_ft_freeing()
+{
+       if(self.deadflag != DEAD_NO)
+               return;
+
+       if (!self.havocbot_role_timeout)
+               self.havocbot_role_timeout = time + random() * 10 + 20;
+
+       if (time > self.havocbot_role_timeout)
+       {
+               dprint("changing role to offense\n");
+               self.havocbot_role = havocbot_role_ft_offense;
+               self.havocbot_role_timeout = 0;
+               return;
+       }
+
+       if (time > self.bot_strategytime)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+
+               navigation_goalrating_start();
+               havocbot_goalrating_items(8000, self.origin, 10000);
+               havocbot_goalrating_enemyplayers(10000, self.origin, 10000);
+               havocbot_goalrating_freeplayers(20000, self.origin, 10000);
+               //havocbot_goalrating_waypoints(1, self.origin, 1000);
+               navigation_goalrating_end();
+       }
+}
+
+void havocbot_chooserole_ft()
+{
+       if(self.deadflag != DEAD_NO)
+               return;
+
+       if (random() < 0.5)
+               self.havocbot_role = havocbot_role_ft_freeing;
+       else
+               self.havocbot_role = havocbot_role_ft_offense;
+}
diff --git a/qcsrc/server/attic/bot/havocbot/role_keepaway.qc b/qcsrc/server/attic/bot/havocbot/role_keepaway.qc
new file mode 100644 (file)
index 0000000..ede6208
--- /dev/null
@@ -0,0 +1,82 @@
+void() havocbot_role_ka_carrier;
+void() havocbot_role_ka_collector;
+void() havocbot_chooserole_ka;
+
+entity ka_ball;
+
+// Keepaway
+// If you don't have the ball, get it; if you do, kill people.
+
+void havocbot_goalrating_ball(float ratingscale, vector org)
+{
+       float t;
+       entity ball_owner;
+       ball_owner = ka_ball.owner;
+
+       if (ball_owner == self)
+               return;
+
+       // If ball is carried by player then hunt them down.
+       if (ball_owner)
+       {
+               t = (self.health + self.armorvalue) / (ball_owner.health + ball_owner.armorvalue);
+               navigation_routerating(ball_owner, t * ratingscale, 2000);
+       }
+
+       // Ball has been dropped so collect.
+       navigation_routerating(ka_ball, ratingscale, 2000);
+}
+
+void havocbot_role_ka_carrier()
+{
+       if (self.deadflag != DEAD_NO)
+               return;
+
+       if (time > self.bot_strategytime)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+
+               navigation_goalrating_start();
+               havocbot_goalrating_items(10000, self.origin, 10000);
+               havocbot_goalrating_enemyplayers(20000, self.origin, 10000);
+               //havocbot_goalrating_waypoints(1, self.origin, 1000);
+               navigation_goalrating_end();
+       }
+
+       if (!self.ballcarried)
+       {
+               self.havocbot_role = havocbot_role_ka_collector;
+               self.bot_strategytime = 0;
+       }
+}
+
+void havocbot_role_ka_collector()
+{
+       if (self.deadflag != DEAD_NO)
+               return;
+
+       if (time > self.bot_strategytime)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+
+               navigation_goalrating_start();
+               havocbot_goalrating_items(10000, self.origin, 10000);
+               havocbot_goalrating_enemyplayers(1000, self.origin, 10000);
+               havocbot_goalrating_ball(20000, self.origin);
+               navigation_goalrating_end();
+       }
+
+       if (self.ballcarried)
+       {
+               self.havocbot_role = havocbot_role_ka_carrier;
+               self.bot_strategytime = 0;
+       }
+}
+
+void havocbot_chooserole_ka()
+{
+       if (self.ballcarried)
+               self.havocbot_role = havocbot_role_ka_carrier;
+       else
+               self.havocbot_role = havocbot_role_ka_collector;
+}
diff --git a/qcsrc/server/attic/ctf.qc b/qcsrc/server/attic/ctf.qc
new file mode 100644 (file)
index 0000000..d65d866
--- /dev/null
@@ -0,0 +1,1226 @@
+#define FLAG_MIN (PL_MIN + '0 0 -13')
+#define FLAG_MAX (PL_MAX + '0 0 -13')
+
+.entity basewaypoint;
+.entity sprite;
+entity ctf_worldflaglist; // CTF flags in the map
+.entity ctf_worldflagnext;
+.float dropperid;
+.float ctf_droptime;
+
+.float next_take_time;                 // the next time a player can pick up a flag (time + blah)
+                                                               /// I used this, in part, to fix the looping score bug. - avirox
+//float FLAGSCORE_PICKUP        =  1;
+//float FLAGSCORE_RETURN        =  5; // returned by owner team
+//float FLAGSCORE_RETURNROGUE   = 10; // returned by rogue team
+//float FLAGSCORE_CAPTURE       =  5;
+
+#define FLAG_CARRY_POS '-15 0 7'
+
+.float ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture
+
+float captureshield_min_negscore; // punish at -20 points
+float captureshield_max_ratio; // punish at most 30% of each team
+float captureshield_force; // push force of the shield
+
+float ctf_captureshield_shielded(entity p)
+{
+       float s, se;
+       entity e;
+       float players_worseeq, players_total;
+
+       if(captureshield_max_ratio <= 0)
+               return FALSE;
+
+       s = PlayerScore_Add(p, SP_SCORE, 0);
+       if(s >= -captureshield_min_negscore)
+               return FALSE;
+
+       players_total = players_worseeq = 0;
+       FOR_EACH_PLAYER(e)
+       {
+               if(e.team != p.team)
+                       continue;
+               se = PlayerScore_Add(e, SP_SCORE, 0);
+               if(se <= s)
+                       ++players_worseeq;
+               ++players_total;
+       }
+
+       // player is in the worse half, if >= half the players are better than him, or consequently, if < half of the players are worse
+       // use this rule here
+
+       if(players_worseeq >= players_total * captureshield_max_ratio)
+               return FALSE;
+
+       return TRUE;
+}
+
+void ctf_captureshield_update(entity p, float dir)
+{
+       float should;
+       if(dir == p.ctf_captureshielded) // 0: shield only, 1: unshield only
+       {
+               should = ctf_captureshield_shielded(p);
+               if(should != dir)
+               {
+                       if(should)
+                       {
+                               Send_CSQC_Centerprint_Generic(other, CPID_CTF_CAPTURESHIELD, "^3You are ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Get some defensive scores before trying again.", 5, 0);
+                               // TODO csqc notifier for this
+                       }
+                       else
+                       {
+                               Send_CSQC_Centerprint_Generic(p, CPID_CTF_CAPTURESHIELD, "^3You are now free.\n\n^3Feel free to ^1try to capture^3 the flag again\n^3if you think you will succeed.", 5, 0);
+                               // TODO csqc notifier for this
+                       }
+                       p.ctf_captureshielded = should;
+               }
+       }
+}
+
+float ctf_captureshield_customize()
+{
+       if not(other.ctf_captureshielded)
+               return FALSE;
+       if(self.team == other.team)
+               return FALSE;
+       return TRUE;
+}
+
+.float ctf_captureshield_touch_msgtime;
+void ctf_captureshield_touch()
+{
+       if not(other.ctf_captureshielded)
+               return;
+       if(self.team == other.team)
+               return;
+       vector mymid;
+       vector othermid;
+       mymid = (self.absmin + self.absmax) * 0.5;
+       othermid = (other.absmin + other.absmax) * 0.5;
+       Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * captureshield_force);
+       if (time - other.ctf_captureshield_touch_msgtime > 2)
+               Send_CSQC_Centerprint_Generic(other, CPID_CTF_CAPTURESHIELD, "^3You are ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Get some defensive scores before trying again.", 5, 0);
+       other.ctf_captureshield_touch_msgtime = time;
+}
+
+void ctf_flag_spawnstuff()
+{
+       entity e;
+       e = spawn();
+       e.enemy = self;
+       e.team = self.team;
+       e.touch = ctf_captureshield_touch;
+       e.customizeentityforclient = ctf_captureshield_customize;
+       e.classname = "ctf_captureshield";
+       e.effects = EF_ADDITIVE;
+       e.movetype = MOVETYPE_NOCLIP;
+       e.solid = SOLID_TRIGGER;
+       e.avelocity = '7 0 11';
+       setorigin(e, self.origin);
+       setmodel(e, "models/ctf/shield.md3");
+       e.scale = 0.5;
+       setsize(e, e.scale * e.mins, e.scale * e.maxs);
+
+       waypoint_spawnforitem_force(self, self.origin);
+       self.nearestwaypointtimeout = 0; // activate waypointing again
+       self.basewaypoint = self.nearestwaypoint;
+
+       if(self.team == COLOR_TEAM1)
+               WaypointSprite_SpawnFixed("redbase", self.origin + '0 0 61', self, sprite, RADARICON_FLAG, colormapPaletteColor(COLOR_TEAM1 - 1, FALSE));
+       else
+               WaypointSprite_SpawnFixed("bluebase", self.origin + '0 0 61', self, sprite, RADARICON_FLAG, colormapPaletteColor(COLOR_TEAM2 - 1, FALSE));
+}
+
+float ctf_score_value(string parameter)
+{
+       return cvar(strcat("g_ctf_personal", parameter));
+}
+
+void FakeTimeLimit(entity e, float t)
+{
+       msg_entity = e;
+       WriteByte(MSG_ONE, 3); // svc_updatestat
+       WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT
+       if(t < 0)
+               WriteCoord(MSG_ONE, autocvar_timelimit);
+       else
+               WriteCoord(MSG_ONE, (t + 1) / 60);
+}
+
+float   flagcaptimerecord;
+.float  flagpickuptime;
+//.float  iscommander;
+//.float  ctf_state;
+
+void() FlagThink;
+void() FlagTouch;
+
+void place_flag()
+{
+       if(self.classname != "item_flag_team")
+       {
+               backtrace("PlaceFlag a non-flag");
+               return;
+       }
+
+       setattachment(self, world, "");
+       self.mdl = self.model;
+       self.flags = FL_ITEM | FL_NOTARGET;
+       self.solid = SOLID_TRIGGER;
+       self.movetype = MOVETYPE_NONE;
+       self.velocity = '0 0 0';
+       self.origin_z = self.origin_z + 6;
+       self.think = FlagThink;
+       self.touch = FlagTouch;
+       self.nextthink = time + 0.1;
+       self.cnt = FLAG_BASE;
+       self.mangle = self.angles;
+       self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
+       //self.effects = self.effects | EF_DIMLIGHT;
+       if(self.noalign)
+       {
+               self.dropped_origin = self.origin;
+       }
+       else
+       {
+               droptofloor();
+               self.movetype = MOVETYPE_TOSS;
+       }
+
+       InitializeEntity(self, ctf_flag_spawnstuff, INITPRIO_SETLOCATION);
+}
+
+void LogCTF(string mode, float flagteam, entity actor)
+{
+       string s;
+       if(!autocvar_sv_eventlog)
+               return;
+       s = strcat(":ctf:", mode);
+       s = strcat(s, ":", ftos(flagteam));
+       if(actor != world)
+               s = strcat(s, ":", ftos(actor.playerid));
+       GameLogEcho(s);
+}
+
+void RegenFlag(entity e)
+{
+       if(e.classname != "item_flag_team")
+       {
+               backtrace("RegenFlag a non-flag");
+               return;
+       }
+
+       if(e.waypointsprite_attachedforcarrier)
+               WaypointSprite_DetachCarrier(e);
+
+       setattachment(e, world, "");
+       e.damageforcescale = 0;
+       e.takedamage = DAMAGE_NO;
+       e.movetype = MOVETYPE_NONE;
+       if(!e.noalign)
+               e.movetype = MOVETYPE_TOSS;
+       e.velocity = '0 0 0';
+       e.solid = SOLID_TRIGGER;
+       // TODO: play a sound here
+       setorigin(e, e.dropped_origin);
+       e.angles = e.mangle;
+       e.cnt = FLAG_BASE;
+       e.owner = world;
+       e.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND and any other junk
+}
+
+void ReturnFlag(entity e)
+{
+       if(e.classname != "item_flag_team")
+       {
+               backtrace("ReturnFlag a non-flag");
+               return;
+       }
+
+       if (e.owner)
+       if (e.owner.flagcarried == e)
+       {
+               WaypointSprite_DetachCarrier(e.owner);
+               e.owner.flagcarried = world;
+
+               if(e.speedrunning)
+                       FakeTimeLimit(e.owner, -1);
+       }
+       e.owner = world;
+       RegenFlag(e);
+}
+
+void DropFlag(entity e, entity penalty_receiver, entity attacker)
+{
+       entity p;
+
+       if(e.classname != "item_flag_team")
+       {
+               backtrace("DropFlag a non-flag");
+               return;
+       }
+
+       if(e.speedrunning)
+       {
+               ReturnFlag(e);
+               return;
+       }
+
+       if (!e.owner)
+       {
+               dprint("FLAG: drop - no owner?!?!\n");
+               return;
+       }
+       p = e.owner;
+       if (p.flagcarried != e)
+       {
+               dprint("FLAG: drop - owner is not carrying this flag??\n");
+               return;
+       }
+       //bprint(p.netname, "^7 lost the ", e.netname, "\n");
+       Send_KillNotification (p.netname, e.netname, "", INFO_LOSTFLAG, MSG_INFO);
+
+       if(penalty_receiver)
+               UpdateFrags(penalty_receiver, -ctf_score_value("penalty_suicidedrop"));
+       else
+               UpdateFrags(p, -ctf_score_value("penalty_drop"));
+       PlayerScore_Add(p, SP_CTF_DROPS, +1);
+       ctf_captureshield_update(p, 0); // shield only
+       e.playerid = attacker.playerid;
+       e.ctf_droptime = time;
+       WaypointSprite_Spawn("flagdropped", 0, 0, e, '0 0 1' * 61, world, COLOR_TEAM1 + COLOR_TEAM2 - e.team, e, waypointsprite_attachedforcarrier, FALSE, RADARICON_FLAG, '0 1 1');
+       WaypointSprite_Ping(e.waypointsprite_attachedforcarrier);
+       
+       if(p.waypointsprite_attachedforcarrier)
+       {
+               WaypointSprite_DetachCarrier(p);
+       }
+       else
+       {
+               bprint("\{1}^1Flag carrier had no flag sprite?!?\n");
+               backtrace("Flag carrier had no flag sprite?!?");
+       }
+       LogCTF("dropped", p.team, p);
+       sound (p, CH_TRIGGER, self.noise4, VOL_BASE, ATTN_NONE);
+
+       setattachment(e, world, "");
+       e.damageforcescale = autocvar_g_balance_ctf_damageforcescale;
+       e.takedamage = DAMAGE_YES;
+
+       if (p.flagcarried == e)
+               p.flagcarried = world;
+       e.owner = world;
+
+       e.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND and any other junk
+       e.solid = SOLID_TRIGGER;
+       e.movetype = MOVETYPE_TOSS;
+       // setsize(e, '-16 -16 0', '16 16 74');
+       setorigin(e, p.origin - '0 0 24' + '0 0 37');
+       e.cnt = FLAG_DROPPED;
+       e.velocity = '0 0 300';
+       e.pain_finished = time + autocvar_g_ctf_flag_returntime;//30;
+
+       trace_startsolid = FALSE;
+       tracebox(e.origin, e.mins, e.maxs, e.origin, TRUE, e);
+       if(trace_startsolid)
+               dprint("FLAG FALLTHROUGH will happen SOON\n");
+}
+
+void FlagThink()
+{
+       entity e;
+
+       self.nextthink = time + 0.1;
+
+       // sorry, we have to reset the flag size if it got squished by something
+       if(self.mins != FLAG_MIN || self.maxs != FLAG_MAX)
+       {
+               // if we can grow back, grow back
+               tracebox(self.origin, FLAG_MIN, FLAG_MAX, self.origin, MOVE_NOMONSTERS, self);
+               if(!trace_startsolid)
+                       setsize(self, FLAG_MIN, FLAG_MAX);
+       }
+
+       if(self == ctf_worldflaglist) // only for the first flag
+       {
+               FOR_EACH_CLIENT(e)
+                       ctf_captureshield_update(e, 1); // release shield only
+       }
+
+       if(self.speedrunning)
+       if(self.cnt == FLAG_CARRY)
+       {
+               if(self.owner)
+               if(flagcaptimerecord)
+               if(time >= self.flagpickuptime + flagcaptimerecord)
+               {
+                       bprint("The ", self.netname, " became impatient after ", ftos_decimals(flagcaptimerecord, 2), " seconds and returned itself\n");
+
+                       sound (self, CH_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE);
+                       self.owner.impulse = 141; // returning!
+
+                       e = self;
+                       self = self.owner;
+                       ReturnFlag(e);
+                       ImpulseCommands();
+                       self = e;
+                       return;
+               }
+       }
+
+       if (self.cnt == FLAG_BASE)
+               return;
+
+       if (self.cnt == FLAG_DROPPED)
+       {
+               // flag fallthrough? FIXME remove this if bug is really fixed now
+               if(self.origin_z < -131072)
+               {
+                       dprint("FLAG FALLTHROUGH just happened\n");
+                       self.pain_finished = 0;
+               }
+               setattachment(self, world, "");
+               if (time > self.pain_finished)
+               {
+                       bprint("The ", self.netname, " has returned to base\n");
+                       sound (self, CH_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE);
+                       LogCTF("returned", self.team, world);
+                       ReturnFlag(self);
+               }
+               return;
+       }
+
+       e = self.owner;
+       if (e.classname != "player" || (e.deadflag) || (e.flagcarried != self))
+       {
+               dprint("CANNOT HAPPEN - player dead and STILL had a flag!\n");
+               DropFlag(self, world, world);
+               return;
+       }
+}
+
+float ctf_usekey()
+{
+       if(self.flagcarried)
+       {
+               DropFlag(self.flagcarried, self, world);
+               return TRUE;
+       }
+       return FALSE;
+}
+
+void flag_cap_ring_spawn(vector org)
+{
+       shockwave_spawn("models/ctf/shockwavetransring.md3", org - '0 0 15', -0.8, 0, 1);
+}
+
+// TODO add FlagDamage, replace weird hurttrigger handling inside trigger_hurt code by it
+void FlagTouch()
+{
+       if(gameover) return;
+
+       float t;
+       entity player;
+       string s, s0, h0, h1;
+
+       if (self.cnt == FLAG_CARRY)
+               return;
+
+       if (self.cnt == FLAG_DROPPED)
+       {
+               if(ITEM_TOUCH_NEEDKILL())
+               {
+                       self.pain_finished = 0; // return immediately
+                       return;
+               }
+       }
+
+       if (other.classname != "player")
+               return;
+       if (other.health < 1) // ignore dead players
+               return;
+
+       if (self.cnt == FLAG_BASE)
+       if (other.team == self.team)
+       if (other.flagcarried) // he's got a flag
+       if (other.flagcarried.team != self.team) // capture
+       {
+               if (other.flagcarried == world)
+               {
+                       return;
+               }
+               if(autocvar_g_ctf_captimerecord_always || player_count - currentbots <= 1) // at most one human
+               {
+                       t = time - other.flagcarried.flagpickuptime;
+                       s = ftos_decimals(t, 2);
+                       s0 = ftos_decimals(flagcaptimerecord, 2);
+                       h0 = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"));
+                       h1 = other.netname;
+                       if(h0 == h1)
+                               h0 = "their";
+                       else
+                               h0 = strcat(h0, "^7's"); // h0: display text for previous netname
+                       if (flagcaptimerecord == 0)
+                       {
+                               s = strcat(" in ", s, " seconds");
+                               flagcaptimerecord = t;
+                               db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(t));
+                               db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), h1);
+                               write_recordmarker(other, time - t, t);
+                       }
+                       else if (t < flagcaptimerecord)
+                       {
+                               s = strcat(" in ", s, " seconds, breaking ", h0, " previous record of ", s0, " seconds");
+                               flagcaptimerecord = t;
+                               db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(t));
+                               db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), h1);
+                               write_recordmarker(other, time - t, t);
+                       }
+                       else
+                       {
+                               s = strcat(" in ", s, " seconds, failing to break ", h0, " record of ", s0, " seconds");
+                       }
+               }
+               else
+                       s = "";
+
+               Send_KillNotification (other.netname, other.flagcarried.netname, s, INFO_CAPTUREFLAG, MSG_INFO);
+
+               PlayerTeamScore_Add(other, SP_CTF_CAPS, ST_CTF_CAPS, 1);
+               LogCTF("capture", other.flagcarried.team, other);
+               // give credit to the individual player
+               UpdateFrags(other, ctf_score_value("score_capture"));
+
+               if (autocvar_g_ctf_flag_capture_effects) {
+                       if (other.team == COLOR_TEAM1) { // red team scores effect
+                               pointparticles(particleeffectnum("red_ground_quake"), self.origin, '0 0 0', 1);
+                               flag_cap_ring_spawn(self.origin);
+                       }
+                       if (other.team == COLOR_TEAM2) { // blue team scores effect
+                               pointparticles(particleeffectnum("blue_ground_quake"), self.origin, '0 0 0', 1);
+                               flag_cap_ring_spawn(self.origin);
+                       }
+               }
+
+               sound (other, CH_TRIGGER, self.noise2, VOL_BASE, ATTN_NONE);
+               WaypointSprite_DetachCarrier(other);
+               if(self.speedrunning)
+                       FakeTimeLimit(other, -1);
+               RegenFlag (other.flagcarried);
+               other.flagcarried = world;
+               other.next_take_time = time + 1;
+       }
+       if (self.cnt == FLAG_BASE)
+       if (other.team == COLOR_TEAM1 || other.team == COLOR_TEAM2) // only red and blue team can steal flags
+       if (other.team != self.team)
+       if (!other.flagcarried)
+       if (!other.ctf_captureshielded)
+       {
+               if(MUTATOR_CALLHOOK(ItemTouch))
+                       return;
+               
+               if (other.next_take_time > time)
+                       return;
+
+               if (autocvar_g_ctf_flag_pickup_effects) // pickup effect
+                       pointparticles(particleeffectnum("smoke_ring"), 0.5 * (self.absmin + self.absmax), '0 0 0', 1);
+
+               // pick up
+               self.flagpickuptime = time; // used for timing runs
+               self.speedrunning = other.speedrunning; // if speedrunning, flag will self-return and teleport the owner back after the record
+               if(other.speedrunning)
+               if(flagcaptimerecord)
+                       FakeTimeLimit(other, time + flagcaptimerecord);
+               self.solid = SOLID_NOT;
+               setorigin(self, self.origin); // relink
+               self.owner = other;
+               other.flagcarried = self;
+               self.cnt = FLAG_CARRY;
+               self.angles = '0 0 0';
+               //bprint(other.netname, "^7 got the ", self.netname, "\n");
+               Send_KillNotification (other.netname, self.netname, "", INFO_GOTFLAG, MSG_INFO);
+               UpdateFrags(other, ctf_score_value("score_pickup_base"));
+               self.dropperid = other.playerid;
+               PlayerScore_Add(other, SP_CTF_PICKUPS, 1);
+               LogCTF("steal", self.team, other);
+               sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NONE);
+
+               FOR_EACH_PLAYER(player)
+                       if(player.team == self.team)
+                               centerprint(player, "The enemy got your flag! Retrieve it!");
+
+               self.movetype = MOVETYPE_NONE;
+               setorigin(self, FLAG_CARRY_POS);
+               setattachment(self, other, "");
+               WaypointSprite_AttachCarrier("flagcarrier", other, RADARICON_FLAGCARRIER, '1 1 0');
+               WaypointSprite_Ping(self.sprite);
+
+               return;
+       }
+
+       if (self.cnt == FLAG_DROPPED)
+       {
+               self.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND and any other junk
+               if (other.team == self.team || (other.team != COLOR_TEAM1 && other.team != COLOR_TEAM2))
+               {
+                       // return flag
+                       Send_KillNotification (other.netname, self.netname, "", INFO_RETURNFLAG, MSG_INFO);
+                       //bprint(other.netname, "^7 returned the ", self.netname, "\n");
+
+                       // punish the player who last had it
+                       FOR_EACH_PLAYER(player)
+                               if(player.playerid == self.dropperid)
+                               {
+                                       PlayerScore_Add(player, SP_SCORE, -ctf_score_value("penalty_returned"));
+                                       ctf_captureshield_update(player, 0); // shield only
+                               }
+
+                       // punish the team who was last carrying it
+                       if(self.team == COLOR_TEAM1)
+                               TeamScore_AddToTeam(COLOR_TEAM2, ST_SCORE, -ctf_score_value("penalty_returned"));
+                       else
+                               TeamScore_AddToTeam(COLOR_TEAM1, ST_SCORE, -ctf_score_value("penalty_returned"));
+
+                       // reward the player who returned it
+                       if(other.playerid == self.playerid) // is this the guy who killed the FC last?
+                       {
+                               if (other.team == COLOR_TEAM1 || other.team == COLOR_TEAM2)
+                                       UpdateFrags(other, ctf_score_value("score_return_by_killer"));
+                               else
+                                       UpdateFrags(other, ctf_score_value("score_return_rogue_by_killer"));
+                       }
+                       else
+                       {
+                               if (other.team == COLOR_TEAM1 || other.team == COLOR_TEAM2)
+                                       UpdateFrags(other, ctf_score_value("score_return"));
+                               else
+                                       UpdateFrags(other, ctf_score_value("score_return_rogue"));
+                       }
+                       PlayerScore_Add(other, SP_CTF_RETURNS, 1);
+                       LogCTF("return", self.team, other);
+                       sound (other, CH_TRIGGER, self.noise1, VOL_BASE, ATTN_NONE);
+                       ReturnFlag(self);
+               }
+               else if (!other.flagcarried && (other.playerid != self.dropperid || time > self.ctf_droptime + autocvar_g_balance_ctf_delay_collect))
+               {
+                       if(self.waypointsprite_attachedforcarrier)
+                               WaypointSprite_DetachCarrier(self);
+
+                       if (autocvar_g_ctf_flag_pickup_effects) // field pickup effect
+                               pointparticles(particleeffectnum("smoke_ring"), 0.5 * (self.absmin + self.absmax), '0 0 0', 1);
+
+                       // pick up
+                       self.solid = SOLID_NOT;
+                       setorigin(self, self.origin); // relink
+                       self.owner = other;
+                       other.flagcarried = self;
+                       self.cnt = FLAG_CARRY;
+                       Send_KillNotification (other.netname, self.netname, "", INFO_PICKUPFLAG, MSG_INFO);
+                       //bprint(other.netname, "^7 picked up the ", self.netname, "\n");
+
+                       float f;
+                       f = bound(0, (self.pain_finished - time) / autocvar_g_ctf_flag_returntime, 1);
+                       //print("factor is ", ftos(f), "\n");
+                       f = ctf_score_value("score_pickup_dropped_late") * (1-f)
+                         + ctf_score_value("score_pickup_dropped_early") * f;
+                       f = floor(f + 0.5);
+                       self.dropperid = other.playerid;
+                       //print("score is ", ftos(f), "\n");
+
+                       UpdateFrags(other, f);
+                       PlayerScore_Add(other, SP_CTF_PICKUPS, 1);
+                       LogCTF("pickup", self.team, other);
+                       sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NONE);
+
+                       FOR_EACH_PLAYER(player)
+                               if(player.team == self.team)
+                                       centerprint(player, "The enemy got your flag! Retrieve it!");
+
+                       self.movetype = MOVETYPE_NONE;  // flag must have MOVETYPE_NONE here, otherwise it will drop through the floor...
+                       setorigin(self, FLAG_CARRY_POS);
+                       setattachment(self, other, "");
+                       self.damageforcescale = 0;
+                       self.takedamage = DAMAGE_NO;
+                       WaypointSprite_AttachCarrier("flagcarrier", other, RADARICON_FLAGCARRIER, '1 1 0');
+               }
+       }
+}
+
+/*QUAKED spawnfunc_info_player_team1 (1 0 0) (-16 -16 -24) (16 16 24)
+CTF Starting point for a player
+in team one (Red).
+
+Keys:
+"angle"
+ viewing angle when spawning
+*/
+void spawnfunc_info_player_team1()
+{
+       if(g_assault)
+       {
+               remove(self);
+               return;
+       }
+       self.team = COLOR_TEAM1; // red
+       spawnfunc_info_player_deathmatch();
+}
+//self.team = 4;self.classname = "info_player_start";spawnfunc_info_player_start();}
+
+/*QUAKED spawnfunc_info_player_team2 (1 0 0) (-16 -16 -24) (16 16 24)
+CTF Starting point for a player in
+team two (Blue).
+
+Keys:
+"angle"
+ viewing angle when spawning
+*/
+void spawnfunc_info_player_team2()
+{
+       if(g_assault)
+       {
+               remove(self);
+               return;
+       }
+       self.team = COLOR_TEAM2; // blue
+       spawnfunc_info_player_deathmatch();
+}
+//self.team = 13;self.classname = "info_player_start";spawnfunc_info_player_start();}
+
+/*QUAKED spawnfunc_info_player_team3 (1 0 0) (-16 -16 -24) (16 16 24)
+CTF Starting point for a player in
+team three (Yellow).
+
+Keys:
+"angle"
+ viewing angle when spawning
+*/
+void spawnfunc_info_player_team3()
+{
+       if(g_assault)
+       {
+               remove(self);
+               return;
+       }
+       self.team = COLOR_TEAM3; // yellow
+       spawnfunc_info_player_deathmatch();
+}
+
+
+/*QUAKED spawnfunc_info_player_team4 (1 0 0) (-16 -16 -24) (16 16 24)
+CTF Starting point for a player in
+team four (Magenta).
+
+Keys:
+"angle"
+ viewing angle when spawning
+*/
+void spawnfunc_info_player_team4()
+{
+       if(g_assault)
+       {
+               remove(self);
+               return;
+       }
+       self.team = COLOR_TEAM4; // purple
+       spawnfunc_info_player_deathmatch();
+}
+
+void item_flag_reset()
+{
+       DropFlag(self, world, world);
+       if(self.waypointsprite_attachedforcarrier)
+               WaypointSprite_DetachCarrier(self);
+       ReturnFlag(self);
+}
+
+void item_flag_postspawn()
+{ // Check CTF Item Flag Post Spawn
+
+       // Flag Glow Trail Support
+       if(autocvar_g_ctf_flag_glowtrails)
+       { // Provide Flag Glow Trail
+               if(self.team == COLOR_TEAM1)
+                       // Red
+                       self.glow_color = 251;
+               else
+               if(self.team == COLOR_TEAM2)
+                       // Blue
+                       self.glow_color = 210;
+
+               self.glow_size = 25;
+               self.glow_trail = 1;
+       }
+}
+
+/*QUAKED spawnfunc_item_flag_team1 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
+CTF flag for team one (Red).
+Multiple are allowed.
+
+Keys:
+"angle"
+ Angle the flag will point
+(minus 90 degrees)
+"model"
+ model to use, note this needs red and blue as skins 0 and 1
+ (default models/ctf/flag.md3)
+"noise"
+ sound played when flag is picked up
+ (default ctf/take.wav)
+"noise1"
+ sound played when flag is returned by a teammate
+ (default ctf/return.wav)
+"noise2"
+ sound played when flag is captured
+ (default ctf/redcapture.wav)
+"noise3"
+ sound played when flag is lost in the field and respawns itself
+ (default ctf/respawn.wav)
+*/
+
+void spawnfunc_item_flag_team2();
+void spawnfunc_item_flag_team1()
+{
+       if (!g_ctf)
+       {
+               remove(self);
+               return;
+       }
+
+       if (g_ctf_reverse)
+       {
+               float old_g_ctf_reverse = g_ctf_reverse;
+               g_ctf_reverse = 0; // avoid an endless loop
+               spawnfunc_item_flag_team2();
+               g_ctf_reverse = old_g_ctf_reverse;
+               return;
+       }
+
+       // link flag into ctf_worldflaglist
+       self.ctf_worldflagnext = ctf_worldflaglist;
+       ctf_worldflaglist = self;
+
+       self.classname = "item_flag_team";
+       self.team = COLOR_TEAM1; // color 4 team (red)
+       self.items = IT_KEY2; // gold key (redish enough)
+       self.netname = "^1RED^7 flag";
+       self.target = "###item###";
+       self.skin = autocvar_g_ctf_flag_red_skin;
+       if(self.spawnflags & 1)
+               self.noalign = 1;
+       if (!self.model)
+               self.model = autocvar_g_ctf_flag_red_model;
+       if (!self.noise)
+               self.noise = "ctf/red_taken.wav";
+       if (!self.noise1)
+               self.noise1 = "ctf/red_returned.wav";
+       if (!self.noise2)
+               self.noise2 = "ctf/red_capture.wav"; // blue team scores by capturing the red flag
+       if (!self.noise3)
+               self.noise3 = "ctf/flag_respawn.wav";
+       if (!self.noise4)
+               self.noise4 = "ctf/red_dropped.wav";
+       precache_model (self.model);
+       setmodel (self, self.model); // precision set below
+       precache_sound (self.noise);
+       precache_sound (self.noise1);
+       precache_sound (self.noise2);
+       precache_sound (self.noise3);
+       precache_sound (self.noise4);
+       //setsize(self, '-16 -16 -37', '16 16 37');
+       setsize(self, FLAG_MIN, FLAG_MAX);
+       setorigin(self, self.origin + '0 0 37');
+       self.nextthink = time + 0.2; // start after doors etc
+       self.think = place_flag;
+
+       if(!self.scale)
+               self.scale = 0.6;
+       //if(!self.glow_size)
+       //      self.glow_size = 50;
+
+       self.effects = self.effects | EF_LOWPRECISION;
+       if(autocvar_g_ctf_fullbrightflags)
+               self.effects |= EF_FULLBRIGHT;
+       if(autocvar_g_ctf_dynamiclights)
+               self.effects |= EF_RED;
+
+       // From Spidflisk
+       item_flag_postspawn();
+
+       precache_model("models/ctf/shield.md3");
+       precache_model("models/ctf/shockwavetransring.md3");
+
+       self.reset = item_flag_reset;
+}
+
+/*QUAKED spawnfunc_item_flag_team2 (0 0.5 0.8) (-48 -48 -24) (48 48 64)
+CTF flag for team two (Blue).
+Multiple are allowed.
+
+Keys:
+"angle"
+ Angle the flag will point
+(minus 90 degrees)
+"model"
+ model to use, note this needs red and blue as skins 0 and 1
+ (default models/ctf/flag.md3)
+"noise"
+ sound played when flag is picked up
+ (default ctf/take.wav)
+"noise1"
+ sound played when flag is returned by a teammate
+ (default ctf/return.wav)
+"noise2"
+ sound played when flag is captured
+ (default ctf/bluecapture.wav)
+"noise3"
+ sound played when flag is lost in the field and respawns itself
+ (default ctf/respawn.wav)
+*/
+
+void spawnfunc_item_flag_team2()
+{
+       if (!g_ctf)
+       {
+               remove(self);
+               return;
+       }
+
+       if (g_ctf_reverse)
+       {
+               float old_g_ctf_reverse = g_ctf_reverse;
+               g_ctf_reverse = 0; // avoid an endless loop
+               spawnfunc_item_flag_team1();
+               g_ctf_reverse = old_g_ctf_reverse;
+               return;
+       }
+
+       // link flag into ctf_worldflaglist
+       self.ctf_worldflagnext = ctf_worldflaglist;
+       ctf_worldflaglist = self;
+
+       self.classname = "item_flag_team";
+       self.team = COLOR_TEAM2; // color 13 team (blue)
+       self.items = IT_KEY1; // silver key (bluish enough)
+       self.netname = "^4BLUE^7 flag";
+       self.target = "###item###";
+       self.skin = autocvar_g_ctf_flag_blue_skin;
+       if(self.spawnflags & 1)
+               self.noalign = 1;
+       if (!self.model)
+               self.model = autocvar_g_ctf_flag_blue_model;
+       if (!self.noise)
+               self.noise = "ctf/blue_taken.wav";
+       if (!self.noise1)
+               self.noise1 = "ctf/blue_returned.wav";
+       if (!self.noise2)
+               self.noise2 = "ctf/blue_capture.wav"; // blue team scores by capturing the red flag
+       if (!self.noise3)
+               self.noise3 = "ctf/flag_respawn.wav";
+       if (!self.noise4)
+               self.noise4 = "ctf/blue_dropped.wav";
+       precache_model (self.model);
+       setmodel (self, self.model); // precision set below
+       precache_sound (self.noise);
+       precache_sound (self.noise1);
+       precache_sound (self.noise2);
+       precache_sound (self.noise3);
+       precache_sound (self.noise4);
+       //setsize(self, '-16 -16 -37', '16 16 37');
+       setsize(self, FLAG_MIN, FLAG_MAX);
+       setorigin(self, self.origin + '0 0 37');
+       self.nextthink = time + 0.2; // start after doors etc
+       self.think = place_flag;
+
+       if(!self.scale)
+               self.scale = 0.6;
+       //if(!self.glow_size)
+       //      self.glow_size = 50;
+
+       self.effects = self.effects | EF_LOWPRECISION;
+       if(autocvar_g_ctf_fullbrightflags)
+               self.effects |= EF_FULLBRIGHT;
+       if(autocvar_g_ctf_dynamiclights)
+               self.effects |= EF_BLUE;
+
+       // From Spidflisk
+       item_flag_postspawn();
+
+       precache_model("models/ctf/shield.md3");
+       precache_model("models/ctf/shockwavetransring.md3");
+
+       self.reset = item_flag_reset;
+}
+
+
+/*QUAKED spawnfunc_ctf_team (0 .5 .8) (-16 -16 -24) (16 16 32)
+Team declaration for CTF gameplay, this allows you to decide what team
+names and control point models are used in your map.
+
+Note: If you use spawnfunc_ctf_team entities you must define at least 2!  However, unlike
+domination, you don't need to make a blank one too.
+
+Keys:
+"netname"
+ Name of the team (for example Red, Blue, Green, Yellow, Life, Death, Offense, Defense, etc)
+"cnt"
+ Scoreboard color of the team (for example 4 is red and 13 is blue)
+
+*/
+
+void spawnfunc_ctf_team()
+{
+       if (!g_ctf)
+       {
+               remove(self);
+               return;
+       }
+       self.classname = "ctf_team";
+       self.team = self.cnt + 1;
+}
+
+// code from here on is just to support maps that don't have control point and team entities
+void ctf_spawnteam (string teamname, float teamcolor)
+{
+       entity oldself;
+       oldself = self;
+       self = spawn();
+       self.classname = "ctf_team";
+       self.netname = teamname;
+       self.cnt = teamcolor;
+
+       spawnfunc_ctf_team();
+
+       self = oldself;
+}
+
+// spawn some default teams if the map is not set up for ctf
+void ctf_spawnteams()
+{
+       float numteams;
+
+       numteams = 2;//cvar("g_ctf_default_teams");
+
+       ctf_spawnteam("Red", COLOR_TEAM1 - 1);
+       ctf_spawnteam("Blue", COLOR_TEAM2 - 1);
+}
+
+void ctf_delayedinit()
+{
+       // if no teams are found, spawn defaults
+       if (find(world, classname, "ctf_team") == world)
+               ctf_spawnteams();
+
+       ScoreRules_ctf();
+}
+
+void ctf_init()
+{
+       InitializeEntity(world, ctf_delayedinit, INITPRIO_GAMETYPE);
+       flagcaptimerecord = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time")));
+
+       captureshield_min_negscore = autocvar_g_ctf_shield_min_negscore;
+       captureshield_max_ratio = autocvar_g_ctf_shield_max_ratio;
+       captureshield_force = autocvar_g_ctf_shield_force;
+}
+
+void ctf_setstatus2(entity flag, float shift)
+{
+       if (flag.cnt == FLAG_CARRY)
+               if (flag.owner == self)
+                       self.items |= shift * 3;
+               else
+                       self.items |= shift * 1;
+       else if (flag.cnt == FLAG_DROPPED)
+               self.items |= shift * 2;
+       else
+       {
+               // no status bits
+       }
+}
+
+void ctf_setstatus()
+{
+       self.items &~= IT_RED_FLAG_TAKEN;
+       self.items &~= IT_RED_FLAG_LOST;
+       self.items &~= IT_BLUE_FLAG_TAKEN;
+       self.items &~= IT_BLUE_FLAG_LOST;
+       self.items &~= IT_CTF_SHIELDED;
+
+       entity flag;
+       float redflags, blueflags;
+
+       if(self.ctf_captureshielded)
+               self.items |= IT_CTF_SHIELDED;
+
+       redflags = 0;
+       blueflags = 0;
+
+       for (flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) if(flag.cnt != FLAG_BASE)
+       {
+               if(flag.items & IT_KEY2) // blue
+                       ++redflags;
+               else if(flag.items & IT_KEY1) // red
+                       ++blueflags;
+       }
+
+       // blinking magic: if there is more than one flag, show one of these in a clever way
+       if(redflags)
+               redflags = mod(floor(time * redflags * 0.75), redflags);
+       if(blueflags)
+               blueflags = mod(floor(time * blueflags * 0.75), blueflags);
+
+       for (flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) if(flag.cnt != FLAG_BASE)
+       {
+               if(flag.items & IT_KEY2) // blue
+               {
+                       if(--redflags == -1) // happens exactly once (redflags is in 0..count-1, and will --'ed count times)
+                               ctf_setstatus2(flag, IT_RED_FLAG_TAKEN);
+               }
+               else if(flag.items & IT_KEY1) // red
+               {
+                       if(--blueflags == -1) // happens exactly once
+                               ctf_setstatus2(flag, IT_BLUE_FLAG_TAKEN);
+               }
+       }
+}
+/*
+entity ctf_team_has_commander(float cteam)
+{
+       entity pl;
+       if(cteam != COLOR_TEAM1 || cteam != COLOR_TEAM2)
+               return world;
+
+       FOR_EACH_REALPLAYER(pl) {
+               if(pl.team == cteam && pl.iscommander) {
+                       return pl;
+               }
+       }
+       return world;
+}
+
+void ctf_setstate(entity e, float st)
+{
+       e.ctf_state = st;
+       ++e.version;
+}
+
+void ctf_new_commander(float cteam)
+{
+       entity pl, plmax;
+
+       plmax = world;
+       FOR_EACH_REALPLAYER(pl) {
+               if(pl.team == cteam) {
+                       if(pl.iscommander) { // don't reassign if alreay there
+                               return;
+                       }
+                       if(plmax == world || plmax.frags < pl.frags) <<<<<<<<<<<<<<<<< BROKEN in new scoring system
+                               plmax = pl;
+               }
+       }
+       if(plmax == world) {
+               bprint(strcat(ColoredTeamName(cteam), " Team has no Commander!\n"));
+               return;
+       }
+
+       plmax.iscommander = TRUE;
+       ctf_setstate(plmax, 3);
+       sprint(plmax, "^3You're the commander now!\n");
+       centerprint(plmax, "^3You're the commander now!\n");
+}
+
+void ctf_clientconnect()
+{
+       self.iscommander = FALSE;
+
+       if(!self.team || self.classname != "player") {
+               ctf_setstate(self, -1);
+       } else
+               ctf_setstate(self, 0);
+
+       self.team_saved = self.team;
+
+       if(self.team != 0 && self.classname == "player" && !ctf_team_has_commander(self.team)) {
+               ctf_new_commander(self.team);
+       }
+}
+
+void ctf_playerchanged()
+{
+       if(!self.team || self.classname != "player") {
+               ctf_setstate(self, -1);
+       } else if(self.ctf_state < 0 && self.classname == "player") {
+               ctf_setstate(self, 0);
+       }
+
+       if(self.iscommander &&
+          (self.classname != "player" || self.team != self.team_saved)
+               )
+       {
+               self.iscommander = FALSE;
+               if(self.classname == "player")
+                       ctf_setstate(self, 0);
+               else
+                       ctf_setstate(self, -1);
+               ctf_new_commander(self.team_saved);
+       }
+
+       self.team_saved = self.team;
+
+       ctf_new_commander(self.team);
+}
+
+void ctf_clientdisconnect()
+{
+       if(self.iscommander)
+       {
+               ctf_new_commander(self.team);
+       }
+}
+
+entity GetPlayer(string);
+float ctf_clientcommand()
+{
+       entity e;
+       if(argv(0) == "order") {
+               if(!g_ctf) {
+                       sprint(self, "This command is not supported in this gamemode.\n");
+                       return TRUE;
+               }
+               if(!self.iscommander) {
+                       sprint(self, "^1You are not the commander!\n");
+                       return TRUE;
+               }
+               if(argv(2) == "") {
+                       sprint(self, "Usage: order #player status   - (playernumber as in status)\n");
+                       return TRUE;
+               }
+               e = GetPlayer(argv(1));
+               if(e == world) {
+                       sprint(self, "Invalid player.\nUsage: order #player status   - (playernumber as in status)\n");
+                       return TRUE;
+               }
+               if(e.team != self.team) {
+                       sprint(self, "^3You can only give orders to your own team!\n");
+                       return TRUE;
+               }
+               if(argv(2) == "attack") {
+                       sprint(self, strcat("Ordering ", e.netname, " to attack!\n"));
+                       sprint(e, "^1Attack!\n");
+                       centerprint(e, "^7You've been ordered to^9\n^1Attack!\n");
+                       ctf_setstate(e, 1);
+               } else if(argv(2) == "defend") {
+                       sprint(self, strcat("Ordering ", e.netname, " to defend!\n"));
+                       sprint(e, "^Defend!\n");
+                       centerprint(e, "^7You've been ordered to^9\n^2Defend!\n");
+                       ctf_setstate(e, 2);
+               } else {
+                       sprint(self, "^7Invalid command, use ^3attack^7, or ^3defend^7.\n");
+               }
+               return TRUE;
+       }
+       return FALSE;
+}
+*/
index 3e160d97b543e89e84fe01a53084dced6aa4139b..b450f761e31af442b2c084763e97fd44ae5faf84 100644 (file)
@@ -259,6 +259,7 @@ void() walkmonster_start_go =
 // spread think times so they don't all happen at same time
        self.nextthink = self.nextthink + random()*0.5 + 0.1;
        self.iscreature = TRUE;
+       self.teleportable = TELEPORT_NORMAL;
        self.damagedbycontents = TRUE;
 
        force_retouch = 2; // mainly to detect teleports
@@ -353,6 +354,7 @@ void() flymonster_start_go =
                }
        }
        self.iscreature = TRUE;
+       self.teleportable = TELEPORT_NORMAL;
        self.damagedbycontents = TRUE;
 
        force_retouch = 2; // mainly to detect teleports
@@ -442,6 +444,7 @@ void() swimmonster_start_go =
                }
        }
        self.iscreature = TRUE;
+       self.teleportable = TELEPORT_NORMAL;
        self.damagedbycontents = TRUE;
 
        force_retouch = 2; // mainly to detect teleports
index a068a33a54e236017194c3ec5ac05e5caa0b9df1..d3f4b55f4a49acf42693df6e04df24942a1b99f3 100644 (file)
@@ -28,6 +28,9 @@ float nb_teams;
 
 .float teamtime;
 
+.float nb_dropperid;
+.float nb_droptime;
+
 void nb_delayedinit();
 void nb_init() // Called early (worldspawn stage)
 {
@@ -155,7 +158,7 @@ void GiveBall (entity plyr, entity ball)
        ball.owner = ball.pusher = plyr; //"owner" is set to the player carrying, "pusher" to the last player who touched it
        ball.team = plyr.team;
        plyr.ballcarried = ball;
-       ball.dropperid = plyr.playerid;
+       ball.nb_dropperid = plyr.playerid;
 
        plyr.effects |= g_nexball_basketball_effects_default;
        ball.effects &~= g_nexball_basketball_effects_default;
@@ -188,7 +191,7 @@ void DropBall (entity ball, vector org, vector vel)
        ball.flags &~= FL_ONGROUND;
        ball.scale = ball_scale;
        ball.velocity = vel;
-       ball.ctf_droptime = time;
+       ball.nb_droptime = time;
        ball.touch = basketball_touch;
        ball.think = ResetBall;
        ball.nextthink = min(time + g_nexball_delay_idle, ball.teamtime);
@@ -302,7 +305,7 @@ void basketball_touch (void)
                football_touch();
                return;
        }
-       if (!self.cnt && other.classname == "player" && (other.playerid != self.dropperid || time > self.ctf_droptime + autocvar_g_nexball_delay_collect)) {
+       if (!self.cnt && other.classname == "player" && (other.playerid != self.nb_dropperid || time > self.nb_droptime + autocvar_g_nexball_delay_collect)) {
                if (other.health <= 0)
                        return;
                LogNB("caught", other);
index c75a4387339f58f429cecf8b6d31acb2292652d6..16a7193c13710903da8cd9ed2db5143802504508 100644 (file)
@@ -177,10 +177,9 @@ 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_ctf_damageforcescale;
-float autocvar_g_balance_ctf_delay_collect;
 float autocvar_g_balance_curse_empathy_minhealth;
 float autocvar_g_balance_curse_empathy_takedamage;
 float autocvar_g_balance_curse_slow_atkrate;
@@ -686,8 +685,8 @@ 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_force;
 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;
@@ -762,23 +761,76 @@ float autocvar_g_chat_flood_spl_team;
 float autocvar_g_chat_flood_spl_tell;
 float autocvar_g_chat_nospectators;
 float autocvar_g_chat_teamcolors;
+float autocvar_g_ctf_allow_vehicle_carry;
+float autocvar_g_ctf_allow_vehicle_touch;
+float autocvar_g_ctf_throw;
+float autocvar_g_ctf_throw_angle_max;
+float autocvar_g_ctf_throw_angle_min;
+float autocvar_g_ctf_throw_punish_count;
+float autocvar_g_ctf_throw_punish_delay;
+float autocvar_g_ctf_throw_punish_time;
+float autocvar_g_ctf_throw_strengthmultiplier;
+float autocvar_g_ctf_throw_velocity_forward;
+float autocvar_g_ctf_throw_velocity_up;
+float autocvar_g_ctf_drop_velocity_up;
+float autocvar_g_ctf_drop_velocity_side;
+float autocvar_g_ctf_portalteleport;
+float autocvar_g_ctf_pass;
+float autocvar_g_ctf_pass_arc;
+float autocvar_g_ctf_pass_arc_max;
+float autocvar_g_ctf_pass_directional_max;
+float autocvar_g_ctf_pass_directional_min;
+float autocvar_g_ctf_pass_radius;
+float autocvar_g_ctf_pass_wait;
+float autocvar_g_ctf_pass_request;
+float autocvar_g_ctf_pass_turnrate;
+float autocvar_g_ctf_pass_timelimit;
+float autocvar_g_ctf_pass_velocity;
 float autocvar_g_ctf_captimerecord_always;
 float autocvar_g_ctf_dynamiclights;
 string autocvar_g_ctf_flag_blue_model;
 float autocvar_g_ctf_flag_blue_skin;
-float autocvar_g_ctf_flag_capture_effects;
+float autocvar_g_ctf_flag_collect_delay;
+float autocvar_g_ctf_flag_damageforcescale;
+float autocvar_g_ctf_flag_dropped_waypoint;
+float autocvar_g_ctf_flag_dropped_floatinwater;
 float autocvar_g_ctf_flag_glowtrails;
-float autocvar_g_ctf_flag_pickup_effects;
+float autocvar_g_ctf_flag_health;
+float autocvar_g_ctf_flag_pickup_verbosename;
 string autocvar_g_ctf_flag_red_model;
 float autocvar_g_ctf_flag_red_skin;
-float autocvar_g_ctf_flag_returntime;
-float autocvar_g_ctf_flagcarrier_selfdamage;
-float autocvar_g_ctf_flagcarrier_selfforce;
+float autocvar_g_ctf_flag_return_time;
+float autocvar_g_ctf_flag_return_when_unreachable;
+float autocvar_g_ctf_flag_return_damage;
+float autocvar_g_ctf_flag_return_dropped;
+float autocvar_g_ctf_flagcarrier_auto_helpme_damage;
+float autocvar_g_ctf_flagcarrier_auto_helpme_time;
+float autocvar_g_ctf_flagcarrier_selfdamagefactor;
+float autocvar_g_ctf_flagcarrier_selfforcefactor;
+float autocvar_g_ctf_flagcarrier_damagefactor;
+float autocvar_g_ctf_flagcarrier_forcefactor;
+//float autocvar_g_ctf_flagcarrier_waypointforenemy_spotting;
 float autocvar_g_ctf_fullbrightflags;
 float autocvar_g_ctf_ignore_frags;
+float autocvar_g_ctf_score_capture;
+float autocvar_g_ctf_score_capture_assist;
+float autocvar_g_ctf_score_kill;
+float autocvar_g_ctf_score_penalty_drop;
+//float autocvar_g_ctf_score_penalty_suicidedrop;
+float autocvar_g_ctf_score_penalty_returned;
+float autocvar_g_ctf_score_pickup_base;
+float autocvar_g_ctf_score_pickup_dropped_early;
+float autocvar_g_ctf_score_pickup_dropped_late;
+float autocvar_g_ctf_score_return;
 float autocvar_g_ctf_shield_force;
 float autocvar_g_ctf_shield_max_ratio;
 float autocvar_g_ctf_shield_min_negscore;
+float autocvar_g_ctf_stalemate;
+float autocvar_g_ctf_stalemate_endcondition;
+float autocvar_g_ctf_stalemate_time;
+float autocvar_g_ctf_reverse;
+float autocvar_g_ctf_dropped_capture_delay;
+float autocvar_g_ctf_dropped_capture_radius;
 float autocvar_g_cts_finish_kill_delay;
 float autocvar_g_cts_selfdamage;
 float autocvar_g_debug_bot_commands;
index b06d578d1813b096ea7a49e2475e3ce59dfea55c..3e1cae313ff6ecbc06c3629d699c911bb16abb42 100644 (file)
@@ -366,6 +366,7 @@ void bot_clientdisconnect()
 {
        if (clienttype(self) != CLIENTTYPE_BOT)
                return;
+       bot_clearqueue(self);
        if(self.cleanname)
                strunzone(self.cleanname);
        if(self.netname_freeme)
index e03cbac66a5937baa08cea8a4cd3575a6675c057..1b9178b1774b7632c2359463a9fd777833771326 100644 (file)
@@ -1,9 +1,6 @@
 #include "havocbot.qh"
-#include "role_ctf.qc"
 #include "role_onslaught.qc"
 #include "role_keyhunt.qc"
-#include "role_freezetag.qc"
-#include "role_keepaway.qc"
 #include "role_assault.qc"
 #include "roles.qc"
 
index be28962912aaf5e6dc662ead1683b8d43fb06e0f..de94691822e902631b6bbe732162d5bb22270d92 100644 (file)
@@ -19,6 +19,7 @@
 .float havocbot_personal_waypoint_failcounter;
 .float havocbot_chooseenemy_finished;
 .float havocbot_stickenemy;
+.float havocbot_role_timeout;
 
 .entity ignoregoal;
 .entity bot_lastseengoal;
@@ -47,6 +48,10 @@ float havocbot_moveto_refresh_route();
 vector havocbot_dodge();
 
 .void() havocbot_role;
+.void() havocbot_previous_role;
+
+void(float ratingscale, vector org, float sradius) havocbot_goalrating_items;
+void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
 
 /*
  * Imports
diff --git a/qcsrc/server/bot/havocbot/role_ctf.qc b/qcsrc/server/bot/havocbot/role_ctf.qc
deleted file mode 100644 (file)
index 178ee0d..0000000
+++ /dev/null
@@ -1,684 +0,0 @@
-#define HAVOCBOT_CTF_ROLE_NONE                 0
-#define HAVOCBOT_CTF_ROLE_DEFENSE      2
-#define HAVOCBOT_CTF_ROLE_MIDDLE       4
-#define HAVOCBOT_CTF_ROLE_OFFENSE      8
-#define HAVOCBOT_CTF_ROLE_CARRIER      16
-#define HAVOCBOT_CTF_ROLE_RETRIEVER    32
-#define HAVOCBOT_CTF_ROLE_ESCORT       64
-
-.void() havocbot_role;
-.void() havocbot_previous_role;
-
-void() havocbot_role_ctf_middle;
-void() havocbot_role_ctf_defense;
-void() havocbot_role_ctf_offense;
-void() havocbot_role_ctf_carrier;
-void() havocbot_role_ctf_retriever;
-void() havocbot_role_ctf_escort;
-
-void(entity bot) havocbot_ctf_reset_role;
-void(float ratingscale, vector org, float sradius) havocbot_goalrating_items;
-void(float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers;
-
-.float havocbot_cantfindflag;
-.float havocbot_role_timeout;
-.entity ctf_worldflagnext;
-.entity basewaypoint;
-
-entity ctf_worldflaglist;
-vector havocbot_ctf_middlepoint;
-float havocbot_ctf_middlepoint_radius;
-
-entity havocbot_ctf_find_flag(entity bot)
-{
-       entity f;
-       f = ctf_worldflaglist;
-       while (f)
-       {
-               if (bot.team == f.team)
-                       return f;
-               f = f.ctf_worldflagnext;
-       }
-       return world;
-}
-
-entity havocbot_ctf_find_enemy_flag(entity bot)
-{
-       entity f;
-       f = ctf_worldflaglist;
-       while (f)
-       {
-               if (bot.team != f.team)
-                       return f;
-               f = f.ctf_worldflagnext;
-       }
-       return world;
-}
-
-float havocbot_ctf_teamcount(entity bot, vector org, float radius)
-{
-       if not(teamplay)
-               return 0;
-
-       float c = 0;
-       entity head;
-
-       FOR_EACH_PLAYER(head)
-       {
-               if(head.team!=bot.team || head.deadflag != DEAD_NO || head == bot)
-                       continue;
-
-               if(vlen(head.origin - org) < radius)
-                       ++c;
-       }
-
-       return c;
-}
-
-void havocbot_goalrating_ctf_ourflag(float ratingscale)
-{
-       entity head;
-       head = ctf_worldflaglist;
-       while (head)
-       {
-               if (self.team == head.team)
-                       break;
-               head = head.ctf_worldflagnext;
-       }
-       if (head)
-               navigation_routerating(head, ratingscale, 10000);
-}
-
-void havocbot_goalrating_ctf_ourbase(float ratingscale)
-{
-       entity head;
-       head = ctf_worldflaglist;
-       while (head)
-       {
-               if (self.team == head.team)
-                       break;
-               head = head.ctf_worldflagnext;
-       }
-       if not(head)
-               return;
-
-       navigation_routerating(head.basewaypoint, ratingscale, 10000);
-}
-
-void havocbot_goalrating_ctf_enemyflag(float ratingscale)
-{
-       entity head;
-       head = ctf_worldflaglist;
-       while (head)
-       {
-               if (self.team != head.team)
-                       break;
-               head = head.ctf_worldflagnext;
-       }
-       if (head)
-               navigation_routerating(head, ratingscale, 10000);
-}
-
-void havocbot_goalrating_ctf_enemybase(float ratingscale)
-{
-       if not(bot_waypoints_for_items)
-       {
-               havocbot_goalrating_ctf_enemyflag(ratingscale);
-               return;
-       }
-
-       entity head;
-
-       head = havocbot_ctf_find_enemy_flag(self);
-
-       if not(head)
-               return;
-
-       navigation_routerating(head.basewaypoint, ratingscale, 10000);
-}
-
-void havocbot_goalrating_ctf_ourstolenflag(float ratingscale)
-{
-       entity mf;
-
-       mf = havocbot_ctf_find_flag(self);
-
-       if(mf.cnt == FLAG_BASE)
-               return;
-
-       if(mf.tag_entity)
-               navigation_routerating(mf.tag_entity, ratingscale, 10000);
-}
-
-void havocbot_goalrating_ctf_droppedflags(float ratingscale, vector org, float radius)
-{
-       entity head;
-       head = ctf_worldflaglist;
-       while (head)
-       {
-               // flag is out in the field
-               if(head.cnt != FLAG_BASE)
-               if(head.tag_entity==world)      // dropped
-               {
-                       if(radius)
-                       {
-                               if(vlen(org-head.origin)<radius)
-                                       navigation_routerating(head, ratingscale, 10000);
-                       }
-                       else
-                               navigation_routerating(head, ratingscale, 10000);
-               }
-
-               head = head.ctf_worldflagnext;
-       }
-}
-
-void havocbot_goalrating_ctf_carrieritems(float ratingscale, vector org, float sradius)
-{
-       entity head;
-       float t;
-       head = findchainfloat(bot_pickup, TRUE);
-       while (head)
-       {
-               // gather health and armor only
-               if (head.solid)
-               if (head.health || head.armorvalue)
-               if (vlen(head.origin - org) < sradius)
-               {
-                       // get the value of the item
-                       t = head.bot_pickupevalfunc(self, head) * 0.0001;
-                       if (t > 0)
-                               navigation_routerating(head, t * ratingscale, 500);
-               }
-               head = head.chain;
-       }
-}
-
-void havocbot_role_ctf_setrole(entity bot, float role)
-{
-       dprint(strcat(bot.netname," switched to "));
-       switch(role)
-       {
-               case HAVOCBOT_CTF_ROLE_CARRIER:
-                       dprint("carrier");
-                       bot.havocbot_role = havocbot_role_ctf_carrier;
-                       bot.havocbot_role_timeout = 0;
-                       bot.havocbot_cantfindflag = time + 10;
-                       bot.bot_strategytime = 0;
-                       break;
-               case HAVOCBOT_CTF_ROLE_DEFENSE:
-                       dprint("defense");
-                       bot.havocbot_role = havocbot_role_ctf_defense;
-                       bot.havocbot_role_timeout = 0;
-                       break;
-               case HAVOCBOT_CTF_ROLE_MIDDLE:
-                       dprint("middle");
-                       bot.havocbot_role = havocbot_role_ctf_middle;
-                       bot.havocbot_role_timeout = 0;
-                       break;
-               case HAVOCBOT_CTF_ROLE_OFFENSE:
-                       dprint("offense");
-                       bot.havocbot_role = havocbot_role_ctf_offense;
-                       bot.havocbot_role_timeout = 0;
-                       break;
-               case HAVOCBOT_CTF_ROLE_RETRIEVER:
-                       dprint("retriever");
-                       bot.havocbot_previous_role = bot.havocbot_role;
-                       bot.havocbot_role = havocbot_role_ctf_retriever;
-                       bot.havocbot_role_timeout = time + 10;
-                       bot.bot_strategytime = 0;
-                       break;
-               case HAVOCBOT_CTF_ROLE_ESCORT:
-                       dprint("escort");
-                       bot.havocbot_previous_role = bot.havocbot_role;
-                       bot.havocbot_role = havocbot_role_ctf_escort;
-                       bot.havocbot_role_timeout = time + 30;
-                       bot.bot_strategytime = 0;
-                       break;
-       }
-       dprint("\n");
-}
-
-void havocbot_role_ctf_carrier()
-{
-       if(self.deadflag != DEAD_NO)
-       {
-               havocbot_ctf_reset_role(self);
-               return;
-       }
-
-       if (self.flagcarried == world)
-       {
-               havocbot_ctf_reset_role(self);
-               return;
-       }
-
-       if (self.bot_strategytime < time)
-       {
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-
-               navigation_goalrating_start();
-               havocbot_goalrating_ctf_ourbase(50000);
-
-               if(self.health<100)
-                       havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000);
-
-               navigation_goalrating_end();
-
-               if (self.navigation_hasgoals)
-                       self.havocbot_cantfindflag = time + 10;
-               else if (time > self.havocbot_cantfindflag)
-               {
-                       // Can't navigate to my own base, suicide!
-                       // TODO: drop it and wander around
-                       Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
-                       return;
-               }
-       }
-}
-
-void havocbot_role_ctf_escort()
-{
-       entity mf, ef;
-
-       if(self.deadflag != DEAD_NO)
-       {
-               havocbot_ctf_reset_role(self);
-               return;
-       }
-
-       if (self.flagcarried)
-       {
-               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
-               return;
-       }
-
-       // If enemy flag is back on the base switch to previous role
-       ef = havocbot_ctf_find_enemy_flag(self);
-       if(ef.cnt==FLAG_BASE)
-       {
-               self.havocbot_role = self.havocbot_previous_role;
-               self.havocbot_role_timeout = 0;
-               return;
-       }
-
-       // If the flag carrier reached the base switch to defense
-       mf = havocbot_ctf_find_flag(self);
-       if(mf.cnt!=FLAG_BASE)
-       if(vlen(ef.origin - mf.dropped_origin) < 300)
-       {
-               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_DEFENSE);
-               return;
-       }
-
-       // Set the role timeout if necessary
-       if (!self.havocbot_role_timeout)
-       {
-               self.havocbot_role_timeout = time + random() * 30 + 60;
-       }
-
-       // If nothing happened just switch to previous role
-       if (time > self.havocbot_role_timeout)
-       {
-               self.havocbot_role = self.havocbot_previous_role;
-               self.havocbot_role_timeout = 0;
-               return;
-       }
-
-       // Chase the flag carrier
-       if (self.bot_strategytime < time)
-       {
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-               navigation_goalrating_start();
-               havocbot_goalrating_ctf_enemyflag(30000);
-               havocbot_goalrating_ctf_ourstolenflag(40000);
-               havocbot_goalrating_items(10000, self.origin, 10000);
-               navigation_goalrating_end();
-       }
-}
-
-void havocbot_role_ctf_offense()
-{
-       entity mf, ef;
-       vector pos;
-
-       if(self.deadflag != DEAD_NO)
-       {
-               havocbot_ctf_reset_role(self);
-               return;
-       }
-
-       if (self.flagcarried)
-       {
-               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
-               return;
-       }
-
-       // Check flags
-       mf = havocbot_ctf_find_flag(self);
-       ef = havocbot_ctf_find_enemy_flag(self);
-
-       // Own flag stolen
-       if(mf.cnt!=FLAG_BASE)
-       {
-               if(mf.tag_entity)
-                       pos = mf.tag_entity.origin;
-               else
-                       pos = mf.origin;
-
-               // Try to get it if closer than the enemy base
-               if(vlen(self.origin-ef.dropped_origin)>vlen(self.origin-pos))
-               {
-                       havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
-                       return;
-               }
-       }
-
-       // Escort flag carrier
-       if(ef.cnt!=FLAG_BASE)
-       {
-               if(ef.tag_entity)
-                       pos = ef.tag_entity.origin;
-               else
-                       pos = ef.origin;
-
-               if(vlen(pos-mf.dropped_origin)>700)
-               {
-                       havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_ESCORT);
-                       return;
-               }
-       }
-
-       // About to fail, switch to middlefield
-       if(self.health<50)
-       {
-               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_MIDDLE);
-               return;
-       }
-
-       // Set the role timeout if necessary
-       if (!self.havocbot_role_timeout)
-               self.havocbot_role_timeout = time + 120;
-
-       if (time > self.havocbot_role_timeout)
-       {
-               havocbot_ctf_reset_role(self);
-               return;
-       }
-
-       if (self.bot_strategytime < time)
-       {
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-               navigation_goalrating_start();
-               havocbot_goalrating_ctf_ourstolenflag(50000);
-               havocbot_goalrating_ctf_enemybase(20000);
-               havocbot_goalrating_items(5000, self.origin, 1000);
-               havocbot_goalrating_items(1000, self.origin, 10000);
-               navigation_goalrating_end();
-       }
-}
-
-// Retriever (temporary role):
-void havocbot_role_ctf_retriever()
-{
-       entity mf;
-
-       if(self.deadflag != DEAD_NO)
-       {
-               havocbot_ctf_reset_role(self);
-               return;
-       }
-
-       if (self.flagcarried)
-       {
-               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
-               return;
-       }
-
-       // If flag is back on the base switch to previous role
-       mf = havocbot_ctf_find_flag(self);
-       if(mf.cnt==FLAG_BASE)
-       {
-               havocbot_ctf_reset_role(self);
-               return;
-       }
-
-       if (!self.havocbot_role_timeout)
-               self.havocbot_role_timeout = time + 20;
-
-       if (time > self.havocbot_role_timeout)
-       {
-               havocbot_ctf_reset_role(self);
-               return;
-       }
-
-       if (self.bot_strategytime < time)
-       {
-               float radius;
-               radius = 10000;
-
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-               navigation_goalrating_start();
-               havocbot_goalrating_ctf_ourstolenflag(50000);
-               havocbot_goalrating_ctf_droppedflags(40000, self.origin, radius);
-               havocbot_goalrating_ctf_enemybase(30000);
-               havocbot_goalrating_items(500, self.origin, radius);
-               navigation_goalrating_end();
-       }
-}
-
-void havocbot_role_ctf_middle()
-{
-       entity mf;
-
-       if(self.deadflag != DEAD_NO)
-       {
-               havocbot_ctf_reset_role(self);
-               return;
-       }
-
-       if (self.flagcarried)
-       {
-               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
-               return;
-       }
-
-       mf = havocbot_ctf_find_flag(self);
-       if(mf.cnt!=FLAG_BASE)
-       {
-               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
-               return;
-       }
-
-       if (!self.havocbot_role_timeout)
-               self.havocbot_role_timeout = time + 10;
-
-       if (time > self.havocbot_role_timeout)
-       {
-               havocbot_ctf_reset_role(self);
-               return;
-       }
-
-       if (self.bot_strategytime < time)
-       {
-               vector org;
-
-               org = havocbot_ctf_middlepoint;
-               org_z = self.origin_z;
-
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-               navigation_goalrating_start();
-               havocbot_goalrating_ctf_ourstolenflag(50000);
-               havocbot_goalrating_ctf_droppedflags(30000, self.origin, 10000);
-               havocbot_goalrating_enemyplayers(10000, org, havocbot_ctf_middlepoint_radius * 0.5);
-               havocbot_goalrating_items(5000, org, havocbot_ctf_middlepoint_radius * 0.5);
-               havocbot_goalrating_items(2500, self.origin, 10000);
-               havocbot_goalrating_ctf_enemybase(2500);
-               navigation_goalrating_end();
-       }
-}
-
-void havocbot_role_ctf_defense()
-{
-       entity mf;
-
-       if(self.deadflag != DEAD_NO)
-       {
-               havocbot_ctf_reset_role(self);
-               return;
-       }
-
-       if (self.flagcarried)
-       {
-               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
-               return;
-       }
-
-       // If own flag was captured
-       mf = havocbot_ctf_find_flag(self);
-       if(mf.cnt!=FLAG_BASE)
-       {
-               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
-               return;
-       }
-
-       if (!self.havocbot_role_timeout)
-               self.havocbot_role_timeout = time + 30;
-
-       if (time > self.havocbot_role_timeout)
-       {
-               havocbot_ctf_reset_role(self);
-               return;
-       }
-       if (self.bot_strategytime < time)
-       {
-               float radius;
-               vector org;
-
-               org = mf.dropped_origin;
-               radius = havocbot_ctf_middlepoint_radius;
-
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-               navigation_goalrating_start();
-
-               // if enemies are closer to our base, go there
-               entity head, closestplayer = world;
-               float distance, bestdistance = 10000;
-               FOR_EACH_PLAYER(head)
-               {
-                       if(head.deadflag!=DEAD_NO)
-                               continue;
-
-                       distance = vlen(org - head.origin);
-                       if(distance<bestdistance)
-                       {
-                               closestplayer = head;
-                               bestdistance = distance;
-                       }
-               }
-
-               if(closestplayer)
-               if(closestplayer.team!=self.team)
-               if(vlen(org - self.origin)>1000)
-               if(checkpvs(self.origin,closestplayer)||random()<0.5)
-                       havocbot_goalrating_ctf_ourbase(30000);
-
-               havocbot_goalrating_ctf_ourstolenflag(20000);
-               havocbot_goalrating_ctf_droppedflags(20000, org, radius);
-               havocbot_goalrating_enemyplayers(15000, org, radius);
-               havocbot_goalrating_items(10000, org, radius);
-               havocbot_goalrating_items(5000, self.origin, 10000);
-               navigation_goalrating_end();
-       }
-}
-
-void havocbot_calculate_middlepoint()
-{
-       entity f;
-       vector s = '0 0 0';
-       vector fo = '0 0 0';
-       float n = 0;
-
-       f = ctf_worldflaglist;
-       while (f)
-       {
-               fo = f.origin;
-               s = s + fo;
-               f = f.ctf_worldflagnext;
-       }
-       if(!n)
-               return;
-       havocbot_ctf_middlepoint = s * (1.0 / n);
-       havocbot_ctf_middlepoint_radius  = vlen(fo - havocbot_ctf_middlepoint);
-}
-
-void havocbot_ctf_reset_role(entity bot)
-{
-       float cdefense, cmiddle, coffense;
-       entity mf, ef, head;
-       float c;
-
-       if(bot.deadflag != DEAD_NO)
-               return;
-
-       if(vlen(havocbot_ctf_middlepoint)==0)
-               havocbot_calculate_middlepoint();
-
-       // Check ctf flags
-       if (bot.flagcarried)
-       {
-               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_CARRIER);
-               return;
-       }
-
-       mf = havocbot_ctf_find_flag(bot);
-       ef = havocbot_ctf_find_enemy_flag(bot);
-
-       // Retrieve stolen flag
-       if(mf.cnt!=FLAG_BASE)
-       {
-               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_RETRIEVER);
-               return;
-       }
-
-       // If enemy flag is taken go to the middle to intercept pursuers
-       if(ef.cnt!=FLAG_BASE)
-       {
-               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);
-               return;
-       }
-
-       // if there is only me on the team switch to offense
-       c = 0;
-       FOR_EACH_PLAYER(head)
-       if(head.team==bot.team)
-               ++c;
-
-       if(c==1)
-       {
-               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE);
-               return;
-       }
-
-       // Evaluate best position to take
-       // Count mates on middle position
-       cmiddle = havocbot_ctf_teamcount(bot, havocbot_ctf_middlepoint, havocbot_ctf_middlepoint_radius * 0.5);
-
-       // Count mates on defense position
-       cdefense = havocbot_ctf_teamcount(bot, mf.dropped_origin, havocbot_ctf_middlepoint_radius * 0.5);
-
-       // Count mates on offense position
-       coffense = havocbot_ctf_teamcount(bot, ef.dropped_origin, havocbot_ctf_middlepoint_radius);
-
-       if(cdefense<=coffense)
-               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_DEFENSE);
-       else if(coffense<=cmiddle)
-               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE);
-       else
-               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);
-}
-
-void havocbot_chooserole_ctf()
-{
-       havocbot_ctf_reset_role(self);
-}
diff --git a/qcsrc/server/bot/havocbot/role_freezetag.qc b/qcsrc/server/bot/havocbot/role_freezetag.qc
deleted file mode 100644 (file)
index 4e5669e..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-void() havocbot_role_ft_freeing;
-void() havocbot_role_ft_offense;
-
-void havocbot_goalrating_freeplayers(float ratingscale, vector org, float sradius)
-{
-       entity head;
-       float distance;
-
-       FOR_EACH_PLAYER(head)
-       {
-               if ((head != self) && (head.team == self.team))
-               {
-                       if (head.freezetag_frozen)
-                       {
-                               distance = vlen(head.origin - org);
-                               if (distance > sradius)
-                                       continue;
-                               navigation_routerating(head, ratingscale, 2000);
-                       }
-                       else
-                       {
-                               // If teamate is not frozen still seek them out as fight better
-                               // in a group.
-                               navigation_routerating(head, ratingscale/3, 2000);
-                       }
-               }
-       }
-}
-
-void havocbot_role_ft_offense()
-{
-       entity head;
-       float unfrozen;
-
-       if(self.deadflag != DEAD_NO)
-               return;
-
-       if (!self.havocbot_role_timeout)
-               self.havocbot_role_timeout = time + random() * 10 + 20;
-
-       // Count how many players on team are unfrozen.
-       unfrozen = 0;
-       FOR_EACH_PLAYER(head)
-       {
-               if ((head.team == self.team) && (!head.freezetag_frozen))
-                       unfrozen++;
-       }
-
-       // If only one left on team or if role has timed out then start trying to free players.
-       if (((unfrozen == 0) && (!self.freezetag_frozen)) || (time > self.havocbot_role_timeout))
-       {
-               dprint("changing role to freeing\n");
-               self.havocbot_role = havocbot_role_ft_freeing;
-               self.havocbot_role_timeout = 0;
-               return;
-       }
-
-       if (time > self.bot_strategytime)
-       {
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-
-               navigation_goalrating_start();
-               havocbot_goalrating_items(10000, self.origin, 10000);
-               havocbot_goalrating_enemyplayers(20000, self.origin, 10000);
-               havocbot_goalrating_freeplayers(9000, self.origin, 10000);
-               //havocbot_goalrating_waypoints(1, self.origin, 1000);
-               navigation_goalrating_end();
-       }
-}
-
-void havocbot_role_ft_freeing()
-{
-       if(self.deadflag != DEAD_NO)
-               return;
-
-       if (!self.havocbot_role_timeout)
-               self.havocbot_role_timeout = time + random() * 10 + 20;
-
-       if (time > self.havocbot_role_timeout)
-       {
-               dprint("changing role to offense\n");
-               self.havocbot_role = havocbot_role_ft_offense;
-               self.havocbot_role_timeout = 0;
-               return;
-       }
-
-       if (time > self.bot_strategytime)
-       {
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-
-               navigation_goalrating_start();
-               havocbot_goalrating_items(8000, self.origin, 10000);
-               havocbot_goalrating_enemyplayers(10000, self.origin, 10000);
-               havocbot_goalrating_freeplayers(20000, self.origin, 10000);
-               //havocbot_goalrating_waypoints(1, self.origin, 1000);
-               navigation_goalrating_end();
-       }
-}
-
-void havocbot_chooserole_ft()
-{
-       if(self.deadflag != DEAD_NO)
-               return;
-
-       if (random() < 0.5)
-               self.havocbot_role = havocbot_role_ft_freeing;
-       else
-               self.havocbot_role = havocbot_role_ft_offense;
-}
diff --git a/qcsrc/server/bot/havocbot/role_keepaway.qc b/qcsrc/server/bot/havocbot/role_keepaway.qc
deleted file mode 100644 (file)
index ede6208..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-void() havocbot_role_ka_carrier;
-void() havocbot_role_ka_collector;
-void() havocbot_chooserole_ka;
-
-entity ka_ball;
-
-// Keepaway
-// If you don't have the ball, get it; if you do, kill people.
-
-void havocbot_goalrating_ball(float ratingscale, vector org)
-{
-       float t;
-       entity ball_owner;
-       ball_owner = ka_ball.owner;
-
-       if (ball_owner == self)
-               return;
-
-       // If ball is carried by player then hunt them down.
-       if (ball_owner)
-       {
-               t = (self.health + self.armorvalue) / (ball_owner.health + ball_owner.armorvalue);
-               navigation_routerating(ball_owner, t * ratingscale, 2000);
-       }
-
-       // Ball has been dropped so collect.
-       navigation_routerating(ka_ball, ratingscale, 2000);
-}
-
-void havocbot_role_ka_carrier()
-{
-       if (self.deadflag != DEAD_NO)
-               return;
-
-       if (time > self.bot_strategytime)
-       {
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-
-               navigation_goalrating_start();
-               havocbot_goalrating_items(10000, self.origin, 10000);
-               havocbot_goalrating_enemyplayers(20000, self.origin, 10000);
-               //havocbot_goalrating_waypoints(1, self.origin, 1000);
-               navigation_goalrating_end();
-       }
-
-       if (!self.ballcarried)
-       {
-               self.havocbot_role = havocbot_role_ka_collector;
-               self.bot_strategytime = 0;
-       }
-}
-
-void havocbot_role_ka_collector()
-{
-       if (self.deadflag != DEAD_NO)
-               return;
-
-       if (time > self.bot_strategytime)
-       {
-               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
-
-               navigation_goalrating_start();
-               havocbot_goalrating_items(10000, self.origin, 10000);
-               havocbot_goalrating_enemyplayers(1000, self.origin, 10000);
-               havocbot_goalrating_ball(20000, self.origin);
-               navigation_goalrating_end();
-       }
-
-       if (self.ballcarried)
-       {
-               self.havocbot_role = havocbot_role_ka_carrier;
-               self.bot_strategytime = 0;
-       }
-}
-
-void havocbot_chooserole_ka()
-{
-       if (self.ballcarried)
-               self.havocbot_role = havocbot_role_ka_carrier;
-       else
-               self.havocbot_role = havocbot_role_ka_collector;
-}
index ac8ddd161b05c749a1e94bf79b29799bf2674c95..ad3bb2367418f611d13bb67725c70cd416d7831f 100644 (file)
@@ -293,8 +293,8 @@ void havocbot_chooserole()
 {
        dprint("choosing a role...\n");
        self.bot_strategytime = 0;
-       if (g_ctf)
-               havocbot_chooserole_ctf();
+       if (MUTATOR_CALLHOOK(HavocBot_ChooseRule))
+               return;
        else if (g_domination)
                havocbot_chooserole_dom();
        else if (g_keyhunt)
@@ -303,10 +303,6 @@ void havocbot_chooserole()
                havocbot_chooserole_race();
        else if (g_onslaught)
                havocbot_chooserole_ons();
-       else if (g_keepaway)
-               havocbot_chooserole_ka();
-       else if (g_freezetag)
-               havocbot_chooserole_ft();
        else if (g_assault)
                havocbot_chooserole_ast();
        else // assume anything else is deathmatch
index 5752e2f499c506e42686b8770e22a925dfe18473..a1c00d0409183fb32e5cea864b7ff3764a5d9c94 100644 (file)
@@ -6,7 +6,7 @@
 void bot_clearqueue(entity bot)
 {
        if(!bot.bot_cmdqueuebuf_allocated)
-               error("clearqueue but no queue allocated");
+               return;
        buf_del(bot.bot_cmdqueuebuf);
        bot.bot_cmdqueuebuf_allocated = FALSE;
        dprint("bot ", bot.netname, " queue cleared\n");
@@ -18,6 +18,8 @@ void bot_queuecommand(entity bot, string cmdstring)
        {
                bot.bot_cmdqueuebuf = buf_create();
                bot.bot_cmdqueuebuf_allocated = TRUE;
+               bot.bot_cmdqueuebuf_start = 0;
+               bot.bot_cmdqueuebuf_end = 0;
        }
 
        bufstr_set(bot.bot_cmdqueuebuf, bot.bot_cmdqueuebuf_end, cmdstring);
@@ -1194,8 +1196,8 @@ void bot_resetqueues()
 
        FOR_EACH_CLIENT(cl) if(cl.isbot)
        {
-               if(cl.bot_cmdqueuebuf_allocated)
-                       bot_clearqueue(cl);
+               cl.bot_cmd_execution_index = 0;
+               bot_clearqueue(cl);
                // also, cancel all barriers
                cl.bot_barrier = 0;
                for(i = 0; i < cl.bot_places_count; ++i)
index 2f6a660cb203f4debee868d307f05fd4c018e4d4..746a22d8940c622f1ffbb7e51060b082886d3c89 100644 (file)
@@ -48,7 +48,7 @@ float CheatsAllowed(float i, float argc, float fr) // the cheat gets passed as a
        // dead people cannot cheat
        if(self.deadflag != DEAD_NO)
                return 0;
-       if(self.classname != "player")
+       if(gamestart_sv_cheats < 2 && self.classname != "player")
                return 0;
        
        // sv_clones
@@ -126,7 +126,6 @@ float CheatImpulse(float i)
        switch(i)
        {
                entity e, e2;
-               vector org;
 
                case CHIMPULSE_SPEEDRUN_INIT: // deploy personal waypoint
                        // shared with regular waypoint init, so this is not a cheat by itself
@@ -191,47 +190,26 @@ float CheatImpulse(float i)
                                        self.oldvelocity = self.velocity = self.personal.velocity;
                                        self.angles = self.personal.v_angle;
                                        self.fixangle = TRUE;
-                                       if(self.flagcarried)
-                                       {
-                                               bprint("The ", self.flagcarried.netname, " was returned to base by its carrier\n");
-                                               ReturnFlag(self.flagcarried);
-                                       }
-                               }
-                               if(g_ctf)
-                               {
-                                       self.ammo_rockets = 999;
-                                       self.ammo_nails = 999;
-                                       self.ammo_cells = 999;
-                                       self.ammo_shells = 999;
-                                       self.ammo_fuel = 999;
-                                       self.health = start_health;
-                                       self.armorvalue = start_armorvalue;
-                                       WEPSET_OR_EA(self.personal, weaponsInMap);
-                                       self.pauserotarmor_finished = time + autocvar_g_balance_pause_armor_rot_spawn;
-                                       self.pauserothealth_finished = time + autocvar_g_balance_pause_health_rot_spawn;
-                                       self.pauserotfuel_finished = time + autocvar_g_balance_pause_fuel_rot_spawn;
-                                       self.pauseregen_finished = time + autocvar_g_balance_pause_health_regen_spawn;
-                                       self.strength_finished = 0;
-                                       self.invincible_finished = 0;
-                               }
-                               else
-                               {
-                                       self.ammo_rockets = self.personal.ammo_rockets;
-                                       self.ammo_nails = self.personal.ammo_nails;
-                                       self.ammo_cells = self.personal.ammo_cells;
-                                       self.ammo_shells = self.personal.ammo_shells;
-                                       self.ammo_fuel = self.personal.ammo_fuel;
-                                       self.health = self.personal.health;
-                                       self.armorvalue = self.personal.armorvalue;
-                                       WEPSET_COPY_EE(self, self.personal);
-                                       self.items = self.personal.items;
-                                       self.pauserotarmor_finished = time + self.personal.pauserotarmor_finished - self.personal.teleport_time;
-                                       self.pauserothealth_finished = time + self.personal.pauserothealth_finished - self.personal.teleport_time;
-                                       self.pauserotfuel_finished = time + self.personal.pauserotfuel_finished - self.personal.teleport_time;
-                                       self.pauseregen_finished = time + self.personal.pauseregen_finished - self.personal.teleport_time;
-                                       self.strength_finished = time + self.personal.strength_finished - self.personal.teleport_time;
-                                       self.invincible_finished = time + self.personal.invincible_finished - self.personal.teleport_time;
+                                       
+                                       MUTATOR_CALLHOOK(AbortSpeedrun);
                                }
+
+                               self.ammo_rockets = self.personal.ammo_rockets;
+                               self.ammo_nails = self.personal.ammo_nails;
+                               self.ammo_cells = self.personal.ammo_cells;
+                               self.ammo_shells = self.personal.ammo_shells;
+                               self.ammo_fuel = self.personal.ammo_fuel;
+                               self.health = self.personal.health;
+                               self.armorvalue = self.personal.armorvalue;
+                               WEPSET_COPY_EE(self, self.personal);
+                               self.items = self.personal.items;
+                               self.pauserotarmor_finished = time + self.personal.pauserotarmor_finished - self.personal.teleport_time;
+                               self.pauserothealth_finished = time + self.personal.pauserothealth_finished - self.personal.teleport_time;
+                               self.pauserotfuel_finished = time + self.personal.pauserotfuel_finished - self.personal.teleport_time;
+                               self.pauseregen_finished = time + self.personal.pauseregen_finished - self.personal.teleport_time;
+                               self.strength_finished = time + self.personal.strength_finished - self.personal.teleport_time;
+                               self.invincible_finished = time + self.personal.invincible_finished - self.personal.teleport_time;
+
                                DID_CHEAT();
                                break;
                        }
@@ -258,7 +236,7 @@ float CheatImpulse(float i)
                                        break;
                                }
                        }
-                       if(MoveToRandomMapLocation(self, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((gamestart_sv_cheats >= 2) ? 100000 : 100), 1024, 256))
+                       if(MoveToRandomMapLocation(self, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((gamestart_sv_cheats < 2) ? 100 : 100000), 1024, 256))
                        {
                                sprint(self, "Emergency teleport used random location\n");
                                self.angles_x = -self.angles_x;
@@ -271,32 +249,24 @@ float CheatImpulse(float i)
                        break;
                case CHIMPULSE_R00T:
                        IS_CHEAT(i, 0, 0);
+                       RandomSelection_Init();
                        FOR_EACH_PLAYER(e)
-                       {
-                               get_model_parameters(e.playermodel, e.skin);
-                               if(get_model_parameters_sex == "Female")
-                               {
-                                       makevectors(e.angles);
-                                       traceline(e.origin, e.origin + v_right * 256, MOVE_NORMAL, e);
-                               }
-                               else
-                               {
-                                       org_x = random();
-                                       org_y = random();
-                                       org_z = 0;
-                                       org = normalize(org);
-                                       traceline(e.origin, e.origin + org * 256, MOVE_NORMAL, e); // random direction
-                               }
+                               if(e.deadflag == DEAD_NO)
+                                       if(IsDifferentTeam(e, self))
+                                               RandomSelection_Add(e, 0, string_null, 1, 1);
+                       if(RandomSelection_chosen_ent)
+                               e = RandomSelection_chosen_ent;
+                       else
+                               e = self;
 
-                               org = findbetterlocation(trace_endpos, 12);
+                       pointparticles(particleeffectnum("rocket_explode"), e.origin, '0 0 0', 1);
+                       sound(e, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
+
+                       e2 = spawn();
+                       setorigin(e2, e.origin);
+                       RadiusDamage(e2, self, 1000, 0, 128, world, 500, DEATH_CHEAT, e);
+                       remove(e2);
 
-                               e2 = spawn();
-                               setorigin(e2, org);
-                               pointparticles(particleeffectnum("rocket_explode"), org, '0 0 0', 1);
-                               sound(e2, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
-                               RadiusDamage(e2, e, 1000, 0, 128, e, 500, DEATH_CHEAT, world);
-                               remove(e2);
-                       }
                        print("404 Sportsmanship not found.\n");
                        DID_CHEAT();
                        break;
@@ -703,6 +673,46 @@ float CheatCommand(float argc)
                        if(GiveItems(self, 1, argc))
                                DID_CHEAT();
                        break;
+               case "usetarget":
+                       IS_CHEAT(0, argc, 0);
+                       e = self;
+                       self = spawn();
+                       self.target = argv(1);
+                       activator = e;
+                       SUB_UseTargets();
+                       remove(self);
+                       self = e;
+                       DID_CHEAT();
+                       break;
+               case "killtarget":
+                       IS_CHEAT(0, argc, 0);
+                       e = self;
+                       self = spawn();
+                       self.killtarget = argv(1);
+                       activator = e;
+                       SUB_UseTargets();
+                       remove(self);
+                       self = e;
+                       DID_CHEAT();
+                       break;
+               case "teleporttotarget":
+                       IS_CHEAT(0, argc, 0);
+                       e = self;
+                       self = spawn();
+                       setorigin(self, self.origin);
+                       self.classname = "cheattriggerteleport";
+                       self.target = argv(1);
+                       teleport_findtarget();
+                       if(!wasfreed(self))
+                       {
+                               Simple_TeleportPlayer(self, e);
+                               remove(self);
+                               self = e;
+                               DID_CHEAT();
+                       }
+                       else
+                               self = e;
+                       break;
        }
 
        END_CHEAT_FUNCTION();
index 18f9bd101112b8d5061564d5d7cafc0386f11a0d..e4f6cf14526e3bc7c5d7bdad8388e8ceeb95d518 100644 (file)
@@ -416,10 +416,7 @@ void PutObserverInServer (void)
        }
 
        if(self.vehicle)
-           vehicles_exit(VHEF_RELESE);
-
-       if(self.flagcarried)
-               DropFlag(self.flagcarried, world, world);
+               vehicles_exit(VHEF_RELESE);         
 
        WaypointSprite_PlayerDead();
 
@@ -449,6 +446,7 @@ void PutObserverInServer (void)
        
        self.classname = "observer";
        self.iscreature = FALSE;
+       self.teleportable = TELEPORT_SIMPLE;
        self.damagedbycontents = FALSE;
        self.health = -666;
        self.takedamage = DAMAGE_NO;
@@ -473,6 +471,7 @@ void PutObserverInServer (void)
        self.invincible_finished = 0;
        self.superweapons_finished = 0;
        self.pushltime = 0;
+       self.istypefrag = 0;
        self.think = SUB_Null;
        self.nextthink = 0;
        self.hook_time = 0;
@@ -641,7 +640,6 @@ PutClientInServer
 Called when a client spawns in the server
 =============
 */
-//void() ctf_playerchanged;
 
 void PutClientInServer (void)
 {
@@ -698,6 +696,7 @@ void PutClientInServer (void)
                self.classname = "player";
                self.wasplayer = TRUE;
                self.iscreature = TRUE;
+               self.teleportable = TELEPORT_NORMAL;
                self.damagedbycontents = TRUE;
                self.movetype = MOVETYPE_WALK;
                self.solid = SOLID_SLIDEBOX;
@@ -928,9 +927,6 @@ void PutClientInServer (void)
        } else if(self.classname == "observer") {
                PutObserverInServer ();
        }
-
-       //if(g_ctf)
-       //      ctf_playerchanged();
 }
 
 .float ebouncefactor, ebouncestop; // electro's values
@@ -1073,8 +1069,7 @@ void ClientKill_Now_TeamChange()
 {
        if(self.killindicator_teamchange == -1)
        {
-               self.team = -1;
-               JoinBestTeam( self, FALSE, FALSE );
+               JoinBestTeam( self, FALSE, TRUE );
        }
        else if(self.killindicator_teamchange == -2)
        {
@@ -1122,7 +1117,7 @@ void KillIndicator_Think()
                return;
        }
 
-       if (self.owner.alpha < 0)
+       if (self.owner.alpha < 0 && !self.owner.vehicle)
        {
                self.owner.killindicator = world;
                remove(self);
@@ -1344,7 +1339,6 @@ ClientConnect
 Called when a client connects to the server
 =============
 */
-//void ctf_clientconnect();
 string ColoredTeamName(float t);
 void DecodeLevelParms (void);
 //void dom_player_join_team(entity pl);
@@ -1443,7 +1437,7 @@ void ClientConnect (void)
        } else {
                if(teamplay)
                {
-                       if(autocvar_g_balance_teams || autocvar_g_balance_teams_force)
+                       if(autocvar_g_balance_teams)
                        {
                                self.classname = "player";
                                campaign_bots_may_start = 1;
@@ -1516,10 +1510,6 @@ void ClientConnect (void)
                if(g_arena)
                        Spawnqueue_Insert(self);
        }
-       /*else if(g_ctf)
-       {
-               ctf_clientconnect();
-       }*/
 
        attach_entcs();
 
@@ -1601,6 +1591,8 @@ void ClientConnect (void)
         return;
         
     sv_notice_join();
+    
+    MUTATOR_CALLHOOK(ClientConnect);
 }
 /*
 =============
@@ -1655,8 +1647,6 @@ void ClientDisconnect (void)
        Portal_ClearAll(self);
 
        RemoveGrapplingHook(self);
-       if(self.flagcarried)
-               DropFlag(self.flagcarried, world, world);
 
        // Here, everything has been done that requires this player to be a client.
 
@@ -2254,8 +2244,13 @@ entity CA_SpectateNext(entity start) {
        return other;
 }
 
-float SpectateNext() {
-       other = find(self.enemy, classname, "player");
+float SpectateNext(entity _prefer) {
+       
+       if(_prefer)
+               other = _prefer;        
+       else
+               other = find(self.enemy, classname, "player");
+       
        if (g_ca && !autocvar_g_ca_spectate_enemies && self.caplayer) {
                // CA and ca players when spectating enemies is forbidden
                other = CA_SpectateNext(other);
@@ -2331,7 +2326,7 @@ void LeaveSpectatorMode()
                if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (self.wasplayer && autocvar_g_changeteam_banned) || self.team_forced > 0) {
                        self.classname = "player";
 
-                       if(autocvar_g_campaign || autocvar_g_balance_teams || autocvar_g_balance_teams_force)
+                       if(autocvar_g_campaign || autocvar_g_balance_teams)
                                JoinBestTeam(self, FALSE, TRUE);
 
                        if(autocvar_g_campaign)
@@ -2469,7 +2464,7 @@ void ObserverThink()
                        self.flags |= FL_SPAWNING;
                } else if(self.BUTTON_ATCK && !self.version_mismatch) {
                        self.flags &~= FL_JUMPRELEASED;
-                       if(SpectateNext() == 1) {
+                       if(SpectateNext(world) == 1) {
                                self.classname = "spectator";
                        }
                } else {
@@ -2500,7 +2495,7 @@ void SpectatorThink()
                        self.flags |= FL_SPAWNING;
                } else if(self.BUTTON_ATCK) {
                        self.flags &~= FL_JUMPRELEASED;
-                       if(SpectateNext() == 1) {
+                       if(SpectateNext(world) == 1) {
                                self.classname = "spectator";
                        } else {
                                self.classname = "observer";
@@ -2532,7 +2527,6 @@ void SpectatorThink()
        self.flags |= FL_CLIENT | FL_NOTARGET;
 }
 
-float ctf_usekey();
 void PlayerUseKey()
 {
        if(self.classname != "player")
@@ -2545,9 +2539,6 @@ void PlayerUseKey()
        }
        
        // a use key was pressed; call handlers
-       if(ctf_usekey())
-               return;
-
        MUTATOR_CALLHOOK(PlayerUseKey);
 }
 
@@ -2561,7 +2552,6 @@ Called every frame for each client before the physics are run
 =============
 */
 .float usekeypressed;
-void() ctf_setstatus;
 void() nexball_setstatus;
 .float items_added;
 void PlayerPreThink (void)
@@ -2798,7 +2788,8 @@ void PlayerPreThink (void)
 
                self.prevorigin = self.origin;
 
-               if (((self.BUTTON_CROUCH && !self.hook.state) || self.health <= g_bloodloss) && self.animstate_startframe != self.anim_melee_x) // prevent crouching if using melee attack
+               if (!self.vehicle)
+               if (((self.BUTTON_CROUCH && !self.hook.state) || self.health <= g_bloodloss) && self.animstate_startframe != self.anim_melee_x && !self.freezetag_frozen) // prevent crouching if using melee attack
                {
                        if (!self.crouch)
                        {
@@ -2859,9 +2850,6 @@ void PlayerPreThink (void)
                if(frametime)
                        player_anim();
 
-               if(g_ctf)
-                       ctf_setstatus();
-
                if(g_nexball)
                        nexball_setstatus();
                
index 987e07c46f228b892e9741c3c4b75e00a5571968..529567a3b407cc7118590aa59c451fb3444798ac 100644 (file)
@@ -165,11 +165,14 @@ void ImpulseCommands (void)
                        case 33:
                                if(self.deadflag == DEAD_NO && teamplay)
                                {
-                                       wp = WaypointSprite_Attach("helpme", TRUE, RADARICON_HELPME, '1 0.5 0');
-                                       if(!wp)
-                                               WaypointSprite_HelpMePing(self.waypointsprite_attachedforcarrier);
-                                       else
-                                               WaypointSprite_Ping(wp);
+                                       if not(MUTATOR_CALLHOOK(HelpMePing))
+                                       {
+                                               wp = WaypointSprite_Attach("helpme", TRUE, RADARICON_HELPME, '1 0.5 0');
+                                               if(!wp)
+                                                       WaypointSprite_HelpMePing(self.waypointsprite_attachedforcarrier);
+                                               else
+                                                       WaypointSprite_Ping(wp);
+                                       }
                                        sprint(self, "HELP ME attached\n");
                                }
                                break;
index 51bfc62c6382a3fcf3c64e5aa22e4ebb8812c654..3e2268d89748d1eb193892407968c8ee721f66a5 100644 (file)
@@ -1074,7 +1074,7 @@ void SV_PlayerPhysics()
                        PM_Accelerate(wishdir, wishspeed, wishspeed, autocvar_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
                }
        }
-       else if ((self.items & IT_JETPACK) && self.BUTTON_HOOK && (!autocvar_g_jetpack_fuel || self.ammo_fuel >= 0.01 || self.items & IT_UNLIMITED_WEAPON_AMMO))
+       else if ((self.items & IT_JETPACK) && self.BUTTON_HOOK && (!autocvar_g_jetpack_fuel || self.ammo_fuel >= 0.01 || self.items & IT_UNLIMITED_WEAPON_AMMO) && !self.freezetag_frozen)
        {
                //makevectors(self.v_angle_y * '0 1 0');
                makevectors(self.v_angle);
@@ -1176,7 +1176,7 @@ void SV_PlayerPhysics()
        else if (self.flags & FL_ONGROUND)
        {
                // we get here if we ran out of ammo
-               if((self.items & IT_JETPACK) && self.BUTTON_HOOK && !(buttons_prev & 32))
+               if((self.items & IT_JETPACK) && self.BUTTON_HOOK && !(buttons_prev & 32) && self.ammo_fuel < 0.01)
                        sprint(self, "You don't have any fuel for the ^2Jetpack\n");
 
                // walking
@@ -1259,7 +1259,7 @@ void SV_PlayerPhysics()
        {
                float wishspeed0;
                // we get here if we ran out of ammo
-               if((self.items & IT_JETPACK) && self.BUTTON_HOOK && !(buttons_prev & 32))
+               if((self.items & IT_JETPACK) && self.BUTTON_HOOK && !(buttons_prev & 32) && self.ammo_fuel < 0.01)
                        sprint(self, "You don't have any fuel for the ^2Jetpack\n");
 
                if(maxspd_mod < 1)
index fd14d42b8d9aa4718450f3a68591d0a4a04bfc0c..3e560bd2452a71020d339d7ed22830a76b212ec8 100644 (file)
@@ -123,6 +123,7 @@ void WeaponStats_LogKill(float awep, float abot, float vwep, float vbot)
 
 .entity pusher;
 .float pushltime;
+.float istypefrag;
 
 .float CopyBody_nextthink;
 .void(void) CopyBody_think;
@@ -151,6 +152,7 @@ void CopyBody(float keepvelocity)
        self.lip = oldself.lip;
        self.colormap = oldself.colormap;
        self.iscreature = oldself.iscreature;
+       self.teleportable = oldself.teleportable;
        self.damagedbycontents = oldself.damagedbycontents;
        self.angles = oldself.angles;
        self.avelocity = oldself.avelocity;
@@ -271,7 +273,9 @@ void player_anim (void)
 
        if (!self.animstate_override)
        {
-               if (!(self.flags & FL_ONGROUND) || self.BUTTON_JUMP)
+               if (self.freezetag_frozen)
+                       setanim(self, self.anim_idle, TRUE, FALSE, FALSE);
+               else if (!(self.flags & FL_ONGROUND) || self.BUTTON_JUMP)
                {
                        if (self.crouch)
                        {
@@ -422,6 +426,7 @@ void PlayerCorpseDamage (entity inflictor, entity attacker, float damage, float
                self.alpha = -1;
                self.solid = SOLID_NOT; // restore later
                self.takedamage = DAMAGE_NO; // restore later
+               self.damagedbycontents = FALSE;
        }
 }
 
@@ -580,11 +585,13 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                // don't reset pushltime for self damage as it may be an attempt to
                // escape a lava pit or similar
                //self.pushltime = 0;
+               self.istypefrag = 0;
        }
        else if(attacker.classname == "player")
        {
                self.pusher = attacker;
                self.pushltime = time + autocvar_g_maxpushtime;
+               self.istypefrag = self.BUTTON_CHAT;
        }
        else if(time < self.pushltime)
        {
@@ -592,7 +599,10 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
                self.pushltime = max(self.pushltime, time + 0.6);
        }
        else
+       {
                self.pushltime = 0;
+               self.istypefrag = 0;
+       }
 
        float abot, vbot, awep;
        abot = (clienttype(attacker) == CLIENTTYPE_BOT);
@@ -695,15 +705,6 @@ void PlayerDamage (entity inflictor, entity attacker, float damage, float deatht
 
                RemoveGrapplingHook(self);
 
-               if(self.flagcarried)
-               {
-                       if(attacker.classname != "player")
-                               DropFlag(self.flagcarried, self, attacker); // penalty for flag loss by suicide
-                       else if(attacker.team == self.team)
-                               DropFlag(self.flagcarried, attacker, attacker); // penalty for flag loss by suicide/teamkill
-                       else
-                               DropFlag(self.flagcarried, world, attacker);
-               }
                Portal_ClearAllLater(self);
 
                if(clienttype(self) == CLIENTTYPE_REAL)
index ab58a4aee39dd12316f935087da74945e8886c6c..eebadfa73b2d48ed5f3ad6573ae0390d72a228b6 100644 (file)
@@ -112,11 +112,40 @@ void BanCommand_unban(float request, float argc)
        switch(request)
        {
                case CMD_REQUEST_COMMAND:
-               {
+               {       
                        if(argv(1))
                        {
-                               Ban_Delete(stof(argv(1)));
-                               return;
+                               float tmp_number = -1;
+                               string tmp_string;
+                               
+                               if(substring(argv(1), 0, 1) == "#")
+                               {
+                                       tmp_string = substring(argv(1), 1, -1);
+                                       
+                                       if(tmp_string != "") // is it all one token? like #1
+                                       {
+                                               tmp_number = stof(tmp_string);
+                                       }
+                                       else if(argc > 2) // no, it's two tokens? # 1
+                                       {
+                                               tmp_number = stof(argv(2));
+                                       }
+                                       else
+                                               tmp_number = -1;
+                               }
+                               else // maybe it's ONLY a number?
+                               {
+                                       tmp_number = stof(argv(1));
+                                       
+                                       if((tmp_number == 0) && (argv(1) != "0"))
+                                               { tmp_number = -1; }
+                               }
+
+                               if(tmp_number >= 0)
+                               {
+                                       Ban_Delete(tmp_number);
+                                       return;
+                               }
                        }
                }
                        
@@ -228,4 +257,4 @@ float BanCommand(string command)
        }
        
        return FALSE;
-}
\ No newline at end of file
+}
index c4e85e28ef55d37debee2e5fc81c9da48ae7b419..530646afd2ec8fbb105dc8a080b41dadaaa25d0f 100644 (file)
@@ -90,7 +90,7 @@ void ClientCommand_clientversion(float request, float argc) // internal command,
                                                self.version_mismatch = 1;
                                                ClientKill_TeamChange(-2); // observe
                                        } 
-                                       else if(autocvar_g_campaign || autocvar_g_balance_teams || autocvar_g_balance_teams_force
+                                       else if(autocvar_g_campaign || autocvar_g_balance_teams) 
                                        {
                                                //JoinBestTeam(self, FALSE, TRUE);
                                        } 
index 13e667ad87123b72c7a282f3633d6940ec1abbd1..eae71b045c82845688d653b999d705f07854ac68 100644 (file)
@@ -146,7 +146,7 @@ float GetFilteredNumber(string input)
        return output;
 }
 
-// switch between sprint and print depending on whether the reciever is the server or a player
+// switch between sprint and print depending on whether the receiver is the server or a player
 void print_to(entity to, string input)
 {
     if(to)
index 10f725f32f7c3e6cf815f731e71125076c571bbe..b2bf9d9b97aa6bb2a9b92433299234c351f01b28 100644 (file)
@@ -118,7 +118,7 @@ void GameCommand_adminmsg(float request, float argc)
                                if(successful)
                                        bprint("Successfully sent message '", admin_message, "' to ", successful, ".\n");
                                else
-                                       print("No players given (", original_targets, ") could recieve the message.\n");
+                                       print("No players given (", original_targets, ") could receive the message.\n");
                                        
                                return;
                        }
@@ -316,7 +316,7 @@ void GameCommand_bbox(float request)
        }
 }
 
-void GameCommand_bot_cmd(float request, float argc)
+void GameCommand_bot_cmd(float request, float argc, string command)
 {
        switch(request)
        {
@@ -329,6 +329,17 @@ void GameCommand_bot_cmd(float request, float argc)
                                bot_resetqueues();
                                return;
                        }
+                       else if(argv(1) == "setbots")
+                       {
+                               cvar_settemp("bot_vs_human", "0");
+                               cvar_settemp("minplayers", "0");
+                               cvar_settemp("bot_number", "0");
+                               bot_fixcount();
+                               cvar_settemp("bot_number", argv(2));
+                               if(!bot_fixcount())
+                                       print("Sorry, could not set requested bot count\n");
+                               return;
+                       }
                        else if(argv(1) == "load" && argc == 3)
                        {
                                float fh, i;
@@ -353,7 +364,10 @@ void GameCommand_bot_cmd(float request, float argc)
                                                }
                                                else if(argv(2) == "setbots")
                                                {
+                                                       cvar_settemp("bot_vs_human", "0");
                                                        cvar_settemp("minplayers", "0");
+                                                       cvar_settemp("bot_number", "0");
+                                                       bot_fixcount();
                                                        cvar_settemp("bot_number", argv(3));
                                                        if(!bot_fixcount())
                                                                print("Sorry, could not set requested bot count\n");
@@ -365,7 +379,7 @@ void GameCommand_bot_cmd(float request, float argc)
                                                        if(bot == world)
                                                                bot = find_bot_by_name(argv(2));
                                                        if(bot)
-                                                               bot_queuecommand(bot, strcat(argv(3), " ", argv(4)));
+                                                               bot_queuecommand(bot, substring(s, argv_start_index(3), -1));
                                                }
                                        }
                                        else
@@ -392,8 +406,8 @@ void GameCommand_bot_cmd(float request, float argc)
                                        bot = find_bot_by_name(argv(1));
                                if(bot)
                                {
-                                       print(strcat("Command '", strcat(argv(2), " ", argv(3)), "' sent to bot ", bot.netname, "\n"));
-                                       bot_queuecommand(bot, strcat(argv(2), " ", argv(3)));
+                                       print(strcat("Command '", substring(command, argv_start_index(2), -1), "' sent to bot ", bot.netname, "\n"));
+                                       bot_queuecommand(bot, substring(command, argv_start_index(2), -1));
                                        return;
                                }
                                else
@@ -1351,7 +1365,7 @@ void GameCommand_stuffto(float request, float argc)
                {
                        if(argv(2))
                        {
-                               entity client = GetIndexedEntity(argc, 1));
+                               entity client = GetIndexedEntity(argc, 1);
                                float accepted = VerifyClientEntity(client, TRUE, FALSE);
                                
                                if(accepted > 0)
@@ -1660,7 +1674,7 @@ void GameCommand_(float request)
        SERVER_COMMAND("allspec", GameCommand_allspec(request, arguments), "Force all players to spectate") \
        SERVER_COMMAND("anticheat", GameCommand_anticheat(request, arguments), "Create an anticheat report for a client") \
        SERVER_COMMAND("bbox", GameCommand_bbox(request), "Print detailed information about world size") \
-       SERVER_COMMAND("bot_cmd", GameCommand_bot_cmd(request, arguments), "Control and send commands to bots") \
+       SERVER_COMMAND("bot_cmd", GameCommand_bot_cmd(request, arguments, command), "Control and send commands to bots") \
        SERVER_COMMAND("cointoss", GameCommand_cointoss(request, arguments), "Flip a virtual coin and give random result") \
        SERVER_COMMAND("database", GameCommand_database(request, arguments), "Extra controls of the serverprogs database") \
        SERVER_COMMAND("defer_clear", GameCommand_defer_clear(request, arguments), "Clear all queued defer commands for a specific client") \
index b95c7261366b099902f2bb67ac4730661c024db7..aec1e3256b020878305579b179dfeea42a18e67e 100644 (file)
@@ -142,11 +142,6 @@ float      MSG_ENTITY                              = 5; // csqc
 
 float TE_BEAM                                  = 13;           // grappling hook
 
-// CTF
-float FLAG_BASE = 1;
-float FLAG_CARRY = 2;
-float FLAG_DROPPED = 3;
-
 float COLOR_TEAM1      = 5;  // red
 float COLOR_TEAM2      = 14; // blue
 float COLOR_TEAM3      = 13; // yellow
diff --git a/qcsrc/server/ctf.qc b/qcsrc/server/ctf.qc
deleted file mode 100644 (file)
index d84075c..0000000
+++ /dev/null
@@ -1,1223 +0,0 @@
-#define FLAG_MIN (PL_MIN + '0 0 -13')
-#define FLAG_MAX (PL_MAX + '0 0 -13')
-
-.entity basewaypoint;
-.entity sprite;
-entity ctf_worldflaglist; // CTF flags in the map
-.entity ctf_worldflagnext;
-.float dropperid;
-.float ctf_droptime;
-
-.float next_take_time;                 // the next time a player can pick up a flag (time + blah)
-                                                               /// I used this, in part, to fix the looping score bug. - avirox
-//float FLAGSCORE_PICKUP        =  1;
-//float FLAGSCORE_RETURN        =  5; // returned by owner team
-//float FLAGSCORE_RETURNROGUE   = 10; // returned by rogue team
-//float FLAGSCORE_CAPTURE       =  5;
-
-#define FLAG_CARRY_POS '-15 0 7'
-
-.float ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture
-
-float captureshield_min_negscore; // punish at -20 points
-float captureshield_max_ratio; // punish at most 30% of each team
-float captureshield_force; // push force of the shield
-
-float ctf_captureshield_shielded(entity p)
-{
-       float s, se;
-       entity e;
-       float players_worseeq, players_total;
-
-       if(captureshield_max_ratio <= 0)
-               return FALSE;
-
-       s = PlayerScore_Add(p, SP_SCORE, 0);
-       if(s >= -captureshield_min_negscore)
-               return FALSE;
-
-       players_total = players_worseeq = 0;
-       FOR_EACH_PLAYER(e)
-       {
-               if(e.team != p.team)
-                       continue;
-               se = PlayerScore_Add(e, SP_SCORE, 0);
-               if(se <= s)
-                       ++players_worseeq;
-               ++players_total;
-       }
-
-       // player is in the worse half, if >= half the players are better than him, or consequently, if < half of the players are worse
-       // use this rule here
-
-       if(players_worseeq >= players_total * captureshield_max_ratio)
-               return FALSE;
-
-       return TRUE;
-}
-
-void ctf_captureshield_update(entity p, float dir)
-{
-       float should;
-       if(dir == p.ctf_captureshielded) // 0: shield only, 1: unshield only
-       {
-               should = ctf_captureshield_shielded(p);
-               if(should != dir)
-               {
-                       if(should)
-                       {
-                               Send_CSQC_Centerprint_Generic(other, CPID_CTF_CAPTURESHIELD, "^3You are ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Get some defensive scores before trying again.", 5, 0);
-                               // TODO csqc notifier for this
-                       }
-                       else
-                       {
-                               Send_CSQC_Centerprint_Generic(p, CPID_CTF_CAPTURESHIELD, "^3You are now free.\n\n^3Feel free to ^1try to capture^3 the flag again\n^3if you think you will succeed.", 5, 0);
-                               // TODO csqc notifier for this
-                       }
-                       p.ctf_captureshielded = should;
-               }
-       }
-}
-
-float ctf_captureshield_customize()
-{
-       if not(other.ctf_captureshielded)
-               return FALSE;
-       if(self.team == other.team)
-               return FALSE;
-       return TRUE;
-}
-
-.float ctf_captureshield_touch_msgtime;
-void ctf_captureshield_touch()
-{
-       if not(other.ctf_captureshielded)
-               return;
-       if(self.team == other.team)
-               return;
-       vector mymid;
-       vector othermid;
-       mymid = (self.absmin + self.absmax) * 0.5;
-       othermid = (other.absmin + other.absmax) * 0.5;
-       Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * captureshield_force);
-       if (time - other.ctf_captureshield_touch_msgtime > 2)
-               Send_CSQC_Centerprint_Generic(other, CPID_CTF_CAPTURESHIELD, "^3You are ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Get some defensive scores before trying again.", 5, 0);
-       other.ctf_captureshield_touch_msgtime = time;
-}
-
-void ctf_flag_spawnstuff()
-{
-       entity e;
-       e = spawn();
-       e.enemy = self;
-       e.team = self.team;
-       e.touch = ctf_captureshield_touch;
-       e.customizeentityforclient = ctf_captureshield_customize;
-       e.classname = "ctf_captureshield";
-       e.effects = EF_ADDITIVE;
-       e.movetype = MOVETYPE_NOCLIP;
-       e.solid = SOLID_TRIGGER;
-       e.avelocity = '7 0 11';
-       setorigin(e, self.origin);
-       setmodel(e, "models/ctf/shield.md3");
-       e.scale = 0.5;
-       setsize(e, e.scale * e.mins, e.scale * e.maxs);
-
-       waypoint_spawnforitem_force(self, self.origin);
-       self.nearestwaypointtimeout = 0; // activate waypointing again
-       self.basewaypoint = self.nearestwaypoint;
-
-       if(self.team == COLOR_TEAM1)
-               WaypointSprite_SpawnFixed("redbase", self.origin + '0 0 61', self, sprite, RADARICON_FLAG, colormapPaletteColor(COLOR_TEAM1 - 1, FALSE));
-       else
-               WaypointSprite_SpawnFixed("bluebase", self.origin + '0 0 61', self, sprite, RADARICON_FLAG, colormapPaletteColor(COLOR_TEAM2 - 1, FALSE));
-}
-
-float ctf_score_value(string parameter)
-{
-       return cvar(strcat("g_ctf_personal", parameter));
-}
-
-void FakeTimeLimit(entity e, float t)
-{
-       msg_entity = e;
-       WriteByte(MSG_ONE, 3); // svc_updatestat
-       WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT
-       if(t < 0)
-               WriteCoord(MSG_ONE, autocvar_timelimit);
-       else
-               WriteCoord(MSG_ONE, (t + 1) / 60);
-}
-
-float   flagcaptimerecord;
-.float  flagpickuptime;
-//.float  iscommander;
-//.float  ctf_state;
-
-void() FlagThink;
-void() FlagTouch;
-
-void place_flag()
-{
-       if(self.classname != "item_flag_team")
-       {
-               backtrace("PlaceFlag a non-flag");
-               return;
-       }
-
-       setattachment(self, world, "");
-       self.mdl = self.model;
-       self.flags = FL_ITEM | FL_NOTARGET;
-       self.solid = SOLID_TRIGGER;
-       self.movetype = MOVETYPE_NONE;
-       self.velocity = '0 0 0';
-       self.origin_z = self.origin_z + 6;
-       self.think = FlagThink;
-       self.touch = FlagTouch;
-       self.nextthink = time + 0.1;
-       self.cnt = FLAG_BASE;
-       self.mangle = self.angles;
-       self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP;
-       //self.effects = self.effects | EF_DIMLIGHT;
-       if(self.noalign)
-       {
-               self.dropped_origin = self.origin;
-       }
-       else
-       {
-               droptofloor();
-               self.movetype = MOVETYPE_TOSS;
-       }
-
-       InitializeEntity(self, ctf_flag_spawnstuff, INITPRIO_SETLOCATION);
-}
-
-void LogCTF(string mode, float flagteam, entity actor)
-{
-       string s;
-       if(!autocvar_sv_eventlog)
-               return;
-       s = strcat(":ctf:", mode);
-       s = strcat(s, ":", ftos(flagteam));
-       if(actor != world)
-               s = strcat(s, ":", ftos(actor.playerid));
-       GameLogEcho(s);
-}
-
-void RegenFlag(entity e)
-{
-       if(e.classname != "item_flag_team")
-       {
-               backtrace("RegenFlag a non-flag");
-               return;
-       }
-
-       if(e.waypointsprite_attachedforcarrier)
-               WaypointSprite_DetachCarrier(e);
-
-       setattachment(e, world, "");
-       e.damageforcescale = 0;
-       e.takedamage = DAMAGE_NO;
-       e.movetype = MOVETYPE_NONE;
-       if(!e.noalign)
-               e.movetype = MOVETYPE_TOSS;
-       e.velocity = '0 0 0';
-       e.solid = SOLID_TRIGGER;
-       // TODO: play a sound here
-       setorigin(e, e.dropped_origin);
-       e.angles = e.mangle;
-       e.cnt = FLAG_BASE;
-       e.owner = world;
-       e.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND and any other junk
-}
-
-void ReturnFlag(entity e)
-{
-       if(e.classname != "item_flag_team")
-       {
-               backtrace("ReturnFlag a non-flag");
-               return;
-       }
-
-       if (e.owner)
-       if (e.owner.flagcarried == e)
-       {
-               WaypointSprite_DetachCarrier(e.owner);
-               e.owner.flagcarried = world;
-
-               if(e.speedrunning)
-                       FakeTimeLimit(e.owner, -1);
-       }
-       e.owner = world;
-       RegenFlag(e);
-}
-
-void DropFlag(entity e, entity penalty_receiver, entity attacker)
-{
-       entity p;
-
-       if(e.classname != "item_flag_team")
-       {
-               backtrace("DropFlag a non-flag");
-               return;
-       }
-
-       if(e.speedrunning)
-       {
-               ReturnFlag(e);
-               return;
-       }
-
-       if (!e.owner)
-       {
-               dprint("FLAG: drop - no owner?!?!\n");
-               return;
-       }
-       p = e.owner;
-       if (p.flagcarried != e)
-       {
-               dprint("FLAG: drop - owner is not carrying this flag??\n");
-               return;
-       }
-       //bprint(p.netname, "^7 lost the ", e.netname, "\n");
-       Send_KillNotification (p.netname, e.netname, "", INFO_LOSTFLAG, MSG_INFO);
-
-       if(penalty_receiver)
-               UpdateFrags(penalty_receiver, -ctf_score_value("penalty_suicidedrop"));
-       else
-               UpdateFrags(p, -ctf_score_value("penalty_drop"));
-       PlayerScore_Add(p, SP_CTF_DROPS, +1);
-       ctf_captureshield_update(p, 0); // shield only
-       e.playerid = attacker.playerid;
-       e.ctf_droptime = time;
-       WaypointSprite_Spawn("flagdropped", 0, 0, e, '0 0 1' * 61, world, COLOR_TEAM1 + COLOR_TEAM2 - e.team, e, waypointsprite_attachedforcarrier, FALSE, RADARICON_FLAG, '0 1 1');
-       WaypointSprite_Ping(e.waypointsprite_attachedforcarrier);
-       
-       if(p.waypointsprite_attachedforcarrier)
-       {
-               WaypointSprite_DetachCarrier(p);
-       }
-       else
-       {
-               bprint("\{1}^1Flag carrier had no flag sprite?!?\n");
-               backtrace("Flag carrier had no flag sprite?!?");
-       }
-       LogCTF("dropped", p.team, p);
-       sound (p, CH_TRIGGER, self.noise4, VOL_BASE, ATTN_NONE);
-
-       setattachment(e, world, "");
-       e.damageforcescale = autocvar_g_balance_ctf_damageforcescale;
-       e.takedamage = DAMAGE_YES;
-
-       if (p.flagcarried == e)
-               p.flagcarried = world;
-       e.owner = world;
-
-       e.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND and any other junk
-       e.solid = SOLID_TRIGGER;
-       e.movetype = MOVETYPE_TOSS;
-       // setsize(e, '-16 -16 0', '16 16 74');
-       setorigin(e, p.origin - '0 0 24' + '0 0 37');
-       e.cnt = FLAG_DROPPED;
-       e.velocity = '0 0 300';
-       e.pain_finished = time + autocvar_g_ctf_flag_returntime;//30;
-
-       trace_startsolid = FALSE;
-       tracebox(e.origin, e.mins, e.maxs, e.origin, TRUE, e);
-       if(trace_startsolid)
-               dprint("FLAG FALLTHROUGH will happen SOON\n");
-}
-
-void FlagThink()
-{
-       entity e;
-
-       self.nextthink = time + 0.1;
-
-       // sorry, we have to reset the flag size if it got squished by something
-       if(self.mins != FLAG_MIN || self.maxs != FLAG_MAX)
-       {
-               // if we can grow back, grow back
-               tracebox(self.origin, FLAG_MIN, FLAG_MAX, self.origin, MOVE_NOMONSTERS, self);
-               if(!trace_startsolid)
-                       setsize(self, FLAG_MIN, FLAG_MAX);
-       }
-
-       if(self == ctf_worldflaglist) // only for the first flag
-       {
-               FOR_EACH_CLIENT(e)
-                       ctf_captureshield_update(e, 1); // release shield only
-       }
-
-       if(self.speedrunning)
-       if(self.cnt == FLAG_CARRY)
-       {
-               if(self.owner)
-               if(flagcaptimerecord)
-               if(time >= self.flagpickuptime + flagcaptimerecord)
-               {
-                       bprint("The ", self.netname, " became impatient after ", ftos_decimals(flagcaptimerecord, 2), " seconds and returned itself\n");
-
-                       sound (self, CH_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE);
-                       self.owner.impulse = 141; // returning!
-
-                       e = self;
-                       self = self.owner;
-                       ReturnFlag(e);
-                       ImpulseCommands();
-                       self = e;
-                       return;
-               }
-       }
-
-       if (self.cnt == FLAG_BASE)
-               return;
-
-       if (self.cnt == FLAG_DROPPED)
-       {
-               // flag fallthrough? FIXME remove this if bug is really fixed now
-               if(self.origin_z < -131072)
-               {
-                       dprint("FLAG FALLTHROUGH just happened\n");
-                       self.pain_finished = 0;
-               }
-               setattachment(self, world, "");
-               if (time > self.pain_finished)
-               {
-                       bprint("The ", self.netname, " has returned to base\n");
-                       sound (self, CH_TRIGGER, self.noise3, VOL_BASE, ATTN_NONE);
-                       LogCTF("returned", self.team, world);
-                       ReturnFlag(self);
-               }
-               return;
-       }
-
-       e = self.owner;
-       if (e.classname != "player" || (e.deadflag) || (e.flagcarried != self))
-       {
-               dprint("CANNOT HAPPEN - player dead and STILL had a flag!\n");
-               DropFlag(self, world, world);
-               return;
-       }
-}
-
-float ctf_usekey()
-{
-       if(self.flagcarried)
-       {
-               DropFlag(self.flagcarried, self, world);
-               return TRUE;
-       }
-       return FALSE;
-}
-
-void flag_cap_ring_spawn(vector org)
-{
-       shockwave_spawn("models/ctf/shockwavetransring.md3", org - '0 0 15', -0.8, 0, 1);
-}
-
-// TODO add FlagDamage, replace weird hurttrigger handling inside trigger_hurt code by it
-void FlagTouch()
-{
-       if(gameover) return;
-
-       float t;
-       entity player;
-       string s, s0, h0, h1;
-
-       if (self.cnt == FLAG_CARRY)
-               return;
-
-       if (self.cnt == FLAG_DROPPED)
-       {
-               if(ITEM_TOUCH_NEEDKILL())
-               {
-                       self.pain_finished = 0; // return immediately
-                       return;
-               }
-       }
-
-       if (other.classname != "player")
-               return;
-       if (other.health < 1) // ignore dead players
-               return;
-
-       if (self.cnt == FLAG_BASE)
-       if (other.team == self.team)
-       if (other.flagcarried) // he's got a flag
-       if (other.flagcarried.team != self.team) // capture
-       {
-               if (other.flagcarried == world)
-               {
-                       return;
-               }
-               if(autocvar_g_ctf_captimerecord_always || player_count - currentbots <= 1) // at most one human
-               {
-                       t = time - other.flagcarried.flagpickuptime;
-                       s = ftos_decimals(t, 2);
-                       s0 = ftos_decimals(flagcaptimerecord, 2);
-                       h0 = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"));
-                       h1 = other.netname;
-                       if(h0 == h1)
-                               h0 = "their";
-                       else
-                               h0 = strcat(h0, "^7's"); // h0: display text for previous netname
-                       if (flagcaptimerecord == 0)
-                       {
-                               s = strcat(" in ", s, " seconds");
-                               flagcaptimerecord = t;
-                               db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(t));
-                               db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), h1);
-                               write_recordmarker(other, time - t, t);
-                       }
-                       else if (t < flagcaptimerecord)
-                       {
-                               s = strcat(" in ", s, " seconds, breaking ", h0, " previous record of ", s0, " seconds");
-                               flagcaptimerecord = t;
-                               db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(t));
-                               db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), h1);
-                               write_recordmarker(other, time - t, t);
-                       }
-                       else
-                       {
-                               s = strcat(" in ", s, " seconds, failing to break ", h0, " record of ", s0, " seconds");
-                       }
-               }
-               else
-                       s = "";
-
-               Send_KillNotification (other.netname, other.flagcarried.netname, s, INFO_CAPTUREFLAG, MSG_INFO);
-
-               PlayerTeamScore_Add(other, SP_CTF_CAPS, ST_CTF_CAPS, 1);
-               LogCTF("capture", other.flagcarried.team, other);
-               // give credit to the individual player
-               UpdateFrags(other, ctf_score_value("score_capture"));
-
-               if (autocvar_g_ctf_flag_capture_effects) {
-                       if (other.team == COLOR_TEAM1) { // red team scores effect
-                               pointparticles(particleeffectnum("red_ground_quake"), self.origin, '0 0 0', 1);
-                               flag_cap_ring_spawn(self.origin);
-                       }
-                       if (other.team == COLOR_TEAM2) { // blue team scores effect
-                               pointparticles(particleeffectnum("blue_ground_quake"), self.origin, '0 0 0', 1);
-                               flag_cap_ring_spawn(self.origin);
-                       }
-               }
-
-               sound (other, CH_TRIGGER, self.noise2, VOL_BASE, ATTN_NONE);
-               WaypointSprite_DetachCarrier(other);
-               if(self.speedrunning)
-                       FakeTimeLimit(other, -1);
-               RegenFlag (other.flagcarried);
-               other.flagcarried = world;
-               other.next_take_time = time + 1;
-       }
-       if (self.cnt == FLAG_BASE)
-       if (other.team == COLOR_TEAM1 || other.team == COLOR_TEAM2) // only red and blue team can steal flags
-       if (other.team != self.team)
-       if (!other.flagcarried)
-       if (!other.ctf_captureshielded)
-       {
-               if (other.next_take_time > time)
-                       return;
-
-               if (autocvar_g_ctf_flag_pickup_effects) // pickup effect
-                       pointparticles(particleeffectnum("smoke_ring"), 0.5 * (self.absmin + self.absmax), '0 0 0', 1);
-
-               // pick up
-               self.flagpickuptime = time; // used for timing runs
-               self.speedrunning = other.speedrunning; // if speedrunning, flag will self-return and teleport the owner back after the record
-               if(other.speedrunning)
-               if(flagcaptimerecord)
-                       FakeTimeLimit(other, time + flagcaptimerecord);
-               self.solid = SOLID_NOT;
-               setorigin(self, self.origin); // relink
-               self.owner = other;
-               other.flagcarried = self;
-               self.cnt = FLAG_CARRY;
-               self.angles = '0 0 0';
-               //bprint(other.netname, "^7 got the ", self.netname, "\n");
-               Send_KillNotification (other.netname, self.netname, "", INFO_GOTFLAG, MSG_INFO);
-               UpdateFrags(other, ctf_score_value("score_pickup_base"));
-               self.dropperid = other.playerid;
-               PlayerScore_Add(other, SP_CTF_PICKUPS, 1);
-               LogCTF("steal", self.team, other);
-               sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NONE);
-
-               FOR_EACH_PLAYER(player)
-                       if(player.team == self.team)
-                               centerprint(player, "The enemy got your flag! Retrieve it!");
-
-               self.movetype = MOVETYPE_NONE;
-               setorigin(self, FLAG_CARRY_POS);
-               setattachment(self, other, "");
-               WaypointSprite_AttachCarrier("flagcarrier", other, RADARICON_FLAGCARRIER, '1 1 0');
-               WaypointSprite_Ping(self.sprite);
-
-               return;
-       }
-
-       if (self.cnt == FLAG_DROPPED)
-       {
-               self.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND and any other junk
-               if (other.team == self.team || (other.team != COLOR_TEAM1 && other.team != COLOR_TEAM2))
-               {
-                       // return flag
-                       Send_KillNotification (other.netname, self.netname, "", INFO_RETURNFLAG, MSG_INFO);
-                       //bprint(other.netname, "^7 returned the ", self.netname, "\n");
-
-                       // punish the player who last had it
-                       FOR_EACH_PLAYER(player)
-                               if(player.playerid == self.dropperid)
-                               {
-                                       PlayerScore_Add(player, SP_SCORE, -ctf_score_value("penalty_returned"));
-                                       ctf_captureshield_update(player, 0); // shield only
-                               }
-
-                       // punish the team who was last carrying it
-                       if(self.team == COLOR_TEAM1)
-                               TeamScore_AddToTeam(COLOR_TEAM2, ST_SCORE, -ctf_score_value("penalty_returned"));
-                       else
-                               TeamScore_AddToTeam(COLOR_TEAM1, ST_SCORE, -ctf_score_value("penalty_returned"));
-
-                       // reward the player who returned it
-                       if(other.playerid == self.playerid) // is this the guy who killed the FC last?
-                       {
-                               if (other.team == COLOR_TEAM1 || other.team == COLOR_TEAM2)
-                                       UpdateFrags(other, ctf_score_value("score_return_by_killer"));
-                               else
-                                       UpdateFrags(other, ctf_score_value("score_return_rogue_by_killer"));
-                       }
-                       else
-                       {
-                               if (other.team == COLOR_TEAM1 || other.team == COLOR_TEAM2)
-                                       UpdateFrags(other, ctf_score_value("score_return"));
-                               else
-                                       UpdateFrags(other, ctf_score_value("score_return_rogue"));
-                       }
-                       PlayerScore_Add(other, SP_CTF_RETURNS, 1);
-                       LogCTF("return", self.team, other);
-                       sound (other, CH_TRIGGER, self.noise1, VOL_BASE, ATTN_NONE);
-                       ReturnFlag(self);
-               }
-               else if (!other.flagcarried && (other.playerid != self.dropperid || time > self.ctf_droptime + autocvar_g_balance_ctf_delay_collect))
-               {
-                       if(self.waypointsprite_attachedforcarrier)
-                               WaypointSprite_DetachCarrier(self);
-
-                       if (autocvar_g_ctf_flag_pickup_effects) // field pickup effect
-                               pointparticles(particleeffectnum("smoke_ring"), 0.5 * (self.absmin + self.absmax), '0 0 0', 1);
-
-                       // pick up
-                       self.solid = SOLID_NOT;
-                       setorigin(self, self.origin); // relink
-                       self.owner = other;
-                       other.flagcarried = self;
-                       self.cnt = FLAG_CARRY;
-                       Send_KillNotification (other.netname, self.netname, "", INFO_PICKUPFLAG, MSG_INFO);
-                       //bprint(other.netname, "^7 picked up the ", self.netname, "\n");
-
-                       float f;
-                       f = bound(0, (self.pain_finished - time) / autocvar_g_ctf_flag_returntime, 1);
-                       //print("factor is ", ftos(f), "\n");
-                       f = ctf_score_value("score_pickup_dropped_late") * (1-f)
-                         + ctf_score_value("score_pickup_dropped_early") * f;
-                       f = floor(f + 0.5);
-                       self.dropperid = other.playerid;
-                       //print("score is ", ftos(f), "\n");
-
-                       UpdateFrags(other, f);
-                       PlayerScore_Add(other, SP_CTF_PICKUPS, 1);
-                       LogCTF("pickup", self.team, other);
-                       sound (other, CH_TRIGGER, self.noise, VOL_BASE, ATTN_NONE);
-
-                       FOR_EACH_PLAYER(player)
-                               if(player.team == self.team)
-                                       centerprint(player, "The enemy got your flag! Retrieve it!");
-
-                       self.movetype = MOVETYPE_NONE;  // flag must have MOVETYPE_NONE here, otherwise it will drop through the floor...
-                       setorigin(self, FLAG_CARRY_POS);
-                       setattachment(self, other, "");
-                       self.damageforcescale = 0;
-                       self.takedamage = DAMAGE_NO;
-                       WaypointSprite_AttachCarrier("flagcarrier", other, RADARICON_FLAGCARRIER, '1 1 0');
-               }
-       }
-}
-
-/*QUAKED spawnfunc_info_player_team1 (1 0 0) (-16 -16 -24) (16 16 24)
-CTF Starting point for a player
-in team one (Red).
-
-Keys:
-"angle"
- viewing angle when spawning
-*/
-void spawnfunc_info_player_team1()
-{
-       if(g_assault)
-       {
-               remove(self);
-               return;
-       }
-       self.team = COLOR_TEAM1; // red
-       spawnfunc_info_player_deathmatch();
-}
-//self.team = 4;self.classname = "info_player_start";spawnfunc_info_player_start();}
-
-/*QUAKED spawnfunc_info_player_team2 (1 0 0) (-16 -16 -24) (16 16 24)
-CTF Starting point for a player in
-team two (Blue).
-
-Keys:
-"angle"
- viewing angle when spawning
-*/
-void spawnfunc_info_player_team2()
-{
-       if(g_assault)
-       {
-               remove(self);
-               return;
-       }
-       self.team = COLOR_TEAM2; // blue
-       spawnfunc_info_player_deathmatch();
-}
-//self.team = 13;self.classname = "info_player_start";spawnfunc_info_player_start();}
-
-/*QUAKED spawnfunc_info_player_team3 (1 0 0) (-16 -16 -24) (16 16 24)
-CTF Starting point for a player in
-team three (Yellow).
-
-Keys:
-"angle"
- viewing angle when spawning
-*/
-void spawnfunc_info_player_team3()
-{
-       if(g_assault)
-       {
-               remove(self);
-               return;
-       }
-       self.team = COLOR_TEAM3; // yellow
-       spawnfunc_info_player_deathmatch();
-}
-
-
-/*QUAKED spawnfunc_info_player_team4 (1 0 0) (-16 -16 -24) (16 16 24)
-CTF Starting point for a player in
-team four (Magenta).
-
-Keys:
-"angle"
- viewing angle when spawning
-*/
-void spawnfunc_info_player_team4()
-{
-       if(g_assault)
-       {
-               remove(self);
-               return;
-       }
-       self.team = COLOR_TEAM4; // purple
-       spawnfunc_info_player_deathmatch();
-}
-
-void item_flag_reset()
-{
-       DropFlag(self, world, world);
-       if(self.waypointsprite_attachedforcarrier)
-               WaypointSprite_DetachCarrier(self);
-       ReturnFlag(self);
-}
-
-void item_flag_postspawn()
-{ // Check CTF Item Flag Post Spawn
-
-       // Flag Glow Trail Support
-       if(autocvar_g_ctf_flag_glowtrails)
-       { // Provide Flag Glow Trail
-               if(self.team == COLOR_TEAM1)
-                       // Red
-                       self.glow_color = 251;
-               else
-               if(self.team == COLOR_TEAM2)
-                       // Blue
-                       self.glow_color = 210;
-
-               self.glow_size = 25;
-               self.glow_trail = 1;
-       }
-}
-
-/*QUAKED spawnfunc_item_flag_team1 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
-CTF flag for team one (Red).
-Multiple are allowed.
-
-Keys:
-"angle"
- Angle the flag will point
-(minus 90 degrees)
-"model"
- model to use, note this needs red and blue as skins 0 and 1
- (default models/ctf/flag.md3)
-"noise"
- sound played when flag is picked up
- (default ctf/take.wav)
-"noise1"
- sound played when flag is returned by a teammate
- (default ctf/return.wav)
-"noise2"
- sound played when flag is captured
- (default ctf/redcapture.wav)
-"noise3"
- sound played when flag is lost in the field and respawns itself
- (default ctf/respawn.wav)
-*/
-
-void spawnfunc_item_flag_team2();
-void spawnfunc_item_flag_team1()
-{
-       if (!g_ctf)
-       {
-               remove(self);
-               return;
-       }
-
-       if (g_ctf_reverse)
-       {
-               float old_g_ctf_reverse = g_ctf_reverse;
-               g_ctf_reverse = 0; // avoid an endless loop
-               spawnfunc_item_flag_team2();
-               g_ctf_reverse = old_g_ctf_reverse;
-               return;
-       }
-
-       // link flag into ctf_worldflaglist
-       self.ctf_worldflagnext = ctf_worldflaglist;
-       ctf_worldflaglist = self;
-
-       self.classname = "item_flag_team";
-       self.team = COLOR_TEAM1; // color 4 team (red)
-       self.items = IT_KEY2; // gold key (redish enough)
-       self.netname = "^1RED^7 flag";
-       self.target = "###item###";
-       self.skin = autocvar_g_ctf_flag_red_skin;
-       if(self.spawnflags & 1)
-               self.noalign = 1;
-       if (!self.model)
-               self.model = autocvar_g_ctf_flag_red_model;
-       if (!self.noise)
-               self.noise = "ctf/red_taken.wav";
-       if (!self.noise1)
-               self.noise1 = "ctf/red_returned.wav";
-       if (!self.noise2)
-               self.noise2 = "ctf/red_capture.wav"; // blue team scores by capturing the red flag
-       if (!self.noise3)
-               self.noise3 = "ctf/flag_respawn.wav";
-       if (!self.noise4)
-               self.noise4 = "ctf/red_dropped.wav";
-       precache_model (self.model);
-       setmodel (self, self.model); // precision set below
-       precache_sound (self.noise);
-       precache_sound (self.noise1);
-       precache_sound (self.noise2);
-       precache_sound (self.noise3);
-       precache_sound (self.noise4);
-       //setsize(self, '-16 -16 -37', '16 16 37');
-       setsize(self, FLAG_MIN, FLAG_MAX);
-       setorigin(self, self.origin + '0 0 37');
-       self.nextthink = time + 0.2; // start after doors etc
-       self.think = place_flag;
-
-       if(!self.scale)
-               self.scale = 0.6;
-       //if(!self.glow_size)
-       //      self.glow_size = 50;
-
-       self.effects = self.effects | EF_LOWPRECISION;
-       if(autocvar_g_ctf_fullbrightflags)
-               self.effects |= EF_FULLBRIGHT;
-       if(autocvar_g_ctf_dynamiclights)
-               self.effects |= EF_RED;
-
-       // From Spidflisk
-       item_flag_postspawn();
-
-       precache_model("models/ctf/shield.md3");
-       precache_model("models/ctf/shockwavetransring.md3");
-
-       self.reset = item_flag_reset;
-}
-
-/*QUAKED spawnfunc_item_flag_team2 (0 0.5 0.8) (-48 -48 -24) (48 48 64)
-CTF flag for team two (Blue).
-Multiple are allowed.
-
-Keys:
-"angle"
- Angle the flag will point
-(minus 90 degrees)
-"model"
- model to use, note this needs red and blue as skins 0 and 1
- (default models/ctf/flag.md3)
-"noise"
- sound played when flag is picked up
- (default ctf/take.wav)
-"noise1"
- sound played when flag is returned by a teammate
- (default ctf/return.wav)
-"noise2"
- sound played when flag is captured
- (default ctf/bluecapture.wav)
-"noise3"
- sound played when flag is lost in the field and respawns itself
- (default ctf/respawn.wav)
-*/
-
-void spawnfunc_item_flag_team2()
-{
-       if (!g_ctf)
-       {
-               remove(self);
-               return;
-       }
-
-       if (g_ctf_reverse)
-       {
-               float old_g_ctf_reverse = g_ctf_reverse;
-               g_ctf_reverse = 0; // avoid an endless loop
-               spawnfunc_item_flag_team1();
-               g_ctf_reverse = old_g_ctf_reverse;
-               return;
-       }
-
-       // link flag into ctf_worldflaglist
-       self.ctf_worldflagnext = ctf_worldflaglist;
-       ctf_worldflaglist = self;
-
-       self.classname = "item_flag_team";
-       self.team = COLOR_TEAM2; // color 13 team (blue)
-       self.items = IT_KEY1; // silver key (bluish enough)
-       self.netname = "^4BLUE^7 flag";
-       self.target = "###item###";
-       self.skin = autocvar_g_ctf_flag_blue_skin;
-       if(self.spawnflags & 1)
-               self.noalign = 1;
-       if (!self.model)
-               self.model = autocvar_g_ctf_flag_blue_model;
-       if (!self.noise)
-               self.noise = "ctf/blue_taken.wav";
-       if (!self.noise1)
-               self.noise1 = "ctf/blue_returned.wav";
-       if (!self.noise2)
-               self.noise2 = "ctf/blue_capture.wav"; // blue team scores by capturing the red flag
-       if (!self.noise3)
-               self.noise3 = "ctf/flag_respawn.wav";
-       if (!self.noise4)
-               self.noise4 = "ctf/blue_dropped.wav";
-       precache_model (self.model);
-       setmodel (self, self.model); // precision set below
-       precache_sound (self.noise);
-       precache_sound (self.noise1);
-       precache_sound (self.noise2);
-       precache_sound (self.noise3);
-       precache_sound (self.noise4);
-       //setsize(self, '-16 -16 -37', '16 16 37');
-       setsize(self, FLAG_MIN, FLAG_MAX);
-       setorigin(self, self.origin + '0 0 37');
-       self.nextthink = time + 0.2; // start after doors etc
-       self.think = place_flag;
-
-       if(!self.scale)
-               self.scale = 0.6;
-       //if(!self.glow_size)
-       //      self.glow_size = 50;
-
-       self.effects = self.effects | EF_LOWPRECISION;
-       if(autocvar_g_ctf_fullbrightflags)
-               self.effects |= EF_FULLBRIGHT;
-       if(autocvar_g_ctf_dynamiclights)
-               self.effects |= EF_BLUE;
-
-       // From Spidflisk
-       item_flag_postspawn();
-
-       precache_model("models/ctf/shield.md3");
-       precache_model("models/ctf/shockwavetransring.md3");
-
-       self.reset = item_flag_reset;
-}
-
-
-/*QUAKED spawnfunc_ctf_team (0 .5 .8) (-16 -16 -24) (16 16 32)
-Team declaration for CTF gameplay, this allows you to decide what team
-names and control point models are used in your map.
-
-Note: If you use spawnfunc_ctf_team entities you must define at least 2!  However, unlike
-domination, you don't need to make a blank one too.
-
-Keys:
-"netname"
- Name of the team (for example Red, Blue, Green, Yellow, Life, Death, Offense, Defense, etc)
-"cnt"
- Scoreboard color of the team (for example 4 is red and 13 is blue)
-
-*/
-
-void spawnfunc_ctf_team()
-{
-       if (!g_ctf)
-       {
-               remove(self);
-               return;
-       }
-       self.classname = "ctf_team";
-       self.team = self.cnt + 1;
-}
-
-// code from here on is just to support maps that don't have control point and team entities
-void ctf_spawnteam (string teamname, float teamcolor)
-{
-       entity oldself;
-       oldself = self;
-       self = spawn();
-       self.classname = "ctf_team";
-       self.netname = teamname;
-       self.cnt = teamcolor;
-
-       spawnfunc_ctf_team();
-
-       self = oldself;
-}
-
-// spawn some default teams if the map is not set up for ctf
-void ctf_spawnteams()
-{
-       float numteams;
-
-       numteams = 2;//cvar("g_ctf_default_teams");
-
-       ctf_spawnteam("Red", COLOR_TEAM1 - 1);
-       ctf_spawnteam("Blue", COLOR_TEAM2 - 1);
-}
-
-void ctf_delayedinit()
-{
-       // if no teams are found, spawn defaults
-       if (find(world, classname, "ctf_team") == world)
-               ctf_spawnteams();
-
-       ScoreRules_ctf();
-}
-
-void ctf_init()
-{
-       InitializeEntity(world, ctf_delayedinit, INITPRIO_GAMETYPE);
-       flagcaptimerecord = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time")));
-
-       captureshield_min_negscore = autocvar_g_ctf_shield_min_negscore;
-       captureshield_max_ratio = autocvar_g_ctf_shield_max_ratio;
-       captureshield_force = autocvar_g_ctf_shield_force;
-}
-
-void ctf_setstatus2(entity flag, float shift)
-{
-       if (flag.cnt == FLAG_CARRY)
-               if (flag.owner == self)
-                       self.items |= shift * 3;
-               else
-                       self.items |= shift * 1;
-       else if (flag.cnt == FLAG_DROPPED)
-               self.items |= shift * 2;
-       else
-       {
-               // no status bits
-       }
-}
-
-void ctf_setstatus()
-{
-       self.items &~= IT_RED_FLAG_TAKEN;
-       self.items &~= IT_RED_FLAG_LOST;
-       self.items &~= IT_BLUE_FLAG_TAKEN;
-       self.items &~= IT_BLUE_FLAG_LOST;
-       self.items &~= IT_CTF_SHIELDED;
-
-       entity flag;
-       float redflags, blueflags;
-
-       if(self.ctf_captureshielded)
-               self.items |= IT_CTF_SHIELDED;
-
-       redflags = 0;
-       blueflags = 0;
-
-       for (flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) if(flag.cnt != FLAG_BASE)
-       {
-               if(flag.items & IT_KEY2) // blue
-                       ++redflags;
-               else if(flag.items & IT_KEY1) // red
-                       ++blueflags;
-       }
-
-       // blinking magic: if there is more than one flag, show one of these in a clever way
-       if(redflags)
-               redflags = mod(floor(time * redflags * 0.75), redflags);
-       if(blueflags)
-               blueflags = mod(floor(time * blueflags * 0.75), blueflags);
-
-       for (flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) if(flag.cnt != FLAG_BASE)
-       {
-               if(flag.items & IT_KEY2) // blue
-               {
-                       if(--redflags == -1) // happens exactly once (redflags is in 0..count-1, and will --'ed count times)
-                               ctf_setstatus2(flag, IT_RED_FLAG_TAKEN);
-               }
-               else if(flag.items & IT_KEY1) // red
-               {
-                       if(--blueflags == -1) // happens exactly once
-                               ctf_setstatus2(flag, IT_BLUE_FLAG_TAKEN);
-               }
-       }
-}
-/*
-entity ctf_team_has_commander(float cteam)
-{
-       entity pl;
-       if(cteam != COLOR_TEAM1 || cteam != COLOR_TEAM2)
-               return world;
-
-       FOR_EACH_REALPLAYER(pl) {
-               if(pl.team == cteam && pl.iscommander) {
-                       return pl;
-               }
-       }
-       return world;
-}
-
-void ctf_setstate(entity e, float st)
-{
-       e.ctf_state = st;
-       ++e.version;
-}
-
-void ctf_new_commander(float cteam)
-{
-       entity pl, plmax;
-
-       plmax = world;
-       FOR_EACH_REALPLAYER(pl) {
-               if(pl.team == cteam) {
-                       if(pl.iscommander) { // don't reassign if alreay there
-                               return;
-                       }
-                       if(plmax == world || plmax.frags < pl.frags) <<<<<<<<<<<<<<<<< BROKEN in new scoring system
-                               plmax = pl;
-               }
-       }
-       if(plmax == world) {
-               bprint(strcat(ColoredTeamName(cteam), " Team has no Commander!\n"));
-               return;
-       }
-
-       plmax.iscommander = TRUE;
-       ctf_setstate(plmax, 3);
-       sprint(plmax, "^3You're the commander now!\n");
-       centerprint(plmax, "^3You're the commander now!\n");
-}
-
-void ctf_clientconnect()
-{
-       self.iscommander = FALSE;
-
-       if(!self.team || self.classname != "player") {
-               ctf_setstate(self, -1);
-       } else
-               ctf_setstate(self, 0);
-
-       self.team_saved = self.team;
-
-       if(self.team != 0 && self.classname == "player" && !ctf_team_has_commander(self.team)) {
-               ctf_new_commander(self.team);
-       }
-}
-
-void ctf_playerchanged()
-{
-       if(!self.team || self.classname != "player") {
-               ctf_setstate(self, -1);
-       } else if(self.ctf_state < 0 && self.classname == "player") {
-               ctf_setstate(self, 0);
-       }
-
-       if(self.iscommander &&
-          (self.classname != "player" || self.team != self.team_saved)
-               )
-       {
-               self.iscommander = FALSE;
-               if(self.classname == "player")
-                       ctf_setstate(self, 0);
-               else
-                       ctf_setstate(self, -1);
-               ctf_new_commander(self.team_saved);
-       }
-
-       self.team_saved = self.team;
-
-       ctf_new_commander(self.team);
-}
-
-void ctf_clientdisconnect()
-{
-       if(self.iscommander)
-       {
-               ctf_new_commander(self.team);
-       }
-}
-
-entity GetPlayer(string);
-float ctf_clientcommand()
-{
-       entity e;
-       if(argv(0) == "order") {
-               if(!g_ctf) {
-                       sprint(self, "This command is not supported in this gamemode.\n");
-                       return TRUE;
-               }
-               if(!self.iscommander) {
-                       sprint(self, "^1You are not the commander!\n");
-                       return TRUE;
-               }
-               if(argv(2) == "") {
-                       sprint(self, "Usage: order #player status   - (playernumber as in status)\n");
-                       return TRUE;
-               }
-               e = GetPlayer(argv(1));
-               if(e == world) {
-                       sprint(self, "Invalid player.\nUsage: order #player status   - (playernumber as in status)\n");
-                       return TRUE;
-               }
-               if(e.team != self.team) {
-                       sprint(self, "^3You can only give orders to your own team!\n");
-                       return TRUE;
-               }
-               if(argv(2) == "attack") {
-                       sprint(self, strcat("Ordering ", e.netname, " to attack!\n"));
-                       sprint(e, "^1Attack!\n");
-                       centerprint(e, "^7You've been ordered to^9\n^1Attack!\n");
-                       ctf_setstate(e, 1);
-               } else if(argv(2) == "defend") {
-                       sprint(self, strcat("Ordering ", e.netname, " to defend!\n"));
-                       sprint(e, "^Defend!\n");
-                       centerprint(e, "^7You've been ordered to^9\n^2Defend!\n");
-                       ctf_setstate(e, 2);
-               } else {
-                       sprint(self, "^7Invalid command, use ^3attack^7, or ^3defend^7.\n");
-               }
-               return TRUE;
-       }
-       return FALSE;
-}
-*/
index 3956033698326bb8eae4b18aefc83d3a4eeba29d..7e4a07b5f6c5565642ecaf7c57f4a0b95bb72a20 100644 (file)
@@ -16,14 +16,10 @@ noref float require_spawnfunc_prefix; // if this float exists, only functions wi
 
 // Globals
 
-float ctf_score_value(string parameter);
-
 float g_cloaked, g_footsteps, g_jump_grunt, g_grappling_hook, g_midair, g_minstagib, g_pinata, g_norecoil, g_minstagib_invis_alpha, g_bloodloss;
 float g_warmup_limit;
 float g_warmup_allguns;
 float g_warmup_allow_timeout;
-float g_ctf_ignore_frags;
-float g_ctf_reverse;
 float g_race_qualifying;
 float inWarmupStage;
 float g_pickup_respawntime_weapon;
@@ -202,6 +198,9 @@ void setanim(entity e, vector anim, float looping, float override, float restart
 .float watersound_finished;
 .float iscreature;
 .float damagedbycontents;
+.float damagedbytriggers;
+.float pushable;
+.float teleportable;
 .vector oldvelocity;
 
 .float pauseregen_finished;
@@ -278,6 +277,7 @@ float blockSpectators; //if set, new or existing spectators or observers will be
 .float spectatortime; //point in time since the client is spectating or observing
 void checkSpectatorBlock();
 
+float game_completion_ratio; // 0 at start, 1 near end
 .float winning;
 .float jointime; // time of joining
 .float alivetime; // time of being alive
@@ -343,7 +343,6 @@ string gamemode_name;
 
 float startitem_failed;
 
-void DropFlag(entity flag, entity penalty_receiver, entity attacker);
 void DropAllRunes(entity pl);
 
 
@@ -570,7 +569,6 @@ float servertime, serverprevtime, serverframetime;
 
 string matchid;
 .float hitplotfh;
-.string noise4;
 
 .float last_pickup;
 
index 9e56023cb500ebb5425d08321fbff54d55d4a7e8..0a2fe02b66fcf13c88c02240c0fb5b653f31ed88 100644 (file)
@@ -395,196 +395,6 @@ void dom_controlpoint_setup()
 
 
 
-// player has joined game, get him on a team
-// depreciated
-/*void dom_player_join_team(entity pl)
-{
-       entity head;
-       float c1, c2, c3, c4, totalteams, smallestteam, smallestteam_count, selectedteam;
-       float balance_teams, force_balance, balance_type;
-
-       balance_teams = autocvar_g_balance_teams;
-       balance_teams = autocvar_g_balance_teams_force;
-
-       c1 = c2 = c3 = c4 = -1;
-       totalteams = 0;
-
-       // first find out what teams are allowed
-       head = find(world, classname, "dom_team");
-       while(head)
-       {
-               if(head.netname != "")
-               {
-                       //if(head.team == pl.team)
-                       //      selected = head;
-                       if(head.team == COLOR_TEAM1)
-                       {
-                                       c1 = 0;
-                       }
-                       if(head.team == COLOR_TEAM2)
-                       {
-                                       c2 = 0;
-                       }
-                       if(head.team == COLOR_TEAM3)
-                       {
-                                       c3 = 0;
-                       }
-                       if(head.team == COLOR_TEAM4)
-                       {
-                                       c4 = 0;
-                       }
-               }
-               head = find(head, classname, "dom_team");
-       }
-
-       // make sure there are at least 2 teams to join
-       if(c1 >= 0)
-               totalteams = totalteams + 1;
-       if(c2 >= 0)
-               totalteams = totalteams + 1;
-       if(c3 >= 0)
-               totalteams = totalteams + 1;
-       if(c4 >= 0)
-               totalteams = totalteams + 1;
-
-       if(totalteams <= 1)
-               error("dom_player_join_team: Too few teams available for domination\n");
-
-       // whichever teams that are available are set to 0 instead of -1
-
-       // if we don't care what team he ends up on, put him on whatever team he entered as.
-       // if he's not on a valid team, then put him on the smallest team
-       if(!balance_teams && !force_balance)
-       {
-               if(     c1 >= 0 && pl.team == COLOR_TEAM1)
-                       selectedteam = pl.team;
-               else if(c2 >= 0 && pl.team == COLOR_TEAM2)
-                       selectedteam = pl.team;
-               else if(c3 >= 0 && pl.team == COLOR_TEAM3)
-                       selectedteam = pl.team;
-               else if(c4 >= 0 && pl.team == COLOR_TEAM4)
-                       selectedteam = pl.team;
-               else
-                       selectedteam = -1;
-               if(selectedteam > 0)
-               {
-                       SetPlayerColors(pl, selectedteam - 1);
-                       return;
-               }
-               // otherwise end up on the smallest team (handled below)
-       }
-
-       // now count how many players are on each team already
-
-       head = find(world, classname, "player");
-       while(head)
-       {
-               //if(head.netname != "")
-               {
-                       if(head.team == COLOR_TEAM1)
-                       {
-                               if(c1 >= 0)
-                                       c1 = c1 + 1;
-                       }
-                       if(head.team == COLOR_TEAM2)
-                       {
-                               if(c2 >= 0)
-                                       c2 = c2 + 1;
-                       }
-                       if(head.team == COLOR_TEAM3)
-                       {
-                               if(c3 >= 0)
-                                       c3 = c3 + 1;
-                       }
-                       if(head.team == COLOR_TEAM4)
-                       {
-                               if(c4 >= 0)
-                                       c4 = c4 + 1;
-                       }
-               }
-               head = find(head, classname, "player");
-       }
-
-       // c1...c4 now have counts of each team
-       // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
-
-       smallestteam = 0;
-       smallestteam_count = 999;
-
-       // 2 gives priority to what team you're already on, 1 goes in order
-       balance_type = 1;
-
-       if(balance_type == 1)
-       {
-               if(c1 >= 0 && c1 < smallestteam_count)
-               {
-                       smallestteam = 1;
-                       smallestteam_count = c1;
-               }
-               if(c2 >= 0 && c2 < smallestteam_count)
-               {
-                       smallestteam = 2;
-                       smallestteam_count = c2;
-               }
-               if(c3 >= 0 && c3 < smallestteam_count)
-               {
-                       smallestteam = 3;
-                       smallestteam_count = c3;
-               }
-               if(c4 >= 0 && c4 < smallestteam_count)
-               {
-                       smallestteam = 4;
-                       smallestteam_count = c4;
-               }
-       }
-       else
-       {
-               if(c1 >= 0 && (c1 < smallestteam_count ||
-                                       (c1 == smallestteam_count && self.team == COLOR_TEAM1) ) )
-               {
-                       smallestteam = 1;
-                       smallestteam_count = c1;
-               }
-               if(c2 >= 0 && c2 < (c2 < smallestteam_count ||
-                                       (c2 == smallestteam_count && self.team == COLOR_TEAM2) ) )
-               {
-                       smallestteam = 2;
-                       smallestteam_count = c2;
-               }
-               if(c3 >= 0 && c3 < (c3 < smallestteam_count ||
-                                       (c3 == smallestteam_count && self.team == COLOR_TEAM3) ) )
-               {
-                       smallestteam = 3;
-                       smallestteam_count = c3;
-               }
-               if(c4 >= 0 && c4 < (c4 < smallestteam_count ||
-                                       (c4 == smallestteam_count && self.team == COLOR_TEAM4) ) )
-               {
-                       smallestteam = 4;
-                       smallestteam_count = c4;
-               }
-       }
-
-       if(smallestteam == 1)
-       {
-               selectedteam = COLOR_TEAM1 - 1;
-       }
-       if(smallestteam == 2)
-       {
-               selectedteam = COLOR_TEAM2 - 1;
-       }
-       if(smallestteam == 3)
-       {
-               selectedteam = COLOR_TEAM3 - 1;
-       }
-       if(smallestteam == 4)
-       {
-               selectedteam = COLOR_TEAM4 - 1;
-       }
-
-       SetPlayerColors(pl, selectedteam);
-}
-*/
 /*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32)
 Control point for Domination gameplay.
 */
index ac7c3cd2a7ceb637b4a69d5f2529a110a454a456..d2cc61db3346cb3405ca086e9d025657fd2a4c6f 100644 (file)
@@ -55,6 +55,7 @@ float damage_headshotbonus; // bonus multiplier for head shots, set to 0 after u
 .float teamkill_soundtime;
 .entity teamkill_soundsource;
 .entity pusher;
+.float istypefrag;
 .float taunt_soundtime;
 
 
@@ -218,11 +219,6 @@ void GiveFrags (entity attacker, entity targ, float f, float deathtype)
                        }
                        f = 0;
                }
-               else if(g_ctf)
-               {
-                       if(g_ctf_ignore_frags)
-                               f = 0;
-               }
        }
 
        attacker.totalfrags += f;
@@ -420,7 +416,7 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype)
                                        PlayerStats_Event(targ, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, 1);
                                }
 
-                               if(targ.BUTTON_CHAT) {
+                               if(targ.istypefrag) {
                                        Send_CSQC_KillCenterprint(attacker, s, Obituary_ExtraFragInfo(targ), KILL_TYPEFRAG, MSG_KILL);
                                        Send_CSQC_KillCenterprint(targ, a, Obituary_ExtraFragInfo(attacker), KILL_TYPEFRAGGED, MSG_KILL);
                                } else {
@@ -442,14 +438,7 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype)
 
                                Send_KillNotification(a, s, msg, deathtype, MSG_KILL);
 
-                               if(g_ctf && targ.flagcarried)
-                               {
-                                       UpdateFrags(attacker, ctf_score_value("score_kill"));
-                                       PlayerScore_Add(attacker, SP_CTF_FCKILLS, 1);
-                                       GiveFrags(attacker, targ, 0, deathtype); // for logging
-                               }
-                               else
-                                       GiveFrags(attacker, targ, 1, deathtype);
+                               GiveFrags(attacker, targ, 1, deathtype);
 
                                if (targ.killcount > 2) {
                                        Send_KillNotification(s, ftos(targ.killcount), a, KILL_END_SPREE, MSG_SPREE);
@@ -457,10 +446,7 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype)
 
                                attacker.killcount = attacker.killcount + 1;
 
-                               if (attacker.killcount > 2) {
-                                       Send_KillNotification(a, ftos(attacker.killcount), "", KILL_SPREE, MSG_SPREE);
-                               }
-                               else if (attacker.killcount == 3)
+                               if (attacker.killcount == 3)
                                {
                                        Send_KillNotification(a, "", "", KILL_SPREE_3, MSG_SPREE);
                                        AnnounceTo(attacker, "03kills");
@@ -502,6 +488,9 @@ void Obituary (entity attacker, entity inflictor, entity targ, float deathtype)
                                        AnnounceTo(attacker, "30kills");
                                        PlayerStats_Event(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_30, 1);
                                }
+                               else if (attacker.killcount > 2) {
+                                       Send_KillNotification(a, ftos(attacker.killcount), "", KILL_SPREE, MSG_SPREE);
+                               }
                                LogDeath("frag", deathtype, attacker, targ);
                        }
                }
@@ -647,11 +636,11 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float
 
                                                        if(autocvar_g_mirrordamage_virtual)
                                                        {
-                                                               vector v = healtharmor_applydamage(attacker.armorvalue, autocvar_g_balance_armor_blockpercent, mirrordamage);
+                                                               vector v  = healtharmor_applydamage(attacker.armorvalue, autocvar_g_balance_armor_blockpercent, mirrordamage);
                                                                attacker.dmg_take += v_x;
                                                                attacker.dmg_save += v_y;
                                                                attacker.dmg_inflictor = inflictor;
-                                                               mirrordamage = 0;
+                                                               mirrordamage = v_z; // = 0, to make fteqcc stfu
                                                                mirrorforce = 0;
                                                        }
 
@@ -763,15 +752,6 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, float
                                damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself
                }
 
-               // CTF: reduce damage/force
-               if(g_ctf)
-               if(targ == attacker)
-               if(targ.flagcarried)
-               {
-                       damage = damage * autocvar_g_ctf_flagcarrier_selfdamage;
-                       force = force * autocvar_g_ctf_flagcarrier_selfforce;
-               }
-
                if(g_runematch)
                {
                        // apply strength rune
@@ -1068,10 +1048,7 @@ float RadiusDamage (entity inflictor, entity attacker, float coredamage, float e
                                                myblastorigin = WarpZone_TransformOrigin(targ, blastorigin);
 
                                                // if it's a player, use the view origin as reference
-                                               if (targ.classname == "player")
-                                                       center = targ.origin + targ.view_ofs;
-                                               else
-                                                       center = targ.origin + (targ.mins + targ.maxs) * 0.5;
+                                               center = CENTER_OR_VIEWOFS(targ);
 
                                                force = normalize(center - myblastorigin);
                                                force = force * (finaldmg / coredamage) * forceintensity;
index 370f2fb9878041cea1bac0cd540d82d2c450a231..8867d0725b0f7c3a6cea40d022c5e133a01291dc 100644 (file)
@@ -213,6 +213,7 @@ void GrapplingHookThink()
                                                self.aiment.flags &~= FL_ONGROUND;
                                                self.aiment.pusher = self.realowner;
                                                self.aiment.pushltime = time + autocvar_g_maxpushtime;
+                                               self.aiment.istypefrag = self.aiment.BUTTON_CHAT;
                                        }
                                }
 
@@ -285,6 +286,7 @@ void GrapplingHook_Damage (entity inflictor, entity attacker, float damage, floa
                {
                        self.realowner.pusher = attacker;
                        self.realowner.pushltime = time + autocvar_g_maxpushtime;
+                       self.realowner.istypefrag = self.realowner.BUTTON_CHAT;
                }
                RemoveGrapplingHook(self.realowner);
        }
index df63a70bb85d4f01578823b1b4ea3ceaba48f8ce..b07cae2714e19770ca08d721d26fd0cec4885575 100644 (file)
@@ -799,7 +799,15 @@ void SetBrushEntityModel()
        if(self.model != "")
        {
                precache_model(self.model);
-               setmodel(self, self.model); // no precision needed
+               if(self.mins != '0 0 0' || self.maxs != '0 0 0')
+               {
+                       vector mi = self.mins;
+                       vector ma = self.maxs;
+                       setmodel(self, self.model); // no precision needed
+                       setsize(self, mi, ma);
+               }
+               else
+                       setmodel(self, self.model); // no precision needed
                InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
        }
        setorigin(self, self.origin);
@@ -811,7 +819,15 @@ void SetBrushEntityModelNoLOD()
        if(self.model != "")
        {
                precache_model(self.model);
-               setmodel(self, self.model); // no precision needed
+               if(self.mins != '0 0 0' || self.maxs != '0 0 0')
+               {
+                       vector mi = self.mins;
+                       vector ma = self.maxs;
+                       setmodel(self, self.model); // no precision needed
+                       setsize(self, mi, ma);
+               }
+               else
+                       setmodel(self, self.model); // no precision needed
        }
        setorigin(self, self.origin);
        ApplyMinMaxScaleAngles(self);
index af7c105617769463262f0759e1b392a2463e8abe..20ddc655170cfbc483ff0b41fe22ccec1e2c2284 100644 (file)
@@ -466,16 +466,19 @@ void trigger_hurt_touch()
                        Damage (other, self, own, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
                }
        }
+       else if(other.damagedbytriggers)
+       {
+               if(other.takedamage)
+               {
+                       EXACTTRIGGER_TOUCH;
+                       Damage(other, self, self, self.dmg, DEATH_HURTTRIGGER, other.origin, '0 0 0');
+               }
+       }
        else
        {
                if (!other.owner)
                {
-                       if (other.items & IT_KEY1 || other.items & IT_KEY2)     // reset flag
-                       {
-                               EXACTTRIGGER_TOUCH;
-                               other.pain_finished = min(other.pain_finished, time + 2);
-                       }
-                       else if (other.classname == "rune")                     // reset runes
+                       if (other.classname == "rune")                  // reset runes
                        {
                                EXACTTRIGGER_TOUCH;
                                other.nextthink = min(other.nextthink, time + 1);
@@ -545,6 +548,7 @@ void trigger_heal_touch()
        if (other.iscreature)
        {
                if (other.takedamage)
+               if (!other.deadflag)
                if (other.triggerhealtime < time)
                {
                        EXACTTRIGGER_TOUCH;
index cbe31ec1c09f5189f39c79b3ff2d17a503879c4e..fb211aaeb7a52653402f11d8f9345887d07cf2a2 100644 (file)
@@ -1,3 +1,7 @@
+#define LATENCY_THINKRATE 10
+.float latency_sum;
+.float latency_cnt;
+.float latency_time;
 entity pingplreport;
 void PingPLReport_Think()
 {
@@ -18,6 +22,15 @@ void PingPLReport_Think()
                WriteShort(MSG_BROADCAST, max(1, e.ping));
                WriteByte(MSG_BROADCAST, ceil(e.ping_packetloss * 255));
                WriteByte(MSG_BROADCAST, ceil(e.ping_movementloss * 255));
+
+               // record latency times for clients throughout the match so we can report it to playerstats
+               if(time > (e.latency_time + LATENCY_THINKRATE))
+               {
+                       e.latency_sum += e.ping;
+                       e.latency_cnt += 1;
+                       e.latency_time = time;
+                       //print("sum: ", ftos(e.latency_sum), ", cnt: ", ftos(e.latency_cnt), ", avg: ", ftos(e.latency_sum / e.latency_cnt), ".\n");
+               }
        }
        else
        {
@@ -295,9 +308,8 @@ void cvar_changes_init()
                BADCVAR("g_balance_kill_delay");
                BADCVAR("g_ca_point_leadlimit");
                BADCVAR("g_ctf_captimerecord_always");
-               BADCVAR("g_ctf_flag_capture_effects");
                BADCVAR("g_ctf_flag_glowtrails");
-               BADCVAR("g_ctf_flag_pickup_effects");
+               BADCVAR("g_ctf_flag_pickup_verbosename");
                BADCVAR("g_domination_point_leadlimit");
                BADCVAR("g_forced_respawn");
                BADCVAR("g_keyhunt_point_leadlimit");
@@ -348,7 +360,8 @@ void cvar_changes_init()
                BADCVAR("gametype");
                BADCVAR("g_antilag");
                BADCVAR("g_balance_teams");
-               BADCVAR("g_balance_teams_force");
+               BADCVAR("g_balance_teams_prevent_imbalance");
+               BADCVAR("g_balance_teams_scorefactor");
                BADCVAR("g_ban_sync_trusted_servers");
                BADCVAR("g_ban_sync_uri");
                BADCVAR("g_ctf_ignore_frags");
@@ -538,8 +551,8 @@ void spawnfunc___init_dedicated_server(void)
        self.classname = "worldspawn"; // safeguard against various stuff ;)
 
        // needs to be done so early because of the constants they create
-       RegisterWeapons();
-       RegisterGametypes();
+       CALL_ACCUMULATED_FUNCTION(RegisterWeapons);
+       CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
 
        MapInfo_Enumerate();
        MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
@@ -547,7 +560,6 @@ void spawnfunc___init_dedicated_server(void)
 
 void Map_MarkAsRecent(string m);
 float world_already_spawned;
-void RegisterWeapons();
 void Nagger_Init();
 void Item_ItemsTime_Init();
 void ClientInit_Spawn();
@@ -586,8 +598,8 @@ void spawnfunc_worldspawn (void)
        }
 
        // needs to be done so early because of the constants they create
-       RegisterWeapons();
-       RegisterGametypes();
+       CALL_ACCUMULATED_FUNCTION(RegisterWeapons);
+       CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
 
        ServerProgsDB = db_load(strcat("server.db", autocvar_sessionid));
 
@@ -639,6 +651,8 @@ void spawnfunc_worldspawn (void)
 
        Map_MarkAsRecent(mapname);
 
+       PlayerStats_Init(); // we need this to be initiated before InitGameplayMode
+
        precache_model ("null"); // we need this one before InitGameplayMode
        InitGameplayMode();
        readlevelcvars();
@@ -924,8 +938,6 @@ void spawnfunc_worldspawn (void)
                cvar_set("sv_curl_serverpackages", substring(s, 1, -1));
        }
 
-       PlayerStats_Init();
-
        // MOD AUTHORS: change this, and possibly remove a few of the blocks below to ignore certain changes
        modname = "Xonotic";
        // physics/balance/config changes that count as mod
@@ -1992,6 +2004,9 @@ float WinningCondition_Scores(float limit, float leadlimit)
                        limitreached = (limitreached || leadlimitreached);
        }
 
+       if(limit)
+               game_completion_ratio = max(game_completion_ratio, bound(0, WinningConditionHelper_topscore / limit, 1));
+
        return GetWinningCode(
                WinningConditionHelper_topscore && limitreached,
                WinningConditionHelper_equality
@@ -2180,6 +2195,11 @@ void CheckRules_World()
        float wantovertime;
        wantovertime = 0;
 
+       if(timelimit > game_starttime)
+               game_completion_ratio = (time - game_starttime) / (timelimit - game_starttime);
+       else
+               game_completion_ratio = 0;
+
        if(checkrules_suddendeathend)
        {
                if(!checkrules_suddendeathwarning)
index 25b42821ed3621735722e23194374d4922396458..20828800deebfdd3d7d0407269179ec0037489a8 100644 (file)
@@ -67,6 +67,16 @@ float DistributeEvenly_Get(float weight)
     DistributeEvenly_amount -= f;
     return f;
 }
+float DistributeEvenly_GetRandomized(float weight)
+{
+    float f;
+    if (weight <= 0)
+        return 0;
+    f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
+    DistributeEvenly_totalweight -= weight;
+    DistributeEvenly_amount -= f;
+    return f;
+}
 
 #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
 
@@ -85,9 +95,12 @@ string STR_OBSERVER = "observer";
 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
+#define FOR_EACH_SPEC(v) FOR_EACH_CLIENT(v) if(v.classname != STR_PLAYER)
 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
 #endif
 
+#define CENTER_OR_VIEWOFS(ent) (ent.origin + ((ent.classname == STR_PLAYER) ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5)))
+
 // copies a string to a tempstring (so one can strunzone it)
 string strcat1(string s) = #115; // FRIK_FILE
 
@@ -1119,7 +1132,9 @@ void readlevelcvars(void)
                if(cvar("g_rocket_flying"))
                        MUTATOR_ADD(mutator_rocketflying);
                if(cvar("g_vampire"))
-                       MUTATOR_ADD(mutator_vampire);
+                       MUTATOR_ADD(mutator_vampire);           
+               if(cvar("g_superspectate"))
+                       MUTATOR_ADD(mutator_superspec);
        }
 
        // is this a mutator? is this a mode?
@@ -1170,7 +1185,6 @@ void readlevelcvars(void)
        g_bloodloss = cvar("g_bloodloss");
        sv_maxidle = cvar("sv_maxidle");
        sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
-       g_ctf_reverse = cvar("g_ctf_reverse");
        sv_autotaunt = cvar("sv_autotaunt");
        sv_taunt = cvar("sv_taunt");
 
@@ -2785,6 +2799,8 @@ float isPushable(entity e)
 {
        if(e.iscreature)
                return TRUE;
+       if(e.pushable)
+               return TRUE;
        switch(e.classname)
        {
                case "body":
diff --git a/qcsrc/server/mode_onslaught.qc b/qcsrc/server/mode_onslaught.qc
deleted file mode 100644 (file)
index ebca6ee..0000000
+++ /dev/null
@@ -1,1431 +0,0 @@
-void onslaught_generator_updatesprite(entity e);
-void onslaught_controlpoint_updatesprite(entity e);
-void onslaught_link_checkupdate();
-
-.entity sprite;
-.string target2;
-.float iscaptured;
-.float islinked;
-.float isgenneighbor_red;
-.float isgenneighbor_blue;
-.float iscpneighbor_red;
-.float iscpneighbor_blue;
-.float isshielded;
-.float lasthealth;
-.float lastteam;
-.float lastshielded;
-.float lastcaptured;
-
-.string model1, model2, model3;
-
-void ons_gib_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
-{
-       self.velocity = self.velocity + vforce;
-}
-
-.float giblifetime;
-void ons_throwgib_think()
-{
-       float d;
-
-       self.nextthink = time + 0.05;
-
-       d = self.giblifetime - time;
-
-       if(d<0)
-       {
-               self.think = SUB_Remove;
-               return;
-       }
-       if(d<1)
-               self.alpha = d;
-
-       if(d>2)
-       if(random()<0.6)
-               pointparticles(particleeffectnum("onslaught_generator_gib_flame"), self.origin, '0 0 0', 1);
-}
-
-void ons_throwgib(vector v_from, vector v_to, string smodel, float f_lifetime, float b_burn)
-{
-       entity gib;
-
-       gib = spawn();
-
-       setmodel(gib, smodel);
-       setorigin(gib, v_from);
-       gib.solid = SOLID_BBOX;
-       gib.movetype = MOVETYPE_BOUNCE;
-       gib.takedamage = DAMAGE_YES;
-       gib.event_damage = ons_gib_damage;
-       gib.health = -1;
-       gib.effects = EF_LOWPRECISION;
-       gib.flags = FL_NOTARGET;
-       gib.velocity = v_to;
-       gib.giblifetime = time + f_lifetime;
-
-       if (b_burn)
-       {
-               gib.think = ons_throwgib_think;
-               gib.nextthink = time + 0.05;
-       }
-       else
-               SUB_SetFade(gib, gib.giblifetime, 2);
-}
-
-void onslaught_updatelinks()
-{
-       entity l, links;
-       float stop, t1, t2, t3, t4;
-       // first check if the game has ended
-       dprint("--- updatelinks ---\n");
-       links = findchain(classname, "onslaught_link");
-       // mark generators as being shielded and networked
-       l = findchain(classname, "onslaught_generator");
-       while (l)
-       {
-               if (l.iscaptured)
-                       dprint(etos(l), " (generator) belongs to team ", ftos(l.team), "\n");
-               else
-                       dprint(etos(l), " (generator) is destroyed\n");
-               l.islinked = l.iscaptured;
-               l.isshielded = l.iscaptured;
-               l = l.chain;
-       }
-       // mark points as shielded and not networked
-       l = findchain(classname, "onslaught_controlpoint");
-       while (l)
-       {
-               l.islinked = FALSE;
-               l.isshielded = TRUE;
-               l.isgenneighbor_red = FALSE;
-               l.isgenneighbor_blue = FALSE;
-               l.iscpneighbor_red = FALSE;
-               l.iscpneighbor_blue = FALSE;
-               dprint(etos(l), " (point) belongs to team ", ftos(l.team), "\n");
-               l = l.chain;
-       }
-       // flow power outward from the generators through the network
-       l = links;
-       while (l)
-       {
-               dprint(etos(l), " (link) connects ", etos(l.goalentity), " with ", etos(l.enemy), "\n");
-               l = l.chain;
-       }
-       stop = FALSE;
-       while (!stop)
-       {
-               stop = TRUE;
-               l = links;
-               while (l)
-               {
-                       // if both points are captured by the same team, and only one of
-                       // them is powered, mark the other one as powered as well
-                       if (l.enemy.iscaptured && l.goalentity.iscaptured)
-                               if (l.enemy.islinked != l.goalentity.islinked)
-                                       if (l.enemy.team == l.goalentity.team)
-                                       {
-                                               if (!l.goalentity.islinked)
-                                               {
-                                                       stop = FALSE;
-                                                       l.goalentity.islinked = TRUE;
-                                                       dprint(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)\n");
-                                               }
-                                               else if (!l.enemy.islinked)
-                                               {
-                                                       stop = FALSE;
-                                                       l.enemy.islinked = TRUE;
-                                                       dprint(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)\n");
-                                               }
-                                       }
-                       l = l.chain;
-               }
-       }
-       // now that we know which points are powered we can mark their neighbors
-       // as unshielded if team differs
-       l = links;
-       while (l)
-       {
-               if (l.goalentity.islinked)
-               {
-                       if (l.goalentity.team != l.enemy.team)
-                       {
-                               dprint(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)\n");
-                               l.enemy.isshielded = FALSE;
-                       }
-                       if(l.goalentity.classname == "onslaught_generator")
-                       {
-                               if(l.goalentity.team == COLOR_TEAM1)
-                                       l.enemy.isgenneighbor_red = TRUE;
-                               else if(l.goalentity.team == COLOR_TEAM2)
-                                       l.enemy.isgenneighbor_blue = TRUE;
-                       }
-                       else
-                       {
-                               if(l.goalentity.team == COLOR_TEAM1)
-                                       l.enemy.iscpneighbor_red = TRUE;
-                               else if(l.goalentity.team == COLOR_TEAM2)
-                                       l.enemy.iscpneighbor_blue = TRUE;
-                       }
-               }
-               if (l.enemy.islinked)
-               {
-                       if (l.goalentity.team != l.enemy.team)
-                       {
-                               dprint(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)\n");
-                               l.goalentity.isshielded = FALSE;
-                       }
-                       if(l.enemy.classname == "onslaught_generator")
-                       {
-                               if(l.enemy.team == COLOR_TEAM1)
-                                       l.goalentity.isgenneighbor_red = TRUE;
-                               else if(l.enemy.team == COLOR_TEAM2)
-                                       l.goalentity.isgenneighbor_blue = TRUE;
-                       }
-                       else
-                       {
-                               if(l.enemy.team == COLOR_TEAM1)
-                                       l.goalentity.iscpneighbor_red = TRUE;
-                               else if(l.enemy.team == COLOR_TEAM2)
-                                       l.goalentity.iscpneighbor_blue = TRUE;
-                       }
-               }
-               l = l.chain;
-       }
-       // now update the takedamage and alpha variables on generator shields
-       l = findchain(classname, "onslaught_generator");
-       while (l)
-       {
-               if (l.isshielded)
-               {
-                       dprint(etos(l), " (generator) is shielded\n");
-                       l.enemy.alpha = 1;
-                       l.takedamage = DAMAGE_NO;
-                       l.bot_attack = FALSE;
-               }
-               else
-               {
-                       dprint(etos(l), " (generator) is not shielded\n");
-                       l.enemy.alpha = -1;
-                       l.takedamage = DAMAGE_AIM;
-                       l.bot_attack = TRUE;
-               }
-               l = l.chain;
-       }
-       // now update the takedamage and alpha variables on control point icons
-       l = findchain(classname, "onslaught_controlpoint");
-       while (l)
-       {
-               if (l.isshielded)
-               {
-                       dprint(etos(l), " (point) is shielded\n");
-                       l.enemy.alpha = 1;
-                       if (l.goalentity)
-                       {
-                               l.goalentity.takedamage = DAMAGE_NO;
-                               l.goalentity.bot_attack = FALSE;
-                       }
-               }
-               else
-               {
-                       dprint(etos(l), " (point) is not shielded\n");
-                       l.enemy.alpha = -1;
-                       if (l.goalentity)
-                       {
-                               l.goalentity.takedamage = DAMAGE_AIM;
-                               l.goalentity.bot_attack = TRUE;
-                       }
-               }
-               onslaught_controlpoint_updatesprite(l);
-               l = l.chain;
-       }
-       // count generators owned by each team
-       t1 = t2 = t3 = t4 = 0;
-       l = findchain(classname, "onslaught_generator");
-       while (l)
-       {
-               if (l.iscaptured)
-               {
-                       if (l.team == COLOR_TEAM1) t1 = 1;
-                       if (l.team == COLOR_TEAM2) t2 = 1;
-                       if (l.team == COLOR_TEAM3) t3 = 1;
-                       if (l.team == COLOR_TEAM4) t4 = 1;
-               }
-               onslaught_generator_updatesprite(l);
-               l = l.chain;
-       }
-       // see if multiple teams remain (if not, it's game over)
-       if (t1 + t2 + t3 + t4 < 2)
-               dprint("--- game over ---\n");
-       else
-               dprint("--- done updating links ---\n");
-}
-
-float onslaught_controlpoint_can_be_linked(entity cp, float t)
-{
-       if(t == COLOR_TEAM1)
-       {
-               if(cp.isgenneighbor_red)
-                       return 2;
-               if(cp.iscpneighbor_red)
-                       return 1;
-       }
-       else if(t == COLOR_TEAM2)
-       {
-               if(cp.isgenneighbor_blue)
-                       return 2;
-               if(cp.iscpneighbor_blue)
-                       return 1;
-       }
-       return 0;
-       /*
-          entity e;
-       // check to see if this player has a legitimate claim to capture this
-       // control point - more specifically that there is a captured path of
-       // points leading back to the team generator
-       e = findchain(classname, "onslaught_link");
-       while (e)
-       {
-       if (e.goalentity == cp)
-       {
-       dprint(etos(e), " (link) connects to ", etos(e.enemy), " (point)");
-       if (e.enemy.islinked)
-       {
-       dprint(" which is linked");
-       if (e.enemy.team == t)
-       {
-       dprint(" and has the correct team!\n");
-       return 1;
-       }
-       else
-       dprint(" but has the wrong team\n");
-       }
-       else
-       dprint("\n");
-       }
-       else if (e.enemy == cp)
-       {
-       dprint(etos(e), " (link) connects to ", etos(e.goalentity), " (point)");
-       if (e.goalentity.islinked)
-       {
-       dprint(" which is linked");
-       if (e.goalentity.team == t)
-       {
-       dprint(" and has a team!\n");
-       return 1;
-       }
-       else
-       dprint(" but has the wrong team\n");
-       }
-       else
-       dprint("\n");
-       }
-       e = e.chain;
-       }
-       return 0;
-        */
-}
-
-float onslaught_controlpoint_attackable(entity cp, float t)
-       // -2: SAME TEAM, attackable by enemy!
-       // -1: SAME TEAM!
-       // 0: off limits
-       // 1: attack it
-       // 2: touch it
-       // 3: attack it (HIGH PRIO)
-       // 4: touch it (HIGH PRIO)
-{
-       float a;
-
-       if(cp.isshielded)
-       {
-               return 0;
-       }
-       else if(cp.goalentity)
-       {
-               // if there's already an icon built, nothing happens
-               if(cp.team == t)
-               {
-                       a = onslaught_controlpoint_can_be_linked(cp, COLOR_TEAM1 + COLOR_TEAM2 - t);
-                       if(a) // attackable by enemy?
-                               return -2; // EMERGENCY!
-                       return -1;
-               }
-               // we know it can be linked, so no need to check
-               // but...
-               a = onslaught_controlpoint_can_be_linked(cp, t);
-               if(a == 2) // near our generator?
-                       return 3; // EMERGENCY!
-               return 1;
-       }
-       else
-       {
-               // free point
-               if(onslaught_controlpoint_can_be_linked(cp, t))
-               {
-                       a = onslaught_controlpoint_can_be_linked(cp, COLOR_TEAM1 + COLOR_TEAM2 - t);
-                       if(a == 2)
-                               return 4; // GET THIS ONE NOW!
-                       else
-                               return 2; // TOUCH ME
-               }
-       }
-       return 0;
-}
-
-float overtime_msg_time;
-void onslaught_generator_think()
-{
-       float d;
-       entity e;
-       self.nextthink = ceil(time + 1);
-       if (!gameover)
-       {
-               if (autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60)
-               {
-                       if (!overtime_msg_time)
-                       {
-                               FOR_EACH_PLAYER(e)
-                                       centerprint(e, "^3Now playing ^1OVERTIME^3!\n^3Generators start now to self-damaging.\n^3The more control points your team holds,\n^3the more damage the enemy generator gets.");
-                               overtime_msg_time = time;
-                       }
-                       // self.max_health / 300 gives 5 minutes of overtime.
-                       // control points reduce the overtime duration.
-                       sound(self, CH_TRIGGER, "onslaught/generator_decay.wav", VOL_BASE, ATTN_NORM);
-                       d = 1;
-                       e = findchain(classname, "onslaught_controlpoint");
-                       while (e)
-                       {
-                               if (e.team != self.team)
-                                       if (e.islinked)
-                                               d = d + 1;
-                               e = e.chain;
-                       }
-                       if(autocvar_g_campaign && autocvar__campaign_testrun)
-                               d = d * self.max_health;
-                       else
-                               d = d * self.max_health / max(30, 60 * autocvar_timelimit_suddendeath);
-                       Damage(self, self, self, d, DEATH_HURTTRIGGER, self.origin, '0 0 0');
-               }
-               else if (overtime_msg_time)
-                       overtime_msg_time = 0;
-       }
-}
-
-void onslaught_generator_ring_spawn(vector org)
-{
-       modeleffect_spawn("models/onslaught/shockwavetransring.md3", 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, -16, 0.1, 1.25, 0.25);
-}
-
-void onslaught_generator_ray_think()
-{
-       self.nextthink = time + 0.05;
-       if(self.count > 10)
-       {
-               self.think = SUB_Remove;
-               return;
-       }
-
-       if(self.count > 5)
-               self.alpha -= 0.1;
-       else
-               self.alpha += 0.1;
-
-       self.scale += 0.2;
-       self.count +=1;
-}
-
-void onslaught_generator_ray_spawn(vector org)
-{
-       entity e;
-       e = spawn();
-       setmodel(e, "models/onslaught/ons_ray.md3");
-       setorigin(e, org);
-       e.angles = randomvec() * 360;
-       e.alpha = 0;
-       e.scale = random() * 5 + 8;
-       e.think = onslaught_generator_ray_think;
-       e.nextthink = time + 0.05;
-}
-
-void onslaught_generator_shockwave_spawn(vector org)
-{
-       shockwave_spawn("models/onslaught/shockwave.md3", org, -64, 0.75, 0.5);
-}
-
-void onslaught_generator_damage_think()
-{
-       if(self.owner.health < 0)
-       {
-               self.think = SUB_Remove;
-               return;
-       }
-       self.nextthink = time+0.1;
-
-       // damaged fx (less probable the more damaged is the generator)
-       if(random() < 0.9 - self.owner.health / self.owner.max_health)
-               if(random() < 0.01)
-               {
-                       pointparticles(particleeffectnum("electro_ballexplode"), self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1);
-                       sound(self, CH_TRIGGER, "onslaught/electricity_explode.wav", VOL_BASE, ATTN_NORM);
-               }
-               else
-                       pointparticles(particleeffectnum("torch_small"), self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1);
-}
-
-void onslaught_generator_damage_spawn(entity gd_owner)
-{
-       entity e;
-       e = spawn();
-       e.owner = gd_owner;
-       e.health = self.owner.health;
-       setorigin(e, gd_owner.origin);
-       e.think = onslaught_generator_damage_think;
-       e.nextthink = time+1;
-}
-
-void onslaught_generator_deaththink()
-{
-       vector org;
-       float i;
-
-       if not (self.count)
-               self.count = 40;
-
-       // White shockwave
-       if(self.count==40||self.count==20)
-       {
-               onslaught_generator_ring_spawn(self.origin);
-               sound(self, CH_TRIGGER, "onslaught/shockwave.wav", VOL_BASE, ATTN_NORM);
-       }
-
-       // Throw some gibs
-       if(random() < 0.3)
-       {
-               i = random();
-               if(i < 0.3)
-                       ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 11 + '0 0 20', "models/onslaught/gen_gib1.md3", 6, TRUE);
-               else if(i > 0.7)
-                       ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 12 + '0 0 20', "models/onslaught/gen_gib2.md3", 6, TRUE);
-               else
-                       ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 13 + '0 0 20', "models/onslaught/gen_gib3.md3", 6, TRUE);
-       }
-
-       // Spawn fire balls
-       for(i=0;i < 10;++i)
-       {
-               org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20');
-               pointparticles(particleeffectnum("onslaught_generator_gib_explode"), org, '0 0 0', 1);
-       }
-
-       // Short explosion sound + small explosion
-       if(random() < 0.25)
-       {
-               te_explosion(self.origin);
-               sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
-       }
-
-       // Particles
-       org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8');
-       pointparticles(particleeffectnum("onslaught_generator_smallexplosion"), org, '0 0 0', 1);
-
-       // rays
-       if(random() > 0.25 )
-       {
-               onslaught_generator_ray_spawn(self.origin);
-       }
-
-       // Final explosion
-       if(self.count==1)
-       {
-               org = self.origin;
-               te_explosion(org);
-               onslaught_generator_shockwave_spawn(org);
-               pointparticles(particleeffectnum("onslaught_generator_finalexplosion"), org, '0 0 0', 1);
-               sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
-       }
-       else
-               self.nextthink = time + 0.05;
-
-       self.count = self.count - 1;
-}
-
-void onslaught_generator_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-{
-       float i;
-       if (damage <= 0)
-               return;
-       if(inWarmupStage)
-               return;
-       if (attacker != self)
-       {
-               if (self.isshielded)
-               {
-                       // this is protected by a shield, so ignore the damage
-                       if (time > self.pain_finished)
-                               if (attacker.classname == "player")
-                               {
-                                       play2(attacker, "onslaught/damageblockedbyshield.wav");
-                                       self.pain_finished = time + 1;
-                               }
-                       return;
-               }
-               if (time > self.pain_finished)
-               {
-                       self.pain_finished = time + 10;
-                       bprint(ColoredTeamName(self.team), " generator under attack!\n");
-                       play2team(self.team, "onslaught/generator_underattack.wav");
-               }
-       }
-       self.health = self.health - damage;
-       WaypointSprite_UpdateHealth(self.sprite, self.health);
-       // choose an animation frame based on health
-       self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1);
-       // see if the generator is still functional, or dying
-       if (self.health > 0)
-       {
-#ifdef ONSLAUGHT_SPAM
-               float h, lh;
-               lh = ceil(self.lasthealth / 100) * 100;
-               h = ceil(self.health / 100) * 100;
-               if(lh != h)
-                       bprint(ColoredTeamName(self.team), " generator has less than ", ftos(h), " health remaining\n");
-#endif
-               self.lasthealth = self.health;
-       }
-       else if not(inWarmupStage)
-       {
-               if (attacker == self)
-                       bprint(ColoredTeamName(self.team), " generator spontaneously exploded due to overtime!\n");
-               else
-               {
-                       string t;
-                       t = ColoredTeamName(attacker.team);
-                       bprint(ColoredTeamName(self.team), " generator destroyed by ", t, "!\n");
-               }
-               self.iscaptured = FALSE;
-               self.islinked = FALSE;
-               self.isshielded = FALSE;
-               self.takedamage = DAMAGE_NO; // can't be hurt anymore
-               self.event_damage = SUB_Null; // won't do anything if hurt
-               self.count = 0; // reset counter
-               self.think = onslaught_generator_deaththink; // explosion sequence
-               self.nextthink = time; // start exploding immediately
-               self.think(); // do the first explosion now
-
-               WaypointSprite_UpdateMaxHealth(self.sprite, 0);
-
-               onslaught_updatelinks();
-       }
-
-       if(self.health <= 0)
-               setmodel(self, "models/onslaught/generator_dead.md3");
-       else if(self.health < self.max_health * 0.10)
-               setmodel(self, "models/onslaught/generator_dmg9.md3");
-       else if(self.health < self.max_health * 0.20)
-               setmodel(self, "models/onslaught/generator_dmg8.md3");
-       else if(self.health < self.max_health * 0.30)
-               setmodel(self, "models/onslaught/generator_dmg7.md3");
-       else if(self.health < self.max_health * 0.40)
-               setmodel(self, "models/onslaught/generator_dmg6.md3");
-       else if(self.health < self.max_health * 0.50)
-               setmodel(self, "models/onslaught/generator_dmg5.md3");
-       else if(self.health < self.max_health * 0.60)
-               setmodel(self, "models/onslaught/generator_dmg4.md3");
-       else if(self.health < self.max_health * 0.70)
-               setmodel(self, "models/onslaught/generator_dmg3.md3");
-       else if(self.health < self.max_health * 0.80)
-               setmodel(self, "models/onslaught/generator_dmg2.md3");
-       else if(self.health < self.max_health * 0.90)
-               setmodel(self, "models/onslaught/generator_dmg1.md3");
-       setsize(self, '-52 -52 -14', '52 52 75');
-
-       // Throw some flaming gibs on damage, more damage = more chance for gib
-       if(random() < damage/220)
-       {
-               sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
-               i = random();
-               if(i < 0.3)
-                       ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib1.md3", 5, TRUE);
-               else if(i > 0.7)
-                       ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib2.md3", 5, TRUE);
-               else
-                       ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib3.md3", 5, TRUE);
-       }
-       else
-       {
-               // particles on every hit
-               pointparticles(particleeffectnum("sparks"), hitloc, force * -1, 1);
-
-               //sound on every hit
-               if (random() < 0.5)
-                       sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTN_NORM);
-               else
-                       sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTN_NORM);
-       }
-
-       //throw some gibs on damage
-       if(random() < damage/200+0.2)
-               if(random() < 0.5)
-                       ons_throwgib(hitloc + '0 0 20', randomvec()*360, "models/onslaught/gen_gib1.md3", 5, FALSE);
-}
-
-// update links after a delay
-void onslaught_generator_delayed()
-{
-       onslaught_updatelinks();
-       // now begin normal thinking
-       self.think = onslaught_generator_think;
-       self.nextthink = time;
-}
-
-string onslaught_generator_waypointsprite_for_team(entity e, float t)
-{
-       if(t == e.team)
-       {
-               if(e.team == COLOR_TEAM1)
-                       return "ons-gen-red";
-               else if(e.team == COLOR_TEAM2)
-                       return "ons-gen-blue";
-       }
-       if(e.isshielded)
-               return "ons-gen-shielded";
-       if(e.team == COLOR_TEAM1)
-               return "ons-gen-red";
-       else if(e.team == COLOR_TEAM2)
-               return "ons-gen-blue";
-       return "";
-}
-
-void onslaught_generator_updatesprite(entity e)
-{
-       string s1, s2, s3;
-       s1 = onslaught_generator_waypointsprite_for_team(e, COLOR_TEAM1);
-       s2 = onslaught_generator_waypointsprite_for_team(e, COLOR_TEAM2);
-       s3 = onslaught_generator_waypointsprite_for_team(e, -1);
-       WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);
-
-       if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded)
-       {
-               e.lastteam = e.team + 2;
-               e.lastshielded = e.isshielded;
-               if(e.lastshielded)
-               {
-                       if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)
-                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, FALSE));
-                       else
-                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5');
-               }
-               else
-               {
-                       if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)
-                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, FALSE));
-                       else
-                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75');
-               }
-               WaypointSprite_Ping(e.sprite);
-       }
-}
-
-string onslaught_controlpoint_waypointsprite_for_team(entity e, float t)
-{
-       float a;
-       if(t != -1)
-       {
-               a = onslaught_controlpoint_attackable(e, t);
-               if(a == 3 || a == 4) // ATTACK/TOUCH THIS ONE NOW
-               {
-                       if(e.team == COLOR_TEAM1)
-                               return "ons-cp-atck-red";
-                       else if(e.team == COLOR_TEAM2)
-                               return "ons-cp-atck-blue";
-                       else
-                               return "ons-cp-atck-neut";
-               }
-               else if(a == -2) // DEFEND THIS ONE NOW
-               {
-                       if(e.team == COLOR_TEAM1)
-                               return "ons-cp-dfnd-red";
-                       else if(e.team == COLOR_TEAM2)
-                               return "ons-cp-dfnd-blue";
-               }
-               else if(e.team == t || a == -1 || a == 1) // own point, or fire at it
-               {
-                       if(e.team == COLOR_TEAM1)
-                               return "ons-cp-red";
-                       else if(e.team == COLOR_TEAM2)
-                               return "ons-cp-blue";
-               }
-               else if(a == 2) // touch it
-                       return "ons-cp-neut";
-       }
-       else
-       {
-               if(e.team == COLOR_TEAM1)
-                       return "ons-cp-red";
-               else if(e.team == COLOR_TEAM2)
-                       return "ons-cp-blue";
-               else
-                       return "ons-cp-neut";
-       }
-       return "";
-}
-
-void onslaught_controlpoint_updatesprite(entity e)
-{
-       string s1, s2, s3;
-       s1 = onslaught_controlpoint_waypointsprite_for_team(e, COLOR_TEAM1);
-       s2 = onslaught_controlpoint_waypointsprite_for_team(e, COLOR_TEAM2);
-       s3 = onslaught_controlpoint_waypointsprite_for_team(e, -1);
-       WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);
-
-       float sh;
-       sh = !(onslaught_controlpoint_can_be_linked(e, COLOR_TEAM1) || onslaught_controlpoint_can_be_linked(e, COLOR_TEAM2));
-
-       if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured)
-       {
-               if(e.iscaptured) // don't mess up build bars!
-               {
-                       if(sh)
-                       {
-                               WaypointSprite_UpdateMaxHealth(e.sprite, 0);
-                       }
-                       else
-                       {
-                               WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health);
-                               WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health);
-                       }
-               }
-               if(e.lastshielded)
-               {
-                       if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)
-                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, FALSE));
-                       else
-                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5');
-               }
-               else
-               {
-                       if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)
-                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, FALSE));
-                       else
-                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75');
-               }
-               WaypointSprite_Ping(e.sprite);
-
-               e.lastteam = e.team + 2;
-               e.lastshielded = sh;
-               e.lastcaptured = e.iscaptured;
-       }
-}
-
-void onslaught_generator_reset()
-{
-       self.team = self.team_saved;
-       self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health;
-       self.takedamage = DAMAGE_AIM;
-       self.bot_attack = TRUE;
-       self.iscaptured = TRUE;
-       self.islinked = TRUE;
-       self.isshielded = TRUE;
-       self.enemy.solid = SOLID_NOT;
-       self.think = onslaught_generator_delayed;
-       self.nextthink = time + 0.2;
-       setmodel(self, "models/onslaught/generator.md3");
-       setsize(self, '-52 -52 -14', '52 52 75');
-
-       WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
-       WaypointSprite_UpdateHealth(self.sprite, self.health);
-}
-
-/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64)
-  Base generator.
-
-  spawnfunc_onslaught_link entities can target this.
-
-keys:
-"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET.
-"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
- */
-void spawnfunc_onslaught_generator()
-{
-       if (!g_onslaught)
-       {
-               remove(self);
-               return;
-       }
-
-       entity e;
-       precache_model("models/onslaught/generator.md3");
-       precache_model("models/onslaught/generator_shield.md3");
-       precache_model("models/onslaught/generator_dmg1.md3");
-       precache_model("models/onslaught/generator_dmg2.md3");
-       precache_model("models/onslaught/generator_dmg3.md3");
-       precache_model("models/onslaught/generator_dmg4.md3");
-       precache_model("models/onslaught/generator_dmg5.md3");
-       precache_model("models/onslaught/generator_dmg6.md3");
-       precache_model("models/onslaught/generator_dmg7.md3");
-       precache_model("models/onslaught/generator_dmg8.md3");
-       precache_model("models/onslaught/generator_dmg9.md3");
-       precache_model("models/onslaught/generator_dead.md3");
-       precache_model("models/onslaught/shockwave.md3");
-       precache_model("models/onslaught/shockwavetransring.md3");
-       precache_model("models/onslaught/gen_gib1.md3");
-       precache_model("models/onslaught/gen_gib2.md3");
-       precache_model("models/onslaught/gen_gib3.md3");
-       precache_model("models/onslaught/ons_ray.md3");
-       precache_sound("onslaught/generator_decay.wav");
-       precache_sound("weapons/grenade_impact.wav");
-       precache_sound("weapons/rocket_impact.wav");
-       precache_sound("onslaught/generator_underattack.wav");
-       precache_sound("onslaught/shockwave.wav");
-       precache_sound("onslaught/ons_hit1.wav");
-       precache_sound("onslaught/ons_hit2.wav");
-       precache_sound("onslaught/electricity_explode.wav");
-       if (!self.team)
-               objerror("team must be set");
-       self.team_saved = self.team;
-       self.colormap = 1024 + (self.team - 1) * 17;
-       self.solid = SOLID_BBOX;
-       self.movetype = MOVETYPE_NONE;
-       self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health;
-       setmodel(self, "models/onslaught/generator.md3");
-       setsize(self, '-52 -52 -14', '52 52 75');
-       setorigin(self, self.origin);
-       self.takedamage = DAMAGE_AIM;
-       self.bot_attack = TRUE;
-       self.event_damage = onslaught_generator_damage;
-       self.iscaptured = TRUE;
-       self.islinked = TRUE;
-       self.isshielded = TRUE;
-       // helper entity that create fx when generator is damaged
-       onslaught_generator_damage_spawn(self);
-       // spawn shield model which indicates whether this can be damaged
-       self.enemy = e = spawn();
-       e.classname = "onslaught_generator_shield";
-       e.solid = SOLID_NOT;
-       e.movetype = MOVETYPE_NONE;
-       e.effects = EF_ADDITIVE;
-       setmodel(e, "models/onslaught/generator_shield.md3");
-       setorigin(e, self.origin);
-       e.colormap = self.colormap;
-       e.team = self.team;
-       self.think = onslaught_generator_delayed;
-       self.nextthink = time + 0.2;
-       InitializeEntity(self, onslaught_generator_delayed, INITPRIO_LAST);
-
-       WaypointSprite_SpawnFixed(string_null, e.origin + '0 0 1' * e.maxs_z, self, sprite, RADARICON_NONE, '0 0 0');
-       WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY);
-       WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
-       WaypointSprite_UpdateHealth(self.sprite, self.health);
-
-       waypoint_spawnforitem(self);
-
-       onslaught_updatelinks();
-
-       self.reset = onslaught_generator_reset;
-}
-
-.float waslinked;
-.float cp_bob_spd;
-.vector cp_origin, cp_bob_origin, cp_bob_dmg;
-
-float ons_notification_time_team1;
-float ons_notification_time_team2;
-
-void onslaught_controlpoint_icon_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-{
-       entity oself;
-       float nag;
-
-       if (damage <= 0)
-               return;
-       if (self.owner.isshielded)
-       {
-               // this is protected by a shield, so ignore the damage
-               if (time > self.pain_finished)
-                       if (attacker.classname == "player")
-                       {
-                               play2(attacker, "onslaught/damageblockedbyshield.wav");
-                               self.pain_finished = time + 1;
-                       }
-               return;
-       }
-
-       if (attacker.classname == "player")
-       {
-               nag = FALSE;
-               if(self.team == COLOR_TEAM1)
-               {
-                       if(time - ons_notification_time_team1 > 10)
-                       {
-                               nag = TRUE;
-                               ons_notification_time_team1 = time;
-                       }
-               }
-               else if(self.team == COLOR_TEAM2)
-               {
-                       if(time - ons_notification_time_team2 > 10)
-                       {
-                               nag = TRUE;
-                               ons_notification_time_team2 = time;
-                       }
-               }
-               else
-                       nag = TRUE;
-
-               if(nag)
-                       play2team(self.team, "onslaught/controlpoint_underattack.wav");
-       }
-
-       self.health = self.health - damage;
-       if(self.owner.iscaptured)
-               WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
-       else
-               WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / sys_frametime));
-       self.pain_finished = time + 1;
-       self.punchangle = (2 * randomvec() - '1 1 1') * 45;
-       self.cp_bob_dmg_z = (2 * random() - 1) * 15;
-       // colormod flash when shot
-       self.colormod = '2 2 2';
-       // particles on every hit
-       pointparticles(particleeffectnum("sparks"), hitloc, force*-1, 1);
-       //sound on every hit
-       if (random() < 0.5)
-               sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTN_NORM);
-       else
-               sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTN_NORM);
-
-       if (self.health < 0)
-       {
-               sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
-               pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
-               {
-                       string t;
-                       t = ColoredTeamName(attacker.team);
-                       bprint(ColoredTeamName(self.team), " ", self.message, " control point destroyed by ", t, "\n");
-                       ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 25, "models/onslaught/controlpoint_icon_gib1.md3", 3, FALSE);
-                       ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE);
-                       ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE);
-                       ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
-                       ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
-                       ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
-                       ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
-               }
-               self.owner.goalentity = world;
-               self.owner.islinked = FALSE;
-               self.owner.iscaptured = FALSE;
-               self.owner.team = 0;
-               self.owner.colormap = 1024;
-
-               WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0);
-
-               onslaught_updatelinks();
-
-               // Use targets now (somebody make sure this is in the right place..)
-               oself = self;
-               self = self.owner;
-               activator = self;
-               SUB_UseTargets ();
-               self = oself;
-
-
-               self.owner.waslinked = self.owner.islinked;
-               if(self.owner.model != "models/onslaught/controlpoint_pad.md3")
-                       setmodel(self.owner, "models/onslaught/controlpoint_pad.md3");
-               //setsize(self, '-32 -32 0', '32 32 8');
-
-               remove(self);
-       }
-}
-
-void onslaught_controlpoint_icon_think()
-{
-       entity oself;
-       self.nextthink = time + sys_frametime;
-       if (time > self.pain_finished + 5)
-       {
-               if(self.health < self.max_health)
-               {
-                       self.health = self.health + self.count;
-                       if (self.health >= self.max_health)
-                               self.health = self.max_health;
-                       WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
-               }
-       }
-       if (self.health < self.max_health * 0.25)
-               setmodel(self, "models/onslaught/controlpoint_icon_dmg3.md3");
-       else if (self.health < self.max_health * 0.50)
-               setmodel(self, "models/onslaught/controlpoint_icon_dmg2.md3");
-       else if (self.health < self.max_health * 0.75)
-               setmodel(self, "models/onslaught/controlpoint_icon_dmg1.md3");
-       else if (self.health < self.max_health * 0.90)
-               setmodel(self, "models/onslaught/controlpoint_icon.md3");
-       // colormod flash when shot
-       self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));
-
-       if(self.owner.islinked != self.owner.waslinked)
-       {
-               // unteam the spawnpoint if needed
-               float t;
-               t = self.owner.team;
-               if(!self.owner.islinked)
-                       self.owner.team = 0;
-
-               oself = self;
-               self = self.owner;
-               activator = self;
-               SUB_UseTargets ();
-               self = oself;
-
-               self.owner.team = t;
-
-               self.owner.waslinked = self.owner.islinked;
-       }
-
-       if (self.punchangle_x > 0)
-       {
-               self.punchangle_x = self.punchangle_x - 60 * sys_frametime;
-               if (self.punchangle_x < 0)
-                       self.punchangle_x = 0;
-       }
-       else if (self.punchangle_x < 0)
-       {
-               self.punchangle_x = self.punchangle_x + 60 * sys_frametime;
-               if (self.punchangle_x > 0)
-                       self.punchangle_x = 0;
-       }
-
-       if (self.punchangle_y > 0)
-       {
-               self.punchangle_y = self.punchangle_y - 60 * sys_frametime;
-               if (self.punchangle_y < 0)
-                       self.punchangle_y = 0;
-       }
-       else if (self.punchangle_y < 0)
-       {
-               self.punchangle_y = self.punchangle_y + 60 * sys_frametime;
-               if (self.punchangle_y > 0)
-                       self.punchangle_y = 0;
-       }
-
-       if (self.punchangle_z > 0)
-       {
-               self.punchangle_z = self.punchangle_z - 60 * sys_frametime;
-               if (self.punchangle_z < 0)
-                       self.punchangle_z = 0;
-       }
-       else if (self.punchangle_z < 0)
-       {
-               self.punchangle_z = self.punchangle_z + 60 * sys_frametime;
-               if (self.punchangle_z > 0)
-                       self.punchangle_z = 0;
-       }
-
-       self.angles_x = self.punchangle_x;
-       self.angles_y = self.punchangle_y + self.mangle_y;
-       self.angles_z = self.punchangle_z;
-       self.mangle_y = self.mangle_y + 45 * sys_frametime;
-
-       self.cp_bob_origin_z = 4 * PI * (1 - cos(self.cp_bob_spd));
-       self.cp_bob_spd = self.cp_bob_spd + 1.875 * sys_frametime;
-       if(self.cp_bob_dmg_z > 0)
-               self.cp_bob_dmg_z = self.cp_bob_dmg_z - 3 * sys_frametime;
-       else
-               self.cp_bob_dmg_z = 0;
-       setorigin(self,self.cp_origin + self.cp_bob_origin + self.cp_bob_dmg);
-
-       // damaged fx
-       if(random() < 0.6 - self.health / self.max_health)
-       {
-               pointparticles(particleeffectnum("electricity_sparks"), self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);
-
-               if(random() > 0.8)
-                       sound(self, CH_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTN_NORM);
-               else if (random() > 0.5)
-                       sound(self, CH_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTN_NORM);
-       }
-}
-
-void onslaught_controlpoint_icon_buildthink()
-{
-       entity oself;
-       float a;
-
-       self.nextthink = time + sys_frametime;
-
-       // only do this if there is power
-       a = onslaught_controlpoint_can_be_linked(self.owner, self.owner.team);
-       if(!a)
-               return;
-
-       self.health = self.health + self.count;
-
-       if (self.health >= self.max_health)
-       {
-               self.health = self.max_health;
-               self.count = autocvar_g_onslaught_cp_regen * sys_frametime; // slow repair rate from now on
-               self.think = onslaught_controlpoint_icon_think;
-               sound(self, CH_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTN_NORM);
-               bprint(ColoredTeamName(self.team), " captured ", self.owner.message, " control point\n");
-               self.owner.iscaptured = TRUE;
-
-               WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health);
-               WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
-
-               onslaught_updatelinks();
-
-               // Use targets now (somebody make sure this is in the right place..)
-               oself = self;
-               self = self.owner;
-               activator = self;
-               SUB_UseTargets ();
-               self = oself;
-               self.cp_origin = self.origin;
-               self.cp_bob_origin = '0 0 0.1';
-               self.cp_bob_spd = 0;
-       }
-       self.alpha = self.health / self.max_health;
-       // colormod flash when shot
-       self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));
-       if(self.owner.model != "models/onslaught/controlpoint_pad2.md3")
-               setmodel(self.owner, "models/onslaught/controlpoint_pad2.md3");
-       //setsize(self, '-32 -32 0', '32 32 8');
-
-       if(random() < 0.9 - self.health / self.max_health)
-               pointparticles(particleeffectnum("rage"), self.origin + 10 * randomvec(), '0 0 -1', 1);
-}
-
-
-
-
-void onslaught_controlpoint_touch()
-{
-       entity e;
-       float a;
-       if (other.classname != "player")
-               return;
-       a = onslaught_controlpoint_attackable(self, other.team);
-       if(a != 2 && a != 4)
-               return;
-       // we've verified that this player has a legitimate claim to this point,
-       // so start building the captured point icon (which only captures this
-       // point if it successfully builds without being destroyed first)
-       self.goalentity = e = spawn();
-       e.classname = "onslaught_controlpoint_icon";
-       e.owner = self;
-       e.max_health = autocvar_g_onslaught_cp_health;
-       e.health = autocvar_g_onslaught_cp_buildhealth;
-       e.solid = SOLID_BBOX;
-       e.movetype = MOVETYPE_NONE;
-       setmodel(e, "models/onslaught/controlpoint_icon.md3");
-       setsize(e, '-32 -32 -32', '32 32 32');
-       setorigin(e, self.origin + '0 0 96');
-       e.takedamage = DAMAGE_AIM;
-       e.bot_attack = TRUE;
-       e.event_damage = onslaught_controlpoint_icon_damage;
-       e.team = other.team;
-       e.colormap = 1024 + (e.team - 1) * 17;
-       e.think = onslaught_controlpoint_icon_buildthink;
-       e.nextthink = time + sys_frametime;
-       e.count = (e.max_health - e.health) * sys_frametime / autocvar_g_onslaught_cp_buildtime; // how long it takes to build
-       sound(e, CH_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTN_NORM);
-       self.team = e.team;
-       self.colormap = e.colormap;
-       WaypointSprite_UpdateBuildFinished(self.sprite, time + (e.max_health - e.health) / (e.count / sys_frametime));
-       onslaught_updatelinks();
-}
-
-void onslaught_controlpoint_reset()
-{
-       if(self.goalentity && self.goalentity != world)
-               remove(self.goalentity);
-       self.goalentity = world;
-       self.team = 0;
-       self.colormap = 1024;
-       self.iscaptured = FALSE;
-       self.islinked = FALSE;
-       self.isshielded = TRUE;
-       self.enemy.solid = SOLID_NOT;
-       self.enemy.colormap = self.colormap;
-       self.think = self.enemy.think = SUB_Null;
-       self.nextthink = 0; // don't like SUB_Null :P
-       setmodel(self, "models/onslaught/controlpoint_pad.md3");
-       //setsize(self, '-32 -32 0', '32 32 8');
-
-       WaypointSprite_UpdateMaxHealth(self.sprite, 0);
-
-       onslaught_updatelinks();
-
-       activator = self;
-       SUB_UseTargets(); // to reset the structures, playerspawns etc.
-}
-
-/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128)
-  Control point. Be sure to give this enough clearance so that the shootable part has room to exist
-
-  This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.
-
-keys:
-"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
-"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities.
-"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc)
- */
-void spawnfunc_onslaught_controlpoint()
-{
-       entity e;
-       if (!g_onslaught)
-       {
-               remove(self);
-               return;
-       }
-       precache_model("models/onslaught/controlpoint_pad.md3");
-       precache_model("models/onslaught/controlpoint_pad2.md3");
-       precache_model("models/onslaught/controlpoint_shield.md3");
-       precache_model("models/onslaught/controlpoint_icon.md3");
-       precache_model("models/onslaught/controlpoint_icon_dmg1.md3");
-       precache_model("models/onslaught/controlpoint_icon_dmg2.md3");
-       precache_model("models/onslaught/controlpoint_icon_dmg3.md3");
-       precache_model("models/onslaught/controlpoint_icon_gib1.md3");
-       precache_model("models/onslaught/controlpoint_icon_gib2.md3");
-       precache_model("models/onslaught/controlpoint_icon_gib4.md3");
-       precache_sound("onslaught/controlpoint_build.wav");
-       precache_sound("onslaught/controlpoint_built.wav");
-       precache_sound("weapons/grenade_impact.wav");
-       precache_sound("onslaught/damageblockedbyshield.wav");
-       precache_sound("onslaught/controlpoint_underattack.wav");
-       precache_sound("onslaught/ons_spark1.wav");
-       precache_sound("onslaught/ons_spark2.wav");
-       self.solid = SOLID_BBOX;
-       self.movetype = MOVETYPE_NONE;
-       setmodel(self, "models/onslaught/controlpoint_pad.md3");
-       //setsize(self, '-32 -32 0', '32 32 8');
-       setorigin(self, self.origin);
-       self.touch = onslaught_controlpoint_touch;
-       self.team = 0;
-       self.colormap = 1024;
-       self.iscaptured = FALSE;
-       self.islinked = FALSE;
-       self.isshielded = TRUE;
-       // spawn shield model which indicates whether this can be damaged
-       self.enemy = e = spawn();
-       e.classname = "onslaught_controlpoint_shield";
-       e.solid = SOLID_NOT;
-       e.movetype = MOVETYPE_NONE;
-       e.effects = EF_ADDITIVE;
-       setmodel(e, "models/onslaught/controlpoint_shield.md3");
-       //setsize(e, '-32 -32 0', '32 32 128');
-       setorigin(e, self.origin);
-       e.colormap = self.colormap;
-
-       waypoint_spawnforitem(self);
-
-       WaypointSprite_SpawnFixed(string_null, e.origin + '0 0 1' * e.maxs_z, self, sprite, RADARICON_NONE, '0 0 0');
-       WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY);
-
-       onslaught_updatelinks();
-
-       self.reset = onslaught_controlpoint_reset;
-}
-
-float onslaught_link_send(entity to, float sendflags)
-{
-       WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK);
-       WriteByte(MSG_ENTITY, sendflags);
-       if(sendflags & 1)
-       {
-               WriteCoord(MSG_ENTITY, self.goalentity.origin_x);
-               WriteCoord(MSG_ENTITY, self.goalentity.origin_y);
-               WriteCoord(MSG_ENTITY, self.goalentity.origin_z);
-       }
-       if(sendflags & 2)
-       {
-               WriteCoord(MSG_ENTITY, self.enemy.origin_x);
-               WriteCoord(MSG_ENTITY, self.enemy.origin_y);
-               WriteCoord(MSG_ENTITY, self.enemy.origin_z);
-       }
-       if(sendflags & 4)
-       {
-               WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16
-       }
-       return TRUE;
-}
-
-void onslaught_link_checkupdate()
-{
-       // TODO check if the two sides have moved (currently they won't move anyway)
-       float redpower, bluepower;
-
-       redpower = bluepower = 0;
-       if(self.goalentity.islinked)
-       {
-               if(self.goalentity.team == COLOR_TEAM1)
-                       redpower = 1;
-               else if(self.goalentity.team == COLOR_TEAM2)
-                       bluepower = 1;
-       }
-       if(self.enemy.islinked)
-       {
-               if(self.enemy.team == COLOR_TEAM1)
-                       redpower = 2;
-               else if(self.enemy.team == COLOR_TEAM2)
-                       bluepower = 2;
-       }
-
-       float cc;
-       if(redpower == 1 && bluepower == 2)
-               cc = (COLOR_TEAM1 - 1) * 0x01 + (COLOR_TEAM2 - 1) * 0x10;
-       else if(redpower == 2 && bluepower == 1)
-               cc = (COLOR_TEAM1 - 1) * 0x10 + (COLOR_TEAM2 - 1) * 0x01;
-       else if(redpower)
-               cc = (COLOR_TEAM1 - 1) * 0x11;
-       else if(bluepower)
-               cc = (COLOR_TEAM2 - 1) * 0x11;
-       else
-               cc = 0;
-
-       //print(etos(self), " rp=", ftos(redpower), " bp=", ftos(bluepower), " ");
-       //print("cc=", ftos(cc), "\n");
-
-       if(cc != self.clientcolors)
-       {
-               self.clientcolors = cc;
-               self.SendFlags |= 4;
-       }
-
-       self.nextthink = time;
-}
-
-void onslaught_link_delayed()
-{
-       self.goalentity = find(world, targetname, self.target);
-       self.enemy = find(world, targetname, self.target2);
-       if (!self.goalentity)
-               objerror("can not find target\n");
-       if (!self.enemy)
-               objerror("can not find target2\n");
-       dprint(etos(self.goalentity), " linked with ", etos(self.enemy), "\n");
-       self.SendFlags |= 3;
-       self.think = onslaught_link_checkupdate;
-       self.nextthink = time;
-}
-
-/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16)
-  Link between control points.
-
-  This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams.
-
-keys:
-"target" - first control point.
-"target2" - second control point.
- */
-void spawnfunc_onslaught_link()
-{
-       if (!g_onslaught)
-       {
-               remove(self);
-               return;
-       }
-       if (self.target == "" || self.target2 == "")
-               objerror("target and target2 must be set\n");
-       InitializeEntity(self, onslaught_link_delayed, INITPRIO_FINDTARGET);
-       Net_LinkEntity(self, FALSE, 0, onslaught_link_send);
-}
index 236e311b6a0cb8c0fa7fa39631678b8b46f6ab64..29a8e4e0b5602d9fa0f645a2b5de7a1e4bb437d0 100644 (file)
@@ -212,3 +212,48 @@ MUTATOR_HOOKABLE(SetWeaponreplace);
                entity other; // weapon info
        // IN+OUT
                string ret_string;
+
+MUTATOR_HOOKABLE(PortalTeleport);
+       // called whenever a player goes through a portal gun teleport
+       // allows you to strip a player of an item if they go through the teleporter to help prevent cheating
+       // INPUT
+       entity self;
+       
+MUTATOR_HOOKABLE(HelpMePing);
+       // called whenever a player uses impulse 33 (help me) in cl_impulse.qc
+       // normally help me ping uses self.waypointsprite_attachedforcarrier,
+       // but if your mutator uses something different then you can handle it
+       // in a special manner using this hook
+       // INPUT
+       entity self; // the player who pressed impulse 33
+       
+MUTATOR_HOOKABLE(VehicleEnter);
+       // called when a player enters a vehicle
+       // allows mutators to set special settings in this event
+       // INPUT
+       entity vh_player; // player
+       entity vh_vehicle; // vehicle
+       
+MUTATOR_HOOKABLE(VehicleExit);
+       // called when a player exits a vehicle
+       // allows mutators to set special settings in this event
+       // INPUT
+       entity vh_player; // player
+       entity vh_vehicle; // vehicle
+       
+MUTATOR_HOOKABLE(AbortSpeedrun);
+       // called when a speedrun is aborted and the player is teleported back to start position
+       // INPUT
+       entity self; // player
+
+MUTATOR_HOOKABLE(ItemTouch);
+       // called at when a item is touched. Called early, can edit item properties.
+       entity self;    // item
+       entity other;   // player
+
+MUTATOR_HOOKABLE(ClientConnect);
+       // called at when a player connect
+       entity self;    // player
+
+MUTATOR_HOOKABLE(HavocBot_ChooseRule);
+       entity self;
diff --git a/qcsrc/server/mutators/gamemode_ctf.qc b/qcsrc/server/mutators/gamemode_ctf.qc
new file mode 100644 (file)
index 0000000..f453cc2
--- /dev/null
@@ -0,0 +1,2211 @@
+// ================================================================
+//  Official capture the flag game mode coding, reworked by Samual
+//  Last updated: September, 2012
+// ================================================================
+
+void ctf_FakeTimeLimit(entity e, float t)
+{
+       msg_entity = e;
+       WriteByte(MSG_ONE, 3); // svc_updatestat
+       WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT
+       if(t < 0)
+               WriteCoord(MSG_ONE, autocvar_timelimit);
+       else
+               WriteCoord(MSG_ONE, (t + 1) / 60);
+}
+
+void ctf_EventLog(string mode, float flagteam, entity actor) // use an alias for easy changing and quick editing later
+{
+       if(autocvar_sv_eventlog)
+               GameLogEcho(strcat(":ctf:", mode, ":", ftos(flagteam), ((actor != world) ? (strcat(":", ftos(actor.playerid))) : "")));
+}
+
+string ctf_CaptureRecord(entity flag, entity player)
+{
+       float cap_time, cap_record, success;
+       string cap_message, refername;
+       
+       if((autocvar_g_ctf_captimerecord_always) || (player_count - currentbots)) 
+       {
+               cap_record = ctf_captimerecord;
+               cap_time = (time - flag.ctf_pickuptime);
+
+               refername = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"));
+               refername = ((refername == player.netname) ? "their" : strcat(refername, "^7's"));
+
+               if(!ctf_captimerecord) 
+                       { cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds"); success = TRUE; }
+               else if(cap_time < cap_record) 
+                       { cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds, breaking ", refername, " previous record of ", ftos_decimals(cap_record, 2), " seconds"); success = TRUE; }
+               else
+                       { cap_message = strcat(" in ", ftos_decimals(cap_time, 2), " seconds, failing to break ", refername, " record of ", ftos_decimals(cap_record, 2), " seconds"); success = FALSE; }
+
+               if(success) 
+               {
+                       ctf_captimerecord = cap_time;
+                       db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(cap_time));
+                       db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), player.netname);
+                       write_recordmarker(player, (time - cap_time), cap_time); 
+               } 
+       }
+       
+       return cap_message;
+}
+
+void ctf_FlagcarrierWaypoints(entity player)
+{
+       WaypointSprite_Spawn("flagcarrier", 0, 0, player, FLAG_WAYPOINT_OFFSET, world, player.team, player, wps_flagcarrier, TRUE, RADARICON_FLAG, WPCOLOR_FLAGCARRIER(player.team));
+       WaypointSprite_UpdateMaxHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent) * 2);
+       WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent));
+       WaypointSprite_UpdateTeamRadar(player.wps_flagcarrier, RADARICON_FLAGCARRIER, WPCOLOR_FLAGCARRIER(player.team));
+}
+
+void ctf_CalculatePassVelocity(entity flag, vector to, vector from, float turnrate)
+{
+       float current_distance = vlen((('1 0 0' * to_x) + ('0 1 0' * to_y)) - (('1 0 0' * from_x) + ('0 1 0' * from_y))); // for the sake of this check, exclude Z axis
+       float initial_height = min(autocvar_g_ctf_pass_arc_max, (flag.pass_distance * tanh(autocvar_g_ctf_pass_arc)));
+       float current_height = (initial_height * min(1, (current_distance / flag.pass_distance)));
+       //print("current_height = ", ftos(current_height), ", initial_height = ", ftos(initial_height), ".\n");
+
+       vector targpos;
+       if(current_height) // make sure we can actually do this arcing path
+       {
+               targpos = (to + ('0 0 1' * current_height));
+               WarpZone_TraceLine(flag.origin, targpos, MOVE_NOMONSTERS, flag);
+               if(trace_fraction < 1)
+               {
+                       //print("normal arc line failed, trying to find new pos...");
+                       WarpZone_TraceLine(to, targpos, MOVE_NOMONSTERS, flag);
+                       targpos = (trace_endpos + FLAG_PASS_ARC_OFFSET);
+                       WarpZone_TraceLine(flag.origin, targpos, MOVE_NOMONSTERS, flag);
+                       if(trace_fraction < 1) { targpos = to; /* print(" ^1FAILURE^7, reverting to original direction.\n"); */ }
+                       /*else { print(" ^3SUCCESS^7, using new arc line.\n"); } */
+               }
+       }
+       else { targpos = to; }
+
+       //flag.angles = normalize(('0 1 0' * to_y) - ('0 1 0' * from_y));
+
+       vector desired_direction = normalize(targpos - from);
+       if(turnrate) { flag.velocity = (normalize(normalize(flag.velocity) + (desired_direction * autocvar_g_ctf_pass_turnrate)) * autocvar_g_ctf_pass_velocity); }
+       else { flag.velocity = (desired_direction * autocvar_g_ctf_pass_velocity); }
+}
+
+float ctf_CheckPassDirection(vector head_center, vector passer_center, vector passer_angle, vector nearest_to_passer)
+{
+       if(autocvar_g_ctf_pass_directional_max || autocvar_g_ctf_pass_directional_min)
+       {
+               // directional tracing only
+               float spreadlimit;
+               makevectors(passer_angle);
+
+               // 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(head_center - passer_center);
+               ang = acos(dotproduct(normalize(head_center - passer_center), v_forward));
+               a = h * cos(ang);
+
+               vector nearest_on_line = (passer_center + a * v_forward);
+               float distance_from_line = vlen(nearest_to_passer - nearest_on_line);
+
+               spreadlimit = (autocvar_g_ctf_pass_radius ? min(1, (vlen(passer_center - nearest_on_line) / autocvar_g_ctf_pass_radius)) : 1);
+               spreadlimit = (autocvar_g_ctf_pass_directional_min * (1 - spreadlimit) + autocvar_g_ctf_pass_directional_max * spreadlimit);
+
+               if(spreadlimit && (distance_from_line <= spreadlimit) && ((vlen(normalize(head_center - passer_center) - v_forward) * RAD2DEG) <= 90))
+                       { return TRUE; }
+               else
+                       { return FALSE; }
+       }
+       else { return TRUE; }
+}
+
+
+// =======================
+// CaptureShield Functions 
+// =======================
+
+float ctf_CaptureShield_CheckStatus(entity p) 
+{
+       float s, se;
+       entity e;
+       float players_worseeq, players_total;
+
+       if(ctf_captureshield_max_ratio <= 0)
+               return FALSE;
+
+       s = PlayerScore_Add(p, SP_SCORE, 0);
+       if(s >= -ctf_captureshield_min_negscore)
+               return FALSE;
+
+       players_total = players_worseeq = 0;
+       FOR_EACH_PLAYER(e)
+       {
+               if(IsDifferentTeam(e, p))
+                       continue;
+               se = PlayerScore_Add(e, SP_SCORE, 0);
+               if(se <= s)
+                       ++players_worseeq;
+               ++players_total;
+       }
+
+       // player is in the worse half, if >= half the players are better than him, or consequently, if < half of the players are worse
+       // use this rule here
+       
+       if(players_worseeq >= players_total * ctf_captureshield_max_ratio)
+               return FALSE;
+
+       return TRUE;
+}
+
+void ctf_CaptureShield_Update(entity player, float wanted_status)
+{
+       float updated_status = ctf_CaptureShield_CheckStatus(player);
+       if((wanted_status == player.ctf_captureshielded) && (updated_status != wanted_status)) // 0: shield only, 1: unshield only
+       {
+               if(updated_status) // TODO csqc notifier for this // Samual: How?
+                       Send_CSQC_Centerprint_Generic(player, CPID_CTF_CAPTURESHIELD, "^3You are now ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Make some defensive scores before trying again.", 5, 0);
+               else
+                       Send_CSQC_Centerprint_Generic(player, CPID_CTF_CAPTURESHIELD, "^3You are now free.\n\n^3Feel free to ^1try to capture^3 the flag again\n^3if you think you will succeed.", 5, 0);
+                       
+               player.ctf_captureshielded = updated_status;
+       }
+}
+
+float ctf_CaptureShield_Customize()
+{
+       if(!other.ctf_captureshielded) { return FALSE; }
+       if(!IsDifferentTeam(self, other)) { return FALSE; }
+       
+       return TRUE;
+}
+
+void ctf_CaptureShield_Touch()
+{
+       if(!other.ctf_captureshielded) { return; }
+       if(!IsDifferentTeam(self, other)) { return; }
+       
+       vector mymid = (self.absmin + self.absmax) * 0.5;
+       vector othermid = (other.absmin + other.absmax) * 0.5;
+
+       Damage(other, self, self, 0, DEATH_HURTTRIGGER, mymid, normalize(othermid - mymid) * ctf_captureshield_force);
+       Send_CSQC_Centerprint_Generic(other, CPID_CTF_CAPTURESHIELD, "^3You are ^4shielded^3 from the flag\n^3for ^1too many unsuccessful attempts^3 to capture.\n\n^3Get some defensive scores before trying again.", 5, 0);
+}
+
+void ctf_CaptureShield_Spawn(entity flag)
+{
+       entity shield = spawn();
+       
+       shield.enemy = self;
+       shield.team = self.team;
+       shield.touch = ctf_CaptureShield_Touch;
+       shield.customizeentityforclient = ctf_CaptureShield_Customize;
+       shield.classname = "ctf_captureshield";
+       shield.effects = EF_ADDITIVE;
+       shield.movetype = MOVETYPE_NOCLIP;
+       shield.solid = SOLID_TRIGGER;
+       shield.avelocity = '7 0 11';
+       shield.scale = 0.5;
+       
+       setorigin(shield, self.origin);
+       setmodel(shield, "models/ctf/shield.md3");
+       setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs);
+}
+
+
+// ====================
+// Drop/Pass/Throw Code
+// ====================
+
+void ctf_Handle_Drop(entity flag, entity player, float droptype)
+{
+       // declarations
+       player = (player ? player : flag.pass_sender);
+
+       // main
+       flag.movetype = MOVETYPE_TOSS;
+       flag.takedamage = DAMAGE_YES;
+       flag.angles = '0 0 0';
+       flag.health = flag.max_flag_health;
+       flag.ctf_droptime = time;
+       flag.ctf_dropper = player;
+       flag.ctf_status = FLAG_DROPPED;
+       
+       // messages and sounds
+       Send_KillNotification(player.netname, flag.netname, "", INFO_LOSTFLAG, MSG_INFO);
+       sound(flag, CH_TRIGGER, flag.snd_flag_dropped, VOL_BASE, ATTN_NONE);
+       ctf_EventLog("dropped", player.team, player);
+
+       // scoring
+       PlayerTeamScore_AddScore(player, -autocvar_g_ctf_score_penalty_drop);   
+       PlayerScore_Add(player, SP_CTF_DROPS, 1);
+       
+       // waypoints
+       if(autocvar_g_ctf_flag_dropped_waypoint)
+               WaypointSprite_Spawn("flagdropped", 0, 0, flag, FLAG_WAYPOINT_OFFSET, world, ((autocvar_g_ctf_flag_dropped_waypoint == 2) ? 0 : player.team), flag, wps_flagdropped, TRUE, RADARICON_FLAG, WPCOLOR_DROPPEDFLAG(flag.team));
+
+       if(autocvar_g_ctf_flag_return_time || (autocvar_g_ctf_flag_return_damage && autocvar_g_ctf_flag_health))
+       {
+               WaypointSprite_UpdateMaxHealth(flag.wps_flagdropped, flag.max_flag_health);
+               WaypointSprite_UpdateHealth(flag.wps_flagdropped, flag.health);
+       }
+       
+       player.throw_antispam = time + autocvar_g_ctf_pass_wait;
+       
+       if(droptype == DROP_PASS)
+       {
+               flag.pass_distance = 0;
+               flag.pass_sender = world;
+               flag.pass_target = world;
+       }
+}
+
+void ctf_Handle_Retrieve(entity flag, entity player)
+{
+       entity tmp_player; // temporary entity which the FOR_EACH_PLAYER loop uses to scan players
+       entity sender = flag.pass_sender;
+       
+       // transfer flag to player
+       flag.owner = player;
+       flag.owner.flagcarried = flag;
+       
+       // reset flag
+       setattachment(flag, player, "");
+       setorigin(flag, FLAG_CARRY_OFFSET);
+       flag.movetype = MOVETYPE_NONE;
+       flag.takedamage = DAMAGE_NO;
+       flag.solid = SOLID_NOT;
+       flag.angles = '0 0 0';
+       flag.ctf_status = FLAG_CARRY;
+
+       // messages and sounds
+       sound(player, CH_TRIGGER, flag.snd_flag_pass, VOL_BASE, ATTN_NORM);
+       ctf_EventLog("receive", flag.team, player);
+       
+       FOR_EACH_REALPLAYER(tmp_player)
+       {
+               if(tmp_player == sender)
+                       centerprint(tmp_player, strcat("You passed the ", flag.netname, " to ", player.netname));
+               else if(tmp_player == player)
+                       centerprint(tmp_player, strcat("You received the ", flag.netname, " from ", sender.netname));
+               else if(!IsDifferentTeam(tmp_player, sender))
+                       centerprint(tmp_player, strcat(sender.netname, " passed the ", flag.netname, " to ", player.netname));
+       }
+       
+       // create new waypoint
+       ctf_FlagcarrierWaypoints(player);
+       
+       sender.throw_antispam = time + autocvar_g_ctf_pass_wait;
+       player.throw_antispam = sender.throw_antispam;
+
+       flag.pass_distance = 0;
+       flag.pass_sender = world;
+       flag.pass_target = world;
+}
+
+void ctf_Handle_Throw(entity player, entity receiver, float droptype)
+{
+       entity flag = player.flagcarried;
+       vector targ_origin, flag_velocity;
+       
+       if(!flag) { return; }
+       if((droptype == DROP_PASS) && !receiver) { return; }
+       
+       if(flag.speedrunning) { ctf_RespawnFlag(flag); return; }
+       
+       // reset the flag
+       setattachment(flag, world, "");
+       setorigin(flag, player.origin + FLAG_DROP_OFFSET);
+       flag.owner.flagcarried = world;
+       flag.owner = world;
+       flag.solid = SOLID_TRIGGER;
+       flag.ctf_dropper = player;
+       flag.ctf_droptime = time;
+       
+       flag.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND for MOVETYPE_TOSS
+       
+       switch(droptype)
+       {
+               case DROP_PASS:
+               {
+                       // warpzone support:
+                       // for the examples, we assume player -> wz1 -> ... -> wzn -> receiver
+                       // findradius has already put wzn ... wz1 into receiver's warpzone parameters!
+                       WarpZone_RefSys_Copy(flag, receiver);
+                       WarpZone_RefSys_AddInverse(flag, receiver); // wz1^-1 ... wzn^-1 receiver
+                       targ_origin = WarpZone_RefSys_TransformOrigin(receiver, flag, (0.5 * (receiver.absmin + receiver.absmax))); // this is target origin as seen by the flag
+
+                       flag.pass_distance = vlen((('1 0 0' * targ_origin_x) + ('0 1 0' * targ_origin_y)) - (('1 0 0' *  player.origin_x) + ('0 1 0' *  player.origin_y))); // for the sake of this check, exclude Z axis
+                       ctf_CalculatePassVelocity(flag, targ_origin, player.origin, FALSE);
+
+                       // main
+                       flag.movetype = MOVETYPE_FLY;
+                       flag.takedamage = DAMAGE_NO;
+                       flag.pass_sender = player;
+                       flag.pass_target = receiver;
+                       flag.ctf_status = FLAG_PASSING;
+                       
+                       // other
+                       sound(player, CH_TRIGGER, flag.snd_flag_touch, VOL_BASE, ATTN_NORM);
+                       WarpZone_TrailParticles(world, particleeffectnum(flag.passeffect), player.origin, targ_origin);
+                       ctf_EventLog("pass", flag.team, player);
+                       break;
+               }
+               
+               case DROP_THROW:
+               {
+                       makevectors((player.v_angle_y * '0 1 0') + (bound(autocvar_g_ctf_throw_angle_min, player.v_angle_x, autocvar_g_ctf_throw_angle_max) * '1 0 0'));
+                               
+                       flag_velocity = (('0 0 1' * autocvar_g_ctf_throw_velocity_up) + ((v_forward * autocvar_g_ctf_throw_velocity_forward) * ((player.items & IT_STRENGTH) ? autocvar_g_ctf_throw_strengthmultiplier : 1)));
+                       flag.velocity = W_CalculateProjectileVelocity(player.velocity, flag_velocity, FALSE);
+                       ctf_Handle_Drop(flag, player, droptype);
+                       break;
+               }
+               
+               case DROP_RESET:
+               {
+                       flag.velocity = '0 0 0'; // do nothing
+                       break;
+               }
+               
+               default:
+               case DROP_NORMAL:
+               {
+                       flag.velocity = W_CalculateProjectileVelocity(player.velocity, (('0 0 1' * autocvar_g_ctf_drop_velocity_up) + ((('0 1 0' * crandom()) + ('1 0 0' * crandom())) * autocvar_g_ctf_drop_velocity_side)), FALSE);
+                       ctf_Handle_Drop(flag, player, droptype);
+                       break;
+               }
+       }
+
+       // kill old waypointsprite
+       WaypointSprite_Ping(player.wps_flagcarrier);
+       WaypointSprite_Kill(player.wps_flagcarrier);
+       
+       if(player.wps_enemyflagcarrier)
+               WaypointSprite_Kill(player.wps_enemyflagcarrier);
+       
+       // captureshield
+       ctf_CaptureShield_Update(player, 0); // shield player from picking up flag
+}
+
+
+// ==============
+// Event Handlers
+// ==============
+
+void ctf_Handle_Capture(entity flag, entity toucher, float capturetype)
+{
+       entity enemy_flag = ((capturetype == CAPTURE_NORMAL) ? toucher.flagcarried : toucher);
+       entity player = ((capturetype == CAPTURE_NORMAL) ? toucher : enemy_flag.ctf_dropper);
+       float old_time, new_time; 
+       
+       if not(player) { return; } // without someone to give the reward to, we can't possibly cap
+       
+       // messages and sounds
+       Send_KillNotification(player.netname, enemy_flag.netname, ctf_CaptureRecord(enemy_flag, player), INFO_CAPTUREFLAG, MSG_INFO);
+       sound(player, CH_TRIGGER, flag.snd_flag_capture, VOL_BASE, ATTN_NONE);
+       
+       switch(capturetype)
+       {
+               case CAPTURE_NORMAL: ctf_EventLog("capture", enemy_flag.team, player); break;
+               case CAPTURE_DROPPED: ctf_EventLog("droppedcapture", enemy_flag.team, player); break;
+               default: break;
+       }
+       
+       // scoring
+       PlayerTeamScore_AddScore(player, autocvar_g_ctf_score_capture);
+       PlayerTeamScore_Add(player, SP_CTF_CAPS, ST_CTF_CAPS, 1);
+
+       old_time = PlayerScore_Add(player, SP_CTF_CAPTIME, 0);
+       new_time = TIME_ENCODE(time - enemy_flag.ctf_pickuptime);
+       if(!old_time || new_time < old_time)
+               PlayerScore_Add(player, SP_CTF_CAPTIME, new_time - old_time);
+
+       // effects
+       pointparticles(particleeffectnum(flag.capeffect), flag.origin, '0 0 0', 1);
+       //shockwave_spawn("models/ctf/shockwavetransring.md3", flag.origin - '0 0 15', -0.8, 0, 1);
+
+       // other
+       if(capturetype == CAPTURE_NORMAL)
+       {
+               WaypointSprite_Kill(player.wps_flagcarrier);
+               if(flag.speedrunning) { ctf_FakeTimeLimit(player, -1); }
+               
+               if((enemy_flag.ctf_dropper) && (player != enemy_flag.ctf_dropper))
+                       { PlayerTeamScore_AddScore(enemy_flag.ctf_dropper, autocvar_g_ctf_score_capture_assist); }
+       }
+       
+       // reset the flag
+       player.next_take_time = time + autocvar_g_ctf_flag_collect_delay;
+       ctf_RespawnFlag(enemy_flag);
+}
+
+void ctf_Handle_Return(entity flag, entity player)
+{
+       // messages and sounds
+       //centerprint(player, strcat("You returned the ", flag.netname));
+       Send_KillNotification(player.netname, flag.netname, "", INFO_RETURNFLAG, MSG_INFO);
+       sound(player, CH_TRIGGER, flag.snd_flag_returned, VOL_BASE, ATTN_NONE);
+       ctf_EventLog("return", flag.team, player);
+
+       // scoring
+       PlayerTeamScore_AddScore(player, autocvar_g_ctf_score_return); // reward for return
+       PlayerScore_Add(player, SP_CTF_RETURNS, 1); // add to count of returns
+
+       TeamScore_AddToTeam(flag.team, ST_SCORE, -autocvar_g_ctf_score_penalty_returned); // punish the team who was last carrying it
+       
+       if(flag.ctf_dropper) 
+       {
+               PlayerScore_Add(flag.ctf_dropper, SP_SCORE, -autocvar_g_ctf_score_penalty_returned); // punish the player who dropped the flag
+               ctf_CaptureShield_Update(flag.ctf_dropper, 0); // shield player from picking up flag 
+               flag.ctf_dropper.next_take_time = time + autocvar_g_ctf_flag_collect_delay; // set next take time
+       }
+       
+       // reset the flag
+       ctf_RespawnFlag(flag);
+}
+
+void ctf_Handle_Pickup(entity flag, entity player, float pickuptype)
+{
+       // declarations
+       entity tmp_player; // temporary entity which the FOR_EACH_PLAYER loop uses to scan players
+       string verbosename; // holds the name of the player OR no name at all for printing in the centerprints
+       float pickup_dropped_score; // used to calculate dropped pickup score
+       
+       // attach the flag to the player
+       flag.owner = player;
+       player.flagcarried = flag;
+       setattachment(flag, player, "");
+       setorigin(flag, FLAG_CARRY_OFFSET);
+       
+       // flag setup
+       flag.movetype = MOVETYPE_NONE;
+       flag.takedamage = DAMAGE_NO;
+       flag.solid = SOLID_NOT;
+       flag.angles = '0 0 0';
+       flag.ctf_status = FLAG_CARRY;
+       
+       switch(pickuptype)
+       {
+               case PICKUP_BASE: flag.ctf_pickuptime = time; break; // used for timing runs
+               case PICKUP_DROPPED: flag.health = flag.max_flag_health; break; // reset health/return timelimit
+               default: break;
+       }
+
+       // messages and sounds
+       Send_KillNotification (player.netname, flag.netname, "", INFO_GOTFLAG, MSG_INFO);
+       sound(player, CH_TRIGGER, flag.snd_flag_taken, VOL_BASE, ATTN_NONE);
+       verbosename = ((autocvar_g_ctf_flag_pickup_verbosename) ? strcat(Team_ColorCode(player.team), "(^7", player.netname, Team_ColorCode(player.team), ") ") : "");
+       
+       FOR_EACH_REALPLAYER(tmp_player)
+       {
+               if(tmp_player == player)
+               {
+                       centerprint(tmp_player, strcat("You got the ", flag.netname, "!"));
+                       //if(ctf_stalemate) { centerprint(tmp_player, "Stalemate! Enemies can see you on radar!"); }
+               }
+               //else if(!IsDifferentTeam(tmp_player, player))
+               //      centerprint(tmp_player, strcat("Your ", Team_ColorCode(player.team), "team mate ", verbosename, "^7got the flag! Protect them!"));
+               else if(!IsDifferentTeam(tmp_player, flag))
+                       centerprint(tmp_player, strcat("The ", Team_ColorCode(player.team), "enemy ", verbosename, "^7got your flag! Retrieve it!"));
+       }
+       
+       // scoring
+       PlayerScore_Add(player, SP_CTF_PICKUPS, 1);
+       switch(pickuptype)
+       {               
+               case PICKUP_BASE:
+               {
+                       PlayerTeamScore_AddScore(player, autocvar_g_ctf_score_pickup_base);
+                       ctf_EventLog("steal", flag.team, player);
+                       break;
+               }
+               
+               case PICKUP_DROPPED:
+               {
+                       pickup_dropped_score = (autocvar_g_ctf_flag_return_time ? bound(0, ((flag.ctf_droptime + autocvar_g_ctf_flag_return_time) - time) / autocvar_g_ctf_flag_return_time, 1) : 1);
+                       pickup_dropped_score = floor((autocvar_g_ctf_score_pickup_dropped_late * (1 - pickup_dropped_score) + autocvar_g_ctf_score_pickup_dropped_early * pickup_dropped_score) + 0.5);
+                       dprint("pickup_dropped_score is ", ftos(pickup_dropped_score), "\n");
+                       PlayerTeamScore_AddScore(player, pickup_dropped_score);
+                       ctf_EventLog("pickup", flag.team, player);
+                       break;
+               }
+               
+               default: break;
+       }
+       
+       // speedrunning
+       if(pickuptype == PICKUP_BASE)
+       {
+               flag.speedrunning = player.speedrunning; // if speedrunning, flag will flag-return and teleport the owner back after the record
+               if((player.speedrunning) && (ctf_captimerecord))
+                       ctf_FakeTimeLimit(player, time + ctf_captimerecord);
+       }
+               
+       // effects
+       pointparticles(particleeffectnum(flag.toucheffect), player.origin, '0 0 0', 1);
+       
+       // waypoints 
+       if(pickuptype == PICKUP_DROPPED) { WaypointSprite_Kill(flag.wps_flagdropped); }
+       ctf_FlagcarrierWaypoints(player);
+       WaypointSprite_Ping(player.wps_flagcarrier);
+}
+
+
+// ===================
+// Main Flag Functions
+// ===================
+
+void ctf_CheckFlagReturn(entity flag, float returntype)
+{
+       if((flag.ctf_status == FLAG_DROPPED) || (flag.ctf_status == FLAG_PASSING))
+       {
+               if(flag.wps_flagdropped) { WaypointSprite_UpdateHealth(flag.wps_flagdropped, flag.health); }
+               
+               if((flag.health <= 0) || (time >= flag.ctf_droptime + autocvar_g_ctf_flag_return_time))
+               {
+                       switch(returntype)
+                       {
+                               case RETURN_DROPPED: bprint("The ", flag.netname, " was dropped in the base and returned itself\n"); break;
+                               case RETURN_DAMAGE: bprint("The ", flag.netname, " was destroyed and returned to base\n"); break;
+                               case RETURN_SPEEDRUN: bprint("The ", flag.netname, " became impatient after ", ftos_decimals(ctf_captimerecord, 2), " seconds and returned itself\n"); break;
+                               case RETURN_NEEDKILL: bprint("The ", flag.netname, " fell somewhere it couldn't be reached and returned to base\n"); break;
+                               
+                               default:
+                               case RETURN_TIMEOUT:
+                                       { bprint("The ", flag.netname, " has returned to base\n"); break; }
+                       }
+                       sound(flag, CH_TRIGGER, flag.snd_flag_respawn, VOL_BASE, ATTN_NONE);
+                       ctf_EventLog("returned", flag.team, world);
+                       ctf_RespawnFlag(flag);
+               }
+       }
+}
+
+void ctf_CheckStalemate(void)
+{
+       // declarations
+       float stale_red_flags, stale_blue_flags;
+       entity tmp_entity;
+
+       entity ctf_staleflaglist; // reset the list, we need to build the list each time this function runs
+
+       // build list of stale flags
+       for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext)
+       {
+               if(autocvar_g_ctf_stalemate)
+               if(tmp_entity.ctf_status != FLAG_BASE)
+               if(time >= tmp_entity.ctf_pickuptime + autocvar_g_ctf_stalemate_time)
+               {
+                       tmp_entity.ctf_staleflagnext = ctf_staleflaglist; // link flag into staleflaglist
+                       ctf_staleflaglist = tmp_entity;
+                       
+                       switch(tmp_entity.team)
+                       {
+                               case COLOR_TEAM1: ++stale_red_flags; break;
+                               case COLOR_TEAM2: ++stale_blue_flags; break;
+                       }
+               }
+       }
+
+       if(stale_red_flags && stale_blue_flags)
+               ctf_stalemate = TRUE;
+       else if((!stale_red_flags && !stale_blue_flags) && autocvar_g_ctf_stalemate_endcondition == 2)
+               { ctf_stalemate = FALSE; wpforenemy_announced = FALSE; }
+       else if((!stale_red_flags || !stale_blue_flags) && autocvar_g_ctf_stalemate_endcondition == 1)
+               { ctf_stalemate = FALSE; wpforenemy_announced = FALSE; }
+               
+       // if sufficient stalemate, then set up the waypointsprite and announce the stalemate if necessary
+       if(ctf_stalemate)
+       {
+               for(tmp_entity = ctf_staleflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_staleflagnext)
+               {
+                       if((tmp_entity.owner) && (!tmp_entity.owner.wps_enemyflagcarrier))
+                               WaypointSprite_Spawn("enemyflagcarrier", 0, 0, tmp_entity.owner, FLAG_WAYPOINT_OFFSET, world, tmp_entity.team, tmp_entity.owner, wps_enemyflagcarrier, TRUE, RADARICON_FLAG, WPCOLOR_ENEMYFC(tmp_entity.owner.team));
+               }
+               
+               if not(wpforenemy_announced)
+               {
+                       FOR_EACH_REALPLAYER(tmp_entity)
+                               if(tmp_entity.flagcarried)
+                                       centerprint(tmp_entity, "Stalemate! Enemies can now see you on radar!");
+                               else
+                                       centerprint(tmp_entity, "Stalemate! Flag carriers can now be seen by enemies on radar!");
+                       
+                       wpforenemy_announced = TRUE;
+               }
+       }
+}
+
+void ctf_FlagDamage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+       if(ITEM_DAMAGE_NEEDKILL(deathtype))
+       {
+               // automatically kill the flag and return it
+               self.health = 0;
+               ctf_CheckFlagReturn(self, RETURN_NEEDKILL);
+               return;
+       }
+       if(autocvar_g_ctf_flag_return_damage) 
+       {
+               // reduce health and check if it should be returned
+               self.health = self.health - damage;
+               ctf_CheckFlagReturn(self, RETURN_DAMAGE);
+               return;
+       }
+}
+
+void ctf_FlagThink()
+{
+       // declarations
+       entity tmp_entity;
+
+       self.nextthink = time + FLAG_THINKRATE; // only 5 fps, more is unnecessary.
+
+       // captureshield
+       if(self == ctf_worldflaglist) // only for the first flag
+               FOR_EACH_CLIENT(tmp_entity)
+                       ctf_CaptureShield_Update(tmp_entity, 1); // release shield only
+
+       // sanity checks
+       if(self.mins != FLAG_MIN || self.maxs != FLAG_MAX) { // reset the flag boundaries in case it got squished
+               dprint("wtf the flag got squashed?\n");
+               tracebox(self.origin, FLAG_MIN, FLAG_MAX, self.origin, MOVE_NOMONSTERS, self);
+               if(!trace_startsolid) // can we resize it without getting stuck?
+                       setsize(self, FLAG_MIN, FLAG_MAX); }
+                       
+       switch(self.ctf_status) // reset flag angles in case warpzones adjust it
+       {
+               case FLAG_DROPPED:
+               {
+                       self.angles = '0 0 0';
+                       break;
+               }
+               
+               default: break;
+       }
+
+       // main think method
+       switch(self.ctf_status)
+       {       
+               case FLAG_BASE:
+               {
+                       if(autocvar_g_ctf_dropped_capture_radius)
+                       {
+                               for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext)
+                                       if(tmp_entity.ctf_status == FLAG_DROPPED)
+                                       if(vlen(self.origin - tmp_entity.origin) < autocvar_g_ctf_dropped_capture_radius)
+                                       if(time > tmp_entity.ctf_droptime + autocvar_g_ctf_dropped_capture_delay)
+                                               ctf_Handle_Capture(self, tmp_entity, CAPTURE_DROPPED);
+                       }
+                       return;
+               }
+               
+               case FLAG_DROPPED:
+               {
+                       if(autocvar_g_ctf_flag_dropped_floatinwater)
+                       {
+                               vector midpoint = ((self.absmin + self.absmax) * 0.5);
+                               if(pointcontents(midpoint) == CONTENT_WATER)
+                               {
+                                       self.velocity = self.velocity * 0.5;
+                                       
+                                       if(pointcontents(midpoint + FLAG_FLOAT_OFFSET) == CONTENT_WATER)
+                                               { self.velocity_z = autocvar_g_ctf_flag_dropped_floatinwater; }
+                                       else
+                                               { self.movetype = MOVETYPE_FLY; }
+                               }
+                               else if(self.movetype == MOVETYPE_FLY) { self.movetype = MOVETYPE_TOSS; }
+                       }
+                       if(autocvar_g_ctf_flag_return_dropped)
+                       {
+                               if((vlen(self.origin - self.ctf_spawnorigin) <= autocvar_g_ctf_flag_return_dropped) || (autocvar_g_ctf_flag_return_dropped == -1))
+                               {
+                                       self.health = 0;
+                                       ctf_CheckFlagReturn(self, RETURN_DROPPED);
+                                       return;
+                               }
+                       }
+                       if(autocvar_g_ctf_flag_return_time)
+                       {
+                               self.health -= ((self.max_flag_health / autocvar_g_ctf_flag_return_time) * FLAG_THINKRATE);
+                               ctf_CheckFlagReturn(self, RETURN_TIMEOUT);
+                               return;
+                       } 
+                       return;
+               }
+                       
+               case FLAG_CARRY:
+               {
+                       if(self.speedrunning && ctf_captimerecord && (time >= self.ctf_pickuptime + ctf_captimerecord)) 
+                       {
+                               self.health = 0;
+                               ctf_CheckFlagReturn(self, RETURN_SPEEDRUN);
+
+                               tmp_entity = self;
+                               self = self.owner;
+                               self.impulse = CHIMPULSE_SPEEDRUN; // move the player back to the waypoint they set
+                               ImpulseCommands();
+                               self = tmp_entity;
+                       }
+                       if(autocvar_g_ctf_stalemate)
+                       {
+                               if(time >= wpforenemy_nextthink)
+                               {
+                                       ctf_CheckStalemate();
+                                       wpforenemy_nextthink = time + WPFE_THINKRATE; // waypoint for enemy think rate (to reduce unnecessary spam of this check)
+                               }
+                       }
+                       return;
+               }
+               
+               case FLAG_PASSING:
+               {
+                       vector targ_origin = ((self.pass_target.absmin + self.pass_target.absmax) * 0.5);
+                       targ_origin = WarpZone_RefSys_TransformOrigin(self.pass_target, self, targ_origin); // origin of target as seen by the flag (us)
+                       WarpZone_TraceLine(self.origin, targ_origin, MOVE_NOMONSTERS, self);
+                       
+                       if((self.pass_target == world)
+                               || (self.pass_target.deadflag != DEAD_NO)
+                               || (vlen(self.origin - targ_origin) > autocvar_g_ctf_pass_radius)
+                               || ((trace_fraction < 1) && (trace_ent != self.pass_target))
+                               || (time > self.ctf_droptime + autocvar_g_ctf_pass_timelimit))
+                       {
+                               // give up, pass failed
+                               ctf_Handle_Drop(self, world, DROP_PASS);
+                       }
+                       else
+                       {
+                               // still a viable target, go for it
+                               ctf_CalculatePassVelocity(self, targ_origin, self.origin, TRUE);
+                       }
+                       return;
+               }
+
+               default: // this should never happen
+               {
+                       dprint("ctf_FlagThink(): Flag exists with no status?\n");
+                       return;
+               }
+       }
+}
+
+void ctf_FlagTouch()
+{
+       if(gameover) { return; }
+       
+       entity toucher = other;
+       
+       // automatically kill the flag and return it if it touched lava/slime/nodrop surfaces
+       if(ITEM_TOUCH_NEEDKILL())
+       {
+               self.health = 0;
+               ctf_CheckFlagReturn(self, RETURN_NEEDKILL);
+               return;
+       }
+       
+       // special touch behaviors
+       if(toucher.vehicle_flags & VHF_ISVEHICLE)
+       {
+               if(autocvar_g_ctf_allow_vehicle_touch)
+                       toucher = toucher.owner; // the player is actually the vehicle owner, not other
+               else
+                       return; // do nothing
+       }
+       else if(toucher.classname != "player") // The flag just touched an object, most likely the world
+       {
+               if(time > self.wait) // if we haven't in a while, play a sound/effect
+               {
+                       pointparticles(particleeffectnum(self.toucheffect), self.origin, '0 0 0', 1);
+                       sound(self, CH_TRIGGER, self.snd_flag_touch, VOL_BASE, ATTN_NORM);
+                       self.wait = time + FLAG_TOUCHRATE;
+               }
+               return;
+       }
+       else if(toucher.deadflag != DEAD_NO) { return; }
+
+       switch(self.ctf_status) 
+       {       
+               case FLAG_BASE:
+               {
+                       if(!IsDifferentTeam(toucher, self) && (toucher.flagcarried) && IsDifferentTeam(toucher.flagcarried, self))
+                               ctf_Handle_Capture(self, toucher, CAPTURE_NORMAL); // toucher just captured the enemies flag to his base
+                       else if(IsDifferentTeam(toucher, self) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time))
+                               ctf_Handle_Pickup(self, toucher, PICKUP_BASE); // toucher just stole the enemies flag
+                       break;
+               }
+               
+               case FLAG_DROPPED:
+               {
+                       if(!IsDifferentTeam(toucher, self))
+                               ctf_Handle_Return(self, toucher); // toucher just returned his own flag
+                       else if((!toucher.flagcarried) && ((toucher != self.ctf_dropper) || (time > self.ctf_droptime + autocvar_g_ctf_flag_collect_delay)))
+                               ctf_Handle_Pickup(self, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy flag
+                       break;
+               }
+                       
+               case FLAG_CARRY:
+               {
+                       dprint("Someone touched a flag even though it was being carried?\n");
+                       break;
+               }
+               
+               case FLAG_PASSING:
+               {
+                       if((toucher.classname == "player") && (toucher.deadflag == DEAD_NO) && (toucher != self.pass_sender))
+                       {
+                               if(IsDifferentTeam(toucher, self.pass_sender))
+                                       ctf_Handle_Return(self, toucher);
+                               else
+                                       ctf_Handle_Retrieve(self, toucher);
+                       }
+                       break;
+               }
+       }
+}
+
+.float last_respawn;
+void ctf_RespawnFlag(entity flag)
+{
+       // check for flag respawn being called twice in a row
+       if(flag.last_respawn > time - 0.5)
+               { backtrace("flag respawn called twice quickly! please notify Samual about this..."); }
+
+       flag.last_respawn = time;
+       
+       // reset the player (if there is one)
+       if((flag.owner) && (flag.owner.flagcarried == flag))
+       {
+               if(flag.owner.wps_enemyflagcarrier)
+                       WaypointSprite_Kill(flag.owner.wps_enemyflagcarrier);
+                       
+               WaypointSprite_Kill(flag.wps_flagcarrier);
+               
+               flag.owner.flagcarried = world;
+
+               if(flag.speedrunning)
+                       ctf_FakeTimeLimit(flag.owner, -1);
+       }
+
+       if((flag.ctf_status == FLAG_DROPPED) && (flag.wps_flagdropped))
+               { WaypointSprite_Kill(flag.wps_flagdropped); }
+
+       // reset the flag
+       setattachment(flag, world, "");
+       setorigin(flag, flag.ctf_spawnorigin);
+       
+       flag.movetype = ((flag.noalign) ? MOVETYPE_NONE : MOVETYPE_TOSS);
+       flag.takedamage = DAMAGE_NO;
+       flag.health = flag.max_flag_health;
+       flag.solid = SOLID_TRIGGER;
+       flag.velocity = '0 0 0';
+       flag.angles = flag.mangle;
+       flag.flags = FL_ITEM | FL_NOTARGET;
+       
+       flag.ctf_status = FLAG_BASE;
+       flag.owner = world;
+       flag.pass_distance = 0;
+       flag.pass_sender = world;
+       flag.pass_target = world;
+       flag.ctf_dropper = world;
+       flag.ctf_pickuptime = 0;
+       flag.ctf_droptime = 0;
+}
+
+void ctf_Reset()
+{
+       if(self.owner)
+               if(self.owner.classname == "player")
+                       ctf_Handle_Throw(self.owner, world, DROP_RESET);
+                       
+       ctf_RespawnFlag(self);
+}
+
+void ctf_DelayedFlagSetup(void) // called after a flag is placed on a map by ctf_FlagSetup()
+{
+       // bot waypoints
+       waypoint_spawnforitem_force(self, self.origin);
+       self.nearestwaypointtimeout = 0; // activate waypointing again
+       self.bot_basewaypoint = self.nearestwaypoint;
+
+       // waypointsprites
+       WaypointSprite_SpawnFixed(((self.team == COLOR_TEAM1) ? "redbase" : "bluebase"), self.origin + FLAG_WAYPOINT_OFFSET, self, wps_flagbase, RADARICON_FLAG, colormapPaletteColor(self.team - 1, FALSE));
+       WaypointSprite_UpdateTeamRadar(self.wps_flagbase, RADARICON_FLAG, colormapPaletteColor(self.team - 1, FALSE));
+
+       // captureshield setup
+       ctf_CaptureShield_Spawn(self);
+}
+
+void ctf_FlagSetup(float teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc 
+{      
+       // declarations
+       teamnumber = fabs(teamnumber - bound(0, autocvar_g_ctf_reverse, 1)); // if we were originally 1, this will become 0. If we were originally 0, this will become 1. 
+       self = flag; // for later usage with droptofloor()
+       
+       // main setup
+       flag.ctf_worldflagnext = ctf_worldflaglist; // link flag into ctf_worldflaglist
+       ctf_worldflaglist = flag;
+
+       setattachment(flag, world, ""); 
+
+       flag.netname = ((teamnumber) ? "^1RED^7 flag" : "^4BLUE^7 flag");
+       flag.team = ((teamnumber) ? COLOR_TEAM1 : COLOR_TEAM2); // COLOR_TEAM1: color 4 team (red) - COLOR_TEAM2: color 13 team (blue)
+       flag.items = ((teamnumber) ? IT_KEY2 : IT_KEY1); // IT_KEY2: gold key (redish enough) - IT_KEY1: silver key (bluish enough)
+       flag.classname = "item_flag_team";
+       flag.target = "###item###"; // wut?
+       flag.flags = FL_ITEM | FL_NOTARGET;
+       flag.solid = SOLID_TRIGGER;
+       flag.takedamage = DAMAGE_NO;
+       flag.damageforcescale = autocvar_g_ctf_flag_damageforcescale;   
+       flag.max_flag_health = ((autocvar_g_ctf_flag_return_damage && autocvar_g_ctf_flag_health) ? autocvar_g_ctf_flag_health : 100);
+       flag.health = flag.max_flag_health;
+       flag.event_damage = ctf_FlagDamage;
+       flag.pushable = TRUE;
+       flag.teleportable = TELEPORT_NORMAL;
+       flag.damagedbytriggers = autocvar_g_ctf_flag_return_when_unreachable;
+       flag.damagedbycontents = autocvar_g_ctf_flag_return_when_unreachable;
+       flag.velocity = '0 0 0';
+       flag.mangle = flag.angles;
+       flag.reset = ctf_Reset;
+       flag.touch = ctf_FlagTouch;
+       flag.think = ctf_FlagThink;
+       flag.nextthink = time + FLAG_THINKRATE;
+       flag.ctf_status = FLAG_BASE;
+       
+       if(!flag.model) { flag.model = ((teamnumber) ? autocvar_g_ctf_flag_red_model : autocvar_g_ctf_flag_blue_model); }
+       if(!flag.scale) { flag.scale = FLAG_SCALE; }
+       if(!flag.skin) { flag.skin = ((teamnumber) ? autocvar_g_ctf_flag_red_skin : autocvar_g_ctf_flag_blue_skin); }
+       if(!flag.toucheffect) { flag.toucheffect = ((teamnumber) ? "redflag_touch" : "blueflag_touch"); }
+       if(!flag.passeffect) { flag.passeffect = ((teamnumber) ? "red_pass" : "blue_pass"); }
+       if(!flag.capeffect) { flag.capeffect = ((teamnumber) ? "red_cap" : "blue_cap"); }
+       
+       // sound 
+       if(!flag.snd_flag_taken) { flag.snd_flag_taken  = ((teamnumber) ? "ctf/red_taken.wav" : "ctf/blue_taken.wav"); }
+       if(!flag.snd_flag_returned) { flag.snd_flag_returned = ((teamnumber) ? "ctf/red_returned.wav" : "ctf/blue_returned.wav"); }
+       if(!flag.snd_flag_capture) { flag.snd_flag_capture = ((teamnumber) ? "ctf/red_capture.wav" : "ctf/blue_capture.wav"); } // blue team scores by capturing the red flag
+       if(!flag.snd_flag_respawn) { flag.snd_flag_respawn = "ctf/flag_respawn.wav"; } // if there is ever a team-based sound for this, update the code to match.
+       if(!flag.snd_flag_dropped) { flag.snd_flag_dropped = ((teamnumber) ? "ctf/red_dropped.wav" : "ctf/blue_dropped.wav"); }
+       if(!flag.snd_flag_touch) { flag.snd_flag_touch = "ctf/touch.wav"; } // again has no team-based sound
+       if(!flag.snd_flag_pass) { flag.snd_flag_pass = "ctf/pass.wav"; } // same story here
+       
+       // precache
+       precache_sound(flag.snd_flag_taken);
+       precache_sound(flag.snd_flag_returned);
+       precache_sound(flag.snd_flag_capture);
+       precache_sound(flag.snd_flag_respawn);
+       precache_sound(flag.snd_flag_dropped);
+       precache_sound(flag.snd_flag_touch);
+       precache_sound(flag.snd_flag_pass);
+       precache_model(flag.model);
+       precache_model("models/ctf/shield.md3");
+       precache_model("models/ctf/shockwavetransring.md3");
+
+       // appearence
+       setmodel(flag, flag.model); // precision set below
+       setsize(flag, FLAG_MIN, FLAG_MAX);
+       setorigin(flag, (flag.origin + FLAG_SPAWN_OFFSET));
+       
+       if(autocvar_g_ctf_flag_glowtrails)
+       {
+               flag.glow_color = ((teamnumber) ? 251 : 210); // 251: red - 210: blue
+               flag.glow_size = 25;
+               flag.glow_trail = 1;
+       }
+       
+       flag.effects |= EF_LOWPRECISION;
+       if(autocvar_g_ctf_fullbrightflags) { flag.effects |= EF_FULLBRIGHT; }
+       if(autocvar_g_ctf_dynamiclights)   { flag.effects |= ((teamnumber) ? EF_RED : EF_BLUE); }
+       
+       // flag placement
+       if((flag.spawnflags & 1) || flag.noalign) // don't drop to floor, just stay at fixed location
+       {       
+               flag.dropped_origin = flag.origin; 
+               flag.noalign = TRUE;
+               flag.movetype = MOVETYPE_NONE;
+       }
+       else // drop to floor, automatically find a platform and set that as spawn origin
+       { 
+               flag.noalign = FALSE;
+               self = flag;
+               droptofloor();
+               flag.movetype = MOVETYPE_TOSS; 
+       }       
+       
+       InitializeEntity(flag, ctf_DelayedFlagSetup, INITPRIO_SETLOCATION);
+}
+
+
+// ================
+// Bot player logic
+// ================
+
+// NOTE: LEGACY CODE, needs to be re-written!
+
+void havocbot_calculate_middlepoint()
+{
+       entity f;
+       vector s = '0 0 0';
+       vector fo = '0 0 0';
+       float n = 0;
+
+       f = ctf_worldflaglist;
+       while (f)
+       {
+               fo = f.origin;
+               s = s + fo;
+               f = f.ctf_worldflagnext;
+       }
+       if(!n)
+               return;
+       havocbot_ctf_middlepoint = s * (1.0 / n);
+       havocbot_ctf_middlepoint_radius  = vlen(fo - havocbot_ctf_middlepoint);
+}
+
+
+entity havocbot_ctf_find_flag(entity bot)
+{
+       entity f;
+       f = ctf_worldflaglist;
+       while (f)
+       {
+               if (bot.team == f.team)
+                       return f;
+               f = f.ctf_worldflagnext;
+       }
+       return world;
+}
+
+entity havocbot_ctf_find_enemy_flag(entity bot)
+{
+       entity f;
+       f = ctf_worldflaglist;
+       while (f)
+       {
+               if (bot.team != f.team)
+                       return f;
+               f = f.ctf_worldflagnext;
+       }
+       return world;
+}
+
+float havocbot_ctf_teamcount(entity bot, vector org, float tc_radius)
+{
+       if not(teamplay)
+               return 0;
+
+       float c = 0;
+       entity head;
+
+       FOR_EACH_PLAYER(head)
+       {
+               if(head.team!=bot.team || head.deadflag != DEAD_NO || head == bot)
+                       continue;
+
+               if(vlen(head.origin - org) < tc_radius)
+                       ++c;
+       }
+
+       return c;
+}
+
+void havocbot_goalrating_ctf_ourflag(float ratingscale)
+{
+       entity head;
+       head = ctf_worldflaglist;
+       while (head)
+       {
+               if (self.team == head.team)
+                       break;
+               head = head.ctf_worldflagnext;
+       }
+       if (head)
+               navigation_routerating(head, ratingscale, 10000);
+}
+
+void havocbot_goalrating_ctf_ourbase(float ratingscale)
+{
+       entity head;
+       head = ctf_worldflaglist;
+       while (head)
+       {
+               if (self.team == head.team)
+                       break;
+               head = head.ctf_worldflagnext;
+       }
+       if not(head)
+               return;
+
+       navigation_routerating(head.bot_basewaypoint, ratingscale, 10000);
+}
+
+void havocbot_goalrating_ctf_enemyflag(float ratingscale)
+{
+       entity head;
+       head = ctf_worldflaglist;
+       while (head)
+       {
+               if (self.team != head.team)
+                       break;
+               head = head.ctf_worldflagnext;
+       }
+       if (head)
+               navigation_routerating(head, ratingscale, 10000);
+}
+
+void havocbot_goalrating_ctf_enemybase(float ratingscale)
+{
+       if not(bot_waypoints_for_items)
+       {
+               havocbot_goalrating_ctf_enemyflag(ratingscale);
+               return;
+       }
+
+       entity head;
+
+       head = havocbot_ctf_find_enemy_flag(self);
+
+       if not(head)
+               return;
+
+       navigation_routerating(head.bot_basewaypoint, ratingscale, 10000);
+}
+
+void havocbot_goalrating_ctf_ourstolenflag(float ratingscale)
+{
+       entity mf;
+
+       mf = havocbot_ctf_find_flag(self);
+
+       if(mf.ctf_status == FLAG_BASE)
+               return;
+
+       if(mf.tag_entity)
+               navigation_routerating(mf.tag_entity, ratingscale, 10000);
+}
+
+void havocbot_goalrating_ctf_droppedflags(float ratingscale, vector org, float df_radius)
+{
+       entity head;
+       head = ctf_worldflaglist;
+       while (head)
+       {
+               // flag is out in the field
+               if(head.ctf_status != FLAG_BASE)
+               if(head.tag_entity==world)      // dropped
+               {
+                       if(df_radius)
+                       {
+                               if(vlen(org-head.origin)<df_radius)
+                                       navigation_routerating(head, ratingscale, 10000);
+                       }
+                       else
+                               navigation_routerating(head, ratingscale, 10000);
+               }
+
+               head = head.ctf_worldflagnext;
+       }
+}
+
+void havocbot_goalrating_ctf_carrieritems(float ratingscale, vector org, float sradius)
+{
+       entity head;
+       float t;
+       head = findchainfloat(bot_pickup, TRUE);
+       while (head)
+       {
+               // gather health and armor only
+               if (head.solid)
+               if (head.health || head.armorvalue)
+               if (vlen(head.origin - org) < sradius)
+               {
+                       // get the value of the item
+                       t = head.bot_pickupevalfunc(self, head) * 0.0001;
+                       if (t > 0)
+                               navigation_routerating(head, t * ratingscale, 500);
+               }
+               head = head.chain;
+       }
+}
+
+void havocbot_ctf_reset_role(entity bot)
+{
+       float cdefense, cmiddle, coffense;
+       entity mf, ef, head;
+       float c;
+
+       if(bot.deadflag != DEAD_NO)
+               return;
+
+       if(vlen(havocbot_ctf_middlepoint)==0)
+               havocbot_calculate_middlepoint();
+
+       // Check ctf flags
+       if (bot.flagcarried)
+       {
+               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_CARRIER);
+               return;
+       }
+
+       mf = havocbot_ctf_find_flag(bot);
+       ef = havocbot_ctf_find_enemy_flag(bot);
+
+       // Retrieve stolen flag
+       if(mf.ctf_status!=FLAG_BASE)
+       {
+               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_RETRIEVER);
+               return;
+       }
+
+       // If enemy flag is taken go to the middle to intercept pursuers
+       if(ef.ctf_status!=FLAG_BASE)
+       {
+               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);
+               return;
+       }
+
+       // if there is only me on the team switch to offense
+       c = 0;
+       FOR_EACH_PLAYER(head)
+       if(head.team==bot.team)
+               ++c;
+
+       if(c==1)
+       {
+               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE);
+               return;
+       }
+
+       // Evaluate best position to take
+       // Count mates on middle position
+       cmiddle = havocbot_ctf_teamcount(bot, havocbot_ctf_middlepoint, havocbot_ctf_middlepoint_radius * 0.5);
+
+       // Count mates on defense position
+       cdefense = havocbot_ctf_teamcount(bot, mf.dropped_origin, havocbot_ctf_middlepoint_radius * 0.5);
+
+       // Count mates on offense position
+       coffense = havocbot_ctf_teamcount(bot, ef.dropped_origin, havocbot_ctf_middlepoint_radius);
+
+       if(cdefense<=coffense)
+               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_DEFENSE);
+       else if(coffense<=cmiddle)
+               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_OFFENSE);
+       else
+               havocbot_role_ctf_setrole(bot, HAVOCBOT_CTF_ROLE_MIDDLE);
+}
+
+void havocbot_role_ctf_carrier()
+{
+       if(self.deadflag != DEAD_NO)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.flagcarried == world)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.bot_strategytime < time)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+
+               navigation_goalrating_start();
+               havocbot_goalrating_ctf_ourbase(50000);
+
+               if(self.health<100)
+                       havocbot_goalrating_ctf_carrieritems(1000, self.origin, 1000);
+
+               navigation_goalrating_end();
+
+               if (self.navigation_hasgoals)
+                       self.havocbot_cantfindflag = time + 10;
+               else if (time > self.havocbot_cantfindflag)
+               {
+                       // Can't navigate to my own base, suicide!
+                       // TODO: drop it and wander around
+                       Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
+                       return;
+               }
+       }
+}
+
+void havocbot_role_ctf_escort()
+{
+       entity mf, ef;
+
+       if(self.deadflag != DEAD_NO)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.flagcarried)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
+               return;
+       }
+
+       // If enemy flag is back on the base switch to previous role
+       ef = havocbot_ctf_find_enemy_flag(self);
+       if(ef.ctf_status==FLAG_BASE)
+       {
+               self.havocbot_role = self.havocbot_previous_role;
+               self.havocbot_role_timeout = 0;
+               return;
+       }
+
+       // If the flag carrier reached the base switch to defense
+       mf = havocbot_ctf_find_flag(self);
+       if(mf.ctf_status!=FLAG_BASE)
+       if(vlen(ef.origin - mf.dropped_origin) < 300)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_DEFENSE);
+               return;
+       }
+
+       // Set the role timeout if necessary
+       if (!self.havocbot_role_timeout)
+       {
+               self.havocbot_role_timeout = time + random() * 30 + 60;
+       }
+
+       // If nothing happened just switch to previous role
+       if (time > self.havocbot_role_timeout)
+       {
+               self.havocbot_role = self.havocbot_previous_role;
+               self.havocbot_role_timeout = 0;
+               return;
+       }
+
+       // Chase the flag carrier
+       if (self.bot_strategytime < time)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+               navigation_goalrating_start();
+               havocbot_goalrating_ctf_enemyflag(30000);
+               havocbot_goalrating_ctf_ourstolenflag(40000);
+               havocbot_goalrating_items(10000, self.origin, 10000);
+               navigation_goalrating_end();
+       }
+}
+
+void havocbot_role_ctf_offense()
+{
+       entity mf, ef;
+       vector pos;
+
+       if(self.deadflag != DEAD_NO)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.flagcarried)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
+               return;
+       }
+
+       // Check flags
+       mf = havocbot_ctf_find_flag(self);
+       ef = havocbot_ctf_find_enemy_flag(self);
+
+       // Own flag stolen
+       if(mf.ctf_status!=FLAG_BASE)
+       {
+               if(mf.tag_entity)
+                       pos = mf.tag_entity.origin;
+               else
+                       pos = mf.origin;
+
+               // Try to get it if closer than the enemy base
+               if(vlen(self.origin-ef.dropped_origin)>vlen(self.origin-pos))
+               {
+                       havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
+                       return;
+               }
+       }
+
+       // Escort flag carrier
+       if(ef.ctf_status!=FLAG_BASE)
+       {
+               if(ef.tag_entity)
+                       pos = ef.tag_entity.origin;
+               else
+                       pos = ef.origin;
+
+               if(vlen(pos-mf.dropped_origin)>700)
+               {
+                       havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_ESCORT);
+                       return;
+               }
+       }
+
+       // About to fail, switch to middlefield
+       if(self.health<50)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_MIDDLE);
+               return;
+       }
+
+       // Set the role timeout if necessary
+       if (!self.havocbot_role_timeout)
+               self.havocbot_role_timeout = time + 120;
+
+       if (time > self.havocbot_role_timeout)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.bot_strategytime < time)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+               navigation_goalrating_start();
+               havocbot_goalrating_ctf_ourstolenflag(50000);
+               havocbot_goalrating_ctf_enemybase(20000);
+               havocbot_goalrating_items(5000, self.origin, 1000);
+               havocbot_goalrating_items(1000, self.origin, 10000);
+               navigation_goalrating_end();
+       }
+}
+
+// Retriever (temporary role):
+void havocbot_role_ctf_retriever()
+{
+       entity mf;
+
+       if(self.deadflag != DEAD_NO)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.flagcarried)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
+               return;
+       }
+
+       // If flag is back on the base switch to previous role
+       mf = havocbot_ctf_find_flag(self);
+       if(mf.ctf_status==FLAG_BASE)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (!self.havocbot_role_timeout)
+               self.havocbot_role_timeout = time + 20;
+
+       if (time > self.havocbot_role_timeout)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.bot_strategytime < time)
+       {
+               float rt_radius;
+               rt_radius = 10000;
+
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+               navigation_goalrating_start();
+               havocbot_goalrating_ctf_ourstolenflag(50000);
+               havocbot_goalrating_ctf_droppedflags(40000, self.origin, rt_radius);
+               havocbot_goalrating_ctf_enemybase(30000);
+               havocbot_goalrating_items(500, self.origin, rt_radius);
+               navigation_goalrating_end();
+       }
+}
+
+void havocbot_role_ctf_middle()
+{
+       entity mf;
+
+       if(self.deadflag != DEAD_NO)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.flagcarried)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
+               return;
+       }
+
+       mf = havocbot_ctf_find_flag(self);
+       if(mf.ctf_status!=FLAG_BASE)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
+               return;
+       }
+
+       if (!self.havocbot_role_timeout)
+               self.havocbot_role_timeout = time + 10;
+
+       if (time > self.havocbot_role_timeout)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.bot_strategytime < time)
+       {
+               vector org;
+
+               org = havocbot_ctf_middlepoint;
+               org_z = self.origin_z;
+
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+               navigation_goalrating_start();
+               havocbot_goalrating_ctf_ourstolenflag(50000);
+               havocbot_goalrating_ctf_droppedflags(30000, self.origin, 10000);
+               havocbot_goalrating_enemyplayers(10000, org, havocbot_ctf_middlepoint_radius * 0.5);
+               havocbot_goalrating_items(5000, org, havocbot_ctf_middlepoint_radius * 0.5);
+               havocbot_goalrating_items(2500, self.origin, 10000);
+               havocbot_goalrating_ctf_enemybase(2500);
+               navigation_goalrating_end();
+       }
+}
+
+void havocbot_role_ctf_defense()
+{
+       entity mf;
+
+       if(self.deadflag != DEAD_NO)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+
+       if (self.flagcarried)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_CARRIER);
+               return;
+       }
+
+       // If own flag was captured
+       mf = havocbot_ctf_find_flag(self);
+       if(mf.ctf_status!=FLAG_BASE)
+       {
+               havocbot_role_ctf_setrole(self, HAVOCBOT_CTF_ROLE_RETRIEVER);
+               return;
+       }
+
+       if (!self.havocbot_role_timeout)
+               self.havocbot_role_timeout = time + 30;
+
+       if (time > self.havocbot_role_timeout)
+       {
+               havocbot_ctf_reset_role(self);
+               return;
+       }
+       if (self.bot_strategytime < time)
+       {
+               float mp_radius;
+               vector org;
+
+               org = mf.dropped_origin;
+               mp_radius = havocbot_ctf_middlepoint_radius;
+
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+               navigation_goalrating_start();
+
+               // if enemies are closer to our base, go there
+               entity head, closestplayer = world;
+               float distance, bestdistance = 10000;
+               FOR_EACH_PLAYER(head)
+               {
+                       if(head.deadflag!=DEAD_NO)
+                               continue;
+
+                       distance = vlen(org - head.origin);
+                       if(distance<bestdistance)
+                       {
+                               closestplayer = head;
+                               bestdistance = distance;
+                       }
+               }
+
+               if(closestplayer)
+               if(closestplayer.team!=self.team)
+               if(vlen(org - self.origin)>1000)
+               if(checkpvs(self.origin,closestplayer)||random()<0.5)
+                       havocbot_goalrating_ctf_ourbase(30000);
+
+               havocbot_goalrating_ctf_ourstolenflag(20000);
+               havocbot_goalrating_ctf_droppedflags(20000, org, mp_radius);
+               havocbot_goalrating_enemyplayers(15000, org, mp_radius);
+               havocbot_goalrating_items(10000, org, mp_radius);
+               havocbot_goalrating_items(5000, self.origin, 10000);
+               navigation_goalrating_end();
+       }
+}
+
+void havocbot_role_ctf_setrole(entity bot, float role)
+{
+       dprint(strcat(bot.netname," switched to "));
+       switch(role)
+       {
+               case HAVOCBOT_CTF_ROLE_CARRIER:
+                       dprint("carrier");
+                       bot.havocbot_role = havocbot_role_ctf_carrier;
+                       bot.havocbot_role_timeout = 0;
+                       bot.havocbot_cantfindflag = time + 10;
+                       bot.bot_strategytime = 0;
+                       break;
+               case HAVOCBOT_CTF_ROLE_DEFENSE:
+                       dprint("defense");
+                       bot.havocbot_role = havocbot_role_ctf_defense;
+                       bot.havocbot_role_timeout = 0;
+                       break;
+               case HAVOCBOT_CTF_ROLE_MIDDLE:
+                       dprint("middle");
+                       bot.havocbot_role = havocbot_role_ctf_middle;
+                       bot.havocbot_role_timeout = 0;
+                       break;
+               case HAVOCBOT_CTF_ROLE_OFFENSE:
+                       dprint("offense");
+                       bot.havocbot_role = havocbot_role_ctf_offense;
+                       bot.havocbot_role_timeout = 0;
+                       break;
+               case HAVOCBOT_CTF_ROLE_RETRIEVER:
+                       dprint("retriever");
+                       bot.havocbot_previous_role = bot.havocbot_role;
+                       bot.havocbot_role = havocbot_role_ctf_retriever;
+                       bot.havocbot_role_timeout = time + 10;
+                       bot.bot_strategytime = 0;
+                       break;
+               case HAVOCBOT_CTF_ROLE_ESCORT:
+                       dprint("escort");
+                       bot.havocbot_previous_role = bot.havocbot_role;
+                       bot.havocbot_role = havocbot_role_ctf_escort;
+                       bot.havocbot_role_timeout = time + 30;
+                       bot.bot_strategytime = 0;
+                       break;
+       }
+       dprint("\n");
+}
+
+
+// ==============
+// Hook Functions
+// ==============
+
+MUTATOR_HOOKFUNCTION(ctf_PlayerPreThink)
+{
+       entity flag;
+       
+       // initially clear items so they can be set as necessary later.
+       self.items &~= (IT_RED_FLAG_CARRYING | IT_RED_FLAG_TAKEN | IT_RED_FLAG_LOST 
+               | IT_BLUE_FLAG_CARRYING | IT_BLUE_FLAG_TAKEN | IT_BLUE_FLAG_LOST | IT_CTF_SHIELDED);
+
+       // scan through all the flags and notify the client about them 
+       for(flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext)
+       {
+               switch(flag.ctf_status)
+               {
+                       case FLAG_PASSING:
+                       case FLAG_CARRY:
+                       {
+                               if((flag.owner == self) || (flag.pass_sender == self))
+                                       self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_CARRYING : IT_BLUE_FLAG_CARRYING); // carrying: self is currently carrying the flag
+                               else 
+                                       self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_TAKEN : IT_BLUE_FLAG_TAKEN); // taken: someone on self's team is carrying the flag
+                               break;
+                       }
+                       case FLAG_DROPPED:
+                       {
+                               self.items |= ((flag.items & IT_KEY2) ? IT_RED_FLAG_LOST : IT_BLUE_FLAG_LOST); // lost: the flag is dropped somewhere on the map
+                               break;
+                       }
+               }
+       }
+       
+       // item for stopping players from capturing the flag too often
+       if(self.ctf_captureshielded)
+               self.items |= IT_CTF_SHIELDED;
+       
+       // update the health of the flag carrier waypointsprite
+       if(self.wps_flagcarrier) 
+               WaypointSprite_UpdateHealth(self.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(self.health, self.armorvalue, autocvar_g_balance_armor_blockpercent));
+       
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(ctf_PlayerDamage) // for changing damage and force values that are applied to players in g_damage.qc
+{
+       if(frag_attacker.flagcarried) // if the attacker is a flagcarrier
+       {
+               if(frag_target == frag_attacker) // damage done to yourself
+               {
+                       frag_damage *= autocvar_g_ctf_flagcarrier_selfdamagefactor;
+                       frag_force *= autocvar_g_ctf_flagcarrier_selfforcefactor;
+               }
+               else // damage done to everyone else
+               {
+                       frag_damage *= autocvar_g_ctf_flagcarrier_damagefactor;
+                       frag_force *= autocvar_g_ctf_flagcarrier_forcefactor;
+               }
+       }
+       else if(frag_target.flagcarried && (frag_target.deadflag == DEAD_NO) && IsDifferentTeam(frag_target, frag_attacker)) // if the target is a flagcarrier
+       {
+               if(autocvar_g_ctf_flagcarrier_auto_helpme_damage > ('1 0 0' * healtharmor_maxdamage(frag_target.health, frag_target.armorvalue, autocvar_g_balance_armor_blockpercent)))
+               if(time > frag_target.wps_helpme_time + autocvar_g_ctf_flagcarrier_auto_helpme_time)
+               {
+                       frag_target.wps_helpme_time = time;
+                       WaypointSprite_HelpMePing(frag_target.wps_flagcarrier);
+               }
+       }
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(ctf_PlayerDies)
+{
+       if((frag_attacker != frag_target) && (frag_attacker.classname == "player") && (frag_target.flagcarried))
+       {
+               PlayerTeamScore_AddScore(frag_attacker, autocvar_g_ctf_score_kill);
+               PlayerScore_Add(frag_attacker, SP_CTF_FCKILLS, 1);
+       }
+                               
+       if(frag_target.flagcarried)
+               { ctf_Handle_Throw(frag_target, world, DROP_NORMAL); }
+               
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(ctf_GiveFragsForKill)
+{
+       frag_score = 0;
+       return (autocvar_g_ctf_ignore_frags); // no frags counted in ctf if this is true
+}
+
+MUTATOR_HOOKFUNCTION(ctf_RemovePlayer)
+{
+       entity flag; // temporary entity for the search method
+       
+       if(self.flagcarried)
+               { ctf_Handle_Throw(self, world, DROP_NORMAL); }
+       
+       for(flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext)
+       {
+               if(flag.pass_sender == self) { flag.pass_sender = world; }
+               if(flag.pass_target == self) { flag.pass_target = world; }
+               if(flag.ctf_dropper == self) { flag.ctf_dropper = world; }
+       }
+               
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(ctf_PortalTeleport)
+{
+       if(self.flagcarried) 
+       if(!autocvar_g_ctf_portalteleport)
+               { ctf_Handle_Throw(self, world, DROP_NORMAL); }
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(ctf_PlayerUseKey)
+{
+       if(MUTATOR_RETURNVALUE || gameover) { return FALSE; }
+       
+       entity player = self;
+
+       if((time > player.throw_antispam) && (player.deadflag == DEAD_NO) && !player.speedrunning && (!player.vehicle || autocvar_g_ctf_allow_vehicle_touch))
+       {
+               // pass the flag to a team mate
+               if(autocvar_g_ctf_pass)
+               {
+                       entity head, closest_target;
+                       head = WarpZone_FindRadius(player.origin, autocvar_g_ctf_pass_radius, TRUE);
+                       
+                       while(head) // find the closest acceptable target to pass to
+                       {
+                               if(head.classname == "player" && head.deadflag == DEAD_NO)
+                               if(head != player && !IsDifferentTeam(head, player))
+                               if(!head.speedrunning && !head.vehicle)
+                               {
+                                       // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) 
+                                       vector head_center = WarpZone_UnTransformOrigin(head, CENTER_OR_VIEWOFS(head));
+                                       vector passer_center = CENTER_OR_VIEWOFS(player);
+                                       
+                                       if(ctf_CheckPassDirection(head_center, passer_center, player.v_angle, head.WarpZone_findradius_nearest))
+                                       {
+                                               if(autocvar_g_ctf_pass_request && !player.flagcarried && head.flagcarried) 
+                                               { 
+                                                       if(clienttype(head) == CLIENTTYPE_BOT)
+                                                       {
+                                                               centerprint(player, strcat("Requesting ", head.netname, " to pass you the ", head.flagcarried.netname)); 
+                                                               ctf_Handle_Throw(head, player, DROP_PASS);
+                                                       }
+                                                       else
+                                                       {
+                                                               centerprint(head, strcat(player.netname, " requests you to pass the ", head.flagcarried.netname)); 
+                                                               centerprint(player, strcat("Requesting ", head.netname, " to pass you the ", head.flagcarried.netname)); 
+                                                       }
+                                                       player.throw_antispam = time + autocvar_g_ctf_pass_wait; 
+                                                       return TRUE; 
+                                               }
+                                               else if(player.flagcarried)
+                                               {
+                                                       if(closest_target)
+                                                       {
+                                                               vector closest_target_center = WarpZone_UnTransformOrigin(closest_target, CENTER_OR_VIEWOFS(closest_target));
+                                                               if(vlen(passer_center - head_center) < vlen(passer_center - closest_target_center))
+                                                                       { closest_target = head; }
+                                                       }
+                                                       else { closest_target = head; }
+                                               }
+                                       }
+                               }
+                               head = head.chain;
+                       }
+                       
+                       if(closest_target) { ctf_Handle_Throw(player, closest_target, DROP_PASS); return TRUE; }
+               }
+               
+               // throw the flag in front of you
+               if(autocvar_g_ctf_throw && player.flagcarried)
+               {
+                       if(player.throw_count == -1)
+                       {
+                               if(time > player.throw_prevtime + autocvar_g_ctf_throw_punish_delay)
+                               {
+                                       player.throw_prevtime = time;
+                                       player.throw_count = 1;
+                                       ctf_Handle_Throw(player, world, DROP_THROW);
+                                       return TRUE;
+                               }
+                               else
+                               {
+                                       centerprint(player, strcat("Too many flag throws, throwing disabled for ", ftos(rint((player.throw_prevtime + autocvar_g_ctf_throw_punish_delay) - time)), " seconds."));
+                                       return FALSE;
+                               }
+                       }
+                       else
+                       {
+                               if(time > player.throw_prevtime + autocvar_g_ctf_throw_punish_time) { player.throw_count = 1; }
+                               else { player.throw_count += 1; }
+                               if(player.throw_count >= autocvar_g_ctf_throw_punish_count) { player.throw_count = -1; }
+                                       
+                               player.throw_prevtime = time;
+                               ctf_Handle_Throw(player, world, DROP_THROW);
+                               return TRUE;
+                       }
+               }
+       }
+               
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(ctf_HelpMePing)
+{
+       if(self.wps_flagcarrier) // update the flagcarrier waypointsprite with "NEEDING HELP" notification
+       {
+               self.wps_helpme_time = time;
+               WaypointSprite_HelpMePing(self.wps_flagcarrier);
+       } 
+       else // create a normal help me waypointsprite
+       {
+               WaypointSprite_Spawn("helpme", waypointsprite_deployed_lifetime, waypointsprite_limitedrange, self, FLAG_WAYPOINT_OFFSET, world, self.team, self, wps_helpme, FALSE, RADARICON_HELPME, '1 0.5 0');
+               WaypointSprite_Ping(self.wps_helpme);
+       }
+
+       return TRUE;
+}
+
+MUTATOR_HOOKFUNCTION(ctf_VehicleEnter)
+{
+       if(vh_player.flagcarried)
+       {
+               if(!autocvar_g_ctf_allow_vehicle_carry && !autocvar_g_ctf_allow_vehicle_touch)
+               {
+                       ctf_Handle_Throw(vh_player, world, DROP_NORMAL);
+               }
+               else
+               {            
+                       setattachment(vh_player.flagcarried, vh_vehicle, ""); 
+                       setorigin(vh_player.flagcarried, VEHICLE_FLAG_OFFSET);
+                       vh_player.flagcarried.scale = VEHICLE_FLAG_SCALE;
+                       //vh_player.flagcarried.angles = '0 0 0';       
+               }
+               return TRUE;
+       }
+               
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(ctf_VehicleExit)
+{
+       if(vh_player.flagcarried)
+       {
+               setattachment(vh_player.flagcarried, vh_player, ""); 
+               setorigin(vh_player.flagcarried, FLAG_CARRY_OFFSET);
+               vh_player.flagcarried.scale = FLAG_SCALE;
+               vh_player.flagcarried.angles = '0 0 0';
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(ctf_AbortSpeedrun)
+{
+       if(self.flagcarried)
+       {
+               bprint("The ", self.flagcarried.netname, " was returned to base by its carrier\n");
+               ctf_RespawnFlag(self);
+               return TRUE;
+       }
+       
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(ctf_MatchEnd)
+{
+       entity flag; // temporary entity for the search method
+       
+       for(flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext)
+       {
+               switch(flag.ctf_status)
+               {
+                       case FLAG_DROPPED:
+                       case FLAG_PASSING:
+                       {
+                               // lock the flag, game is over
+                               flag.movetype = MOVETYPE_NONE;
+                               flag.takedamage = DAMAGE_NO;
+                               flag.solid = SOLID_NOT;
+                               flag.nextthink = FALSE; // stop thinking
+                               
+                               print("stopping the ", flag.netname, " from moving.\n");
+                               break;
+                       }
+                       
+                       default:
+                       case FLAG_BASE:
+                       case FLAG_CARRY:
+                       {
+                               // do nothing for these flags
+                               break;
+                       }
+               }
+       }
+       
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(ctf_BotRoles)
+{
+       havocbot_ctf_reset_role(self);
+       return TRUE;
+}
+
+
+// ==========
+// Spawnfuncs
+// ==========
+
+/*QUAKED spawnfunc_info_player_team1 (1 0 0) (-16 -16 -24) (16 16 24)
+CTF Starting point for a player in team one (Red).
+Keys: "angle" viewing angle when spawning. */
+void spawnfunc_info_player_team1()
+{
+       if(g_assault) { remove(self); return; }
+       
+       self.team = COLOR_TEAM1; // red
+       spawnfunc_info_player_deathmatch();
+}
+
+
+/*QUAKED spawnfunc_info_player_team2 (1 0 0) (-16 -16 -24) (16 16 24)
+CTF Starting point for a player in team two (Blue).
+Keys: "angle" viewing angle when spawning. */
+void spawnfunc_info_player_team2()
+{
+       if(g_assault) { remove(self); return; }
+       
+       self.team = COLOR_TEAM2; // blue
+       spawnfunc_info_player_deathmatch();
+}
+
+/*QUAKED spawnfunc_info_player_team3 (1 0 0) (-16 -16 -24) (16 16 24)
+CTF Starting point for a player in team three (Yellow).
+Keys: "angle" viewing angle when spawning. */
+void spawnfunc_info_player_team3()
+{
+       if(g_assault) { remove(self); return; }
+       
+       self.team = COLOR_TEAM3; // yellow
+       spawnfunc_info_player_deathmatch();
+}
+
+
+/*QUAKED spawnfunc_info_player_team4 (1 0 0) (-16 -16 -24) (16 16 24)
+CTF Starting point for a player in team four (Purple).
+Keys: "angle" viewing angle when spawning. */
+void spawnfunc_info_player_team4()
+{
+       if(g_assault) { remove(self); return; }
+       
+       self.team = COLOR_TEAM4; // purple
+       spawnfunc_info_player_deathmatch();
+}
+
+/*QUAKED spawnfunc_item_flag_team1 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
+CTF flag for team one (Red).
+Keys: 
+"angle" Angle the flag will point (minus 90 degrees)... 
+"model" model to use, note this needs red and blue as skins 0 and 1...
+"noise" sound played when flag is picked up...
+"noise1" sound played when flag is returned by a teammate...
+"noise2" sound played when flag is captured...
+"noise3" sound played when flag is lost in the field and respawns itself... 
+"noise4" sound played when flag is dropped by a player...
+"noise5" sound played when flag touches the ground... */
+void spawnfunc_item_flag_team1()
+{
+       if(!g_ctf) { remove(self); return; }
+
+       ctf_FlagSetup(1, self); // 1 = red
+}
+
+/*QUAKED spawnfunc_item_flag_team2 (0 0.5 0.8) (-48 -48 -37) (48 48 37)
+CTF flag for team two (Blue).
+Keys: 
+"angle" Angle the flag will point (minus 90 degrees)... 
+"model" model to use, note this needs red and blue as skins 0 and 1...
+"noise" sound played when flag is picked up...
+"noise1" sound played when flag is returned by a teammate...
+"noise2" sound played when flag is captured...
+"noise3" sound played when flag is lost in the field and respawns itself... 
+"noise4" sound played when flag is dropped by a player...
+"noise5" sound played when flag touches the ground... */
+void spawnfunc_item_flag_team2()
+{
+       if(!g_ctf) { remove(self); return; }
+
+       ctf_FlagSetup(0, self); // the 0 is misleading, but -- 0 = blue.
+}
+
+/*QUAKED spawnfunc_ctf_team (0 .5 .8) (-16 -16 -24) (16 16 32)
+Team declaration for CTF gameplay, this allows you to decide what team names and control point models are used in your map.
+Note: If you use spawnfunc_ctf_team entities you must define at least 2!  However, unlike domination, you don't need to make a blank one too.
+Keys:
+"netname" Name of the team (for example Red, Blue, Green, Yellow, Life, Death, Offense, Defense, etc)...
+"cnt" Scoreboard color of the team (for example 4 is red and 13 is blue)... */
+void spawnfunc_ctf_team()
+{
+       if(!g_ctf) { remove(self); return; }
+       
+       self.classname = "ctf_team";
+       self.team = self.cnt + 1;
+}
+
+// compatibility for quake maps
+void spawnfunc_team_CTF_redflag()    { spawnfunc_item_flag_team1();    }
+void spawnfunc_team_CTF_blueflag()   { spawnfunc_item_flag_team2();    }
+void spawnfunc_team_CTF_redplayer()  { spawnfunc_info_player_team1();  }
+void spawnfunc_team_CTF_blueplayer() { spawnfunc_info_player_team2();  }
+void spawnfunc_team_CTF_redspawn()   { spawnfunc_info_player_team1();  }
+void spawnfunc_team_CTF_bluespawn()  { spawnfunc_info_player_team2();  }
+
+
+// ==============
+// Initialization
+// ==============
+
+// scoreboard setup
+void ctf_ScoreRules()
+{
+       ScoreRules_basics(2, SFL_SORT_PRIO_PRIMARY, 0, TRUE);
+       ScoreInfo_SetLabel_TeamScore  (ST_CTF_CAPS,     "caps",      SFL_SORT_PRIO_PRIMARY);
+       ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPS,     "caps",      SFL_SORT_PRIO_SECONDARY);
+       ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPTIME,  "captime",   SFL_LOWER_IS_BETTER | SFL_TIME);
+       ScoreInfo_SetLabel_PlayerScore(SP_CTF_PICKUPS,  "pickups",   0);
+       ScoreInfo_SetLabel_PlayerScore(SP_CTF_FCKILLS,  "fckills",   0);
+       ScoreInfo_SetLabel_PlayerScore(SP_CTF_RETURNS,  "returns",   0);
+       ScoreInfo_SetLabel_PlayerScore(SP_CTF_DROPS,    "drops",     SFL_LOWER_IS_BETTER);
+       ScoreRules_basics_end();
+}
+
+// code from here on is just to support maps that don't have flag and team entities
+void ctf_SpawnTeam (string teamname, float teamcolor)
+{
+       entity oldself;
+       oldself = self;
+       self = spawn();
+       self.classname = "ctf_team";
+       self.netname = teamname;
+       self.cnt = teamcolor;
+
+       spawnfunc_ctf_team();
+
+       self = oldself;
+}
+
+void ctf_DelayedInit() // Do this check with a delay so we can wait for teams to be set up.
+{
+       // if no teams are found, spawn defaults
+       if(find(world, classname, "ctf_team") == world)
+       {
+               print("No ""ctf_team"" entities found on this map, creating them anyway.\n");
+               ctf_SpawnTeam("Red", COLOR_TEAM1 - 1);
+               ctf_SpawnTeam("Blue", COLOR_TEAM2 - 1);
+       }
+       
+       ctf_ScoreRules();
+}
+
+void ctf_Initialize()
+{
+       ctf_captimerecord = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time")));
+
+       ctf_captureshield_min_negscore = autocvar_g_ctf_shield_min_negscore;
+       ctf_captureshield_max_ratio = autocvar_g_ctf_shield_max_ratio;
+       ctf_captureshield_force = autocvar_g_ctf_shield_force;
+       
+       InitializeEntity(world, ctf_DelayedInit, INITPRIO_GAMETYPE);
+}
+
+
+MUTATOR_DEFINITION(gamemode_ctf)
+{
+       MUTATOR_HOOK(MakePlayerObserver, ctf_RemovePlayer, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ClientDisconnect, ctf_RemovePlayer, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerDies, ctf_PlayerDies, CBC_ORDER_ANY);
+       MUTATOR_HOOK(MatchEnd, ctf_MatchEnd, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PortalTeleport, ctf_PortalTeleport, CBC_ORDER_ANY);
+       MUTATOR_HOOK(GiveFragsForKill, ctf_GiveFragsForKill, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerPreThink, ctf_PlayerPreThink, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerDamage_Calculate, ctf_PlayerDamage, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerUseKey, ctf_PlayerUseKey, CBC_ORDER_ANY);
+       MUTATOR_HOOK(HelpMePing, ctf_HelpMePing, CBC_ORDER_ANY);
+       MUTATOR_HOOK(VehicleEnter, ctf_VehicleEnter, CBC_ORDER_ANY);
+       MUTATOR_HOOK(VehicleExit, ctf_VehicleExit, CBC_ORDER_ANY);
+       MUTATOR_HOOK(AbortSpeedrun, ctf_AbortSpeedrun, CBC_ORDER_ANY);
+       MUTATOR_HOOK(HavocBot_ChooseRule, ctf_BotRoles, CBC_ORDER_ANY);
+       
+       MUTATOR_ONADD
+       {
+               if(time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+               g_ctf = 1;
+               ctf_Initialize();
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               g_ctf = 0;
+               error("This is a game type and it cannot be removed at runtime.");
+       }
+
+       return 0;
+}
diff --git a/qcsrc/server/mutators/gamemode_ctf.qh b/qcsrc/server/mutators/gamemode_ctf.qh
new file mode 100644 (file)
index 0000000..b6ca033
--- /dev/null
@@ -0,0 +1,132 @@
+// these are needed since mutators are compiled last
+
+// used in cheats.qc
+void ctf_RespawnFlag(entity flag)
+
+// score rule declarations
+#define ST_CTF_CAPS 1
+#define SP_CTF_CAPS 4
+#define SP_CTF_CAPTIME 5
+#define SP_CTF_PICKUPS 6
+#define SP_CTF_DROPS 7
+#define SP_CTF_FCKILLS 8
+#define SP_CTF_RETURNS 9
+
+// flag constants // for most of these, there is just one question to be asked: WHYYYYY?
+#define FLAG_MIN (PL_MIN + '0 0 -13')
+#define FLAG_MAX (PL_MAX + '0 0 -13')
+
+#define FLAG_SCALE 0.6
+
+#define FLAG_THINKRATE 0.2
+#define FLAG_TOUCHRATE 0.5
+#define WPFE_THINKRATE 0.5
+
+#define FLAG_DROP_OFFSET ('0 0 32')
+#define FLAG_CARRY_OFFSET ('-16 0 8')
+#define FLAG_SPAWN_OFFSET ('0 0 1' * (PL_MAX_z - 13))
+#define FLAG_WAYPOINT_OFFSET ('0 0 64')
+#define FLAG_FLOAT_OFFSET ('0 0 32')
+#define FLAG_PASS_ARC_OFFSET ('0 0 -10')
+
+#define VEHICLE_FLAG_OFFSET ('0 0 96')
+#define VEHICLE_FLAG_SCALE 1.0
+
+// waypoint colors
+#define WPCOLOR_ENEMYFC(t) (colormapPaletteColor(t - 1, FALSE) * 0.75)
+#define WPCOLOR_FLAGCARRIER(t) ('0.8 0.8 0')
+#define WPCOLOR_DROPPEDFLAG(t) (('0.25 0.25 0.25' + colormapPaletteColor(t - 1, FALSE)) * 0.5)
+
+// sounds 
+#define snd_flag_taken noise
+#define snd_flag_returned noise1
+#define snd_flag_capture noise2
+#define snd_flag_respawn noise3
+.string snd_flag_dropped;
+.string snd_flag_touch;
+.string snd_flag_pass;
+
+// effects
+.string toucheffect;
+.string passeffect;
+.string capeffect;
+
+// list of flags on the map
+entity ctf_worldflaglist;
+.entity ctf_worldflagnext;
+.entity ctf_staleflagnext;
+
+// waypoint sprites
+.entity bot_basewaypoint; // flag waypointsprite
+.entity wps_helpme;
+.entity wps_flagbase; 
+.entity wps_flagcarrier;
+.entity wps_flagdropped;
+.entity wps_enemyflagcarrier;
+.float wps_helpme_time;
+float wpforenemy_announced;
+float wpforenemy_nextthink;
+
+// statuses
+#define FLAG_BASE 1
+#define FLAG_DROPPED 2
+#define FLAG_CARRY 3
+#define FLAG_PASSING 4
+
+#define DROP_NORMAL 1
+#define DROP_THROW 2
+#define DROP_PASS 3
+#define DROP_RESET 4
+
+#define PICKUP_BASE 1
+#define PICKUP_DROPPED 2
+
+#define CAPTURE_NORMAL 1
+#define CAPTURE_DROPPED 2
+
+#define RETURN_TIMEOUT 1
+#define RETURN_DROPPED 2
+#define RETURN_DAMAGE 3
+#define RETURN_SPEEDRUN 4
+#define RETURN_NEEDKILL 5
+
+// flag properties
+#define ctf_spawnorigin dropped_origin
+float ctf_stalemate; // indicates that a stalemate is active
+float ctf_captimerecord; // record time for capturing the flag
+.float ctf_pickuptime;
+.float ctf_droptime;
+.float ctf_status; // status of the flag (FLAG_BASE, FLAG_DROPPED, FLAG_CARRY declared globally)
+.entity ctf_dropper; // don't allow spam of dropping the flag
+.float max_flag_health;
+.float next_take_time;
+
+// passing/throwing properties
+.float pass_distance;
+.entity pass_sender;
+.entity pass_target;
+.float throw_antispam;
+.float throw_prevtime;
+.float throw_count;
+
+// CaptureShield: If the player is too bad to be allowed to capture, shield them from taking the flag.
+.float ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture
+float ctf_captureshield_min_negscore; // punish at -20 points
+float ctf_captureshield_max_ratio; // punish at most 30% of each team
+float ctf_captureshield_force; // push force of the shield
+
+// bot player logic
+#define HAVOCBOT_CTF_ROLE_NONE 0
+#define HAVOCBOT_CTF_ROLE_DEFENSE 2
+#define HAVOCBOT_CTF_ROLE_MIDDLE 4
+#define HAVOCBOT_CTF_ROLE_OFFENSE 8
+#define HAVOCBOT_CTF_ROLE_CARRIER 16
+#define HAVOCBOT_CTF_ROLE_RETRIEVER 32
+#define HAVOCBOT_CTF_ROLE_ESCORT 64
+
+.float havocbot_cantfindflag;
+
+vector havocbot_ctf_middlepoint;
+float havocbot_ctf_middlepoint_radius;
+
+void havocbot_role_ctf_setrole(entity bot, float role);
index 2a2ae9ee2900fea47c6d90233a5fbae180281d96..aedfd6364c942ef5f1d967b38d0dac04fa8d202f 100644 (file)
@@ -27,7 +27,7 @@ void freezetag_CheckWinner()
 
        FOR_EACH_PLAYER(e)
        {
-               if(e.freezetag_frozen == 0 && e.classname == "player" && e.health >= 1) // here's one player from the winning team... good
+               if(e.freezetag_frozen == 0 && e.health >= 1) // here's one player from the winning team... good
                {
                        winner = e;
                        break; // break, we found the winner
@@ -129,9 +129,118 @@ void freezetag_Unfreeze(entity attacker)
                WaypointSprite_Kill(self.waypointsprite_attached);
 }
 
+
+// ================
+// Bot player logic
+// ================
+
+void() havocbot_role_ft_freeing;
+void() havocbot_role_ft_offense;
+
+void havocbot_goalrating_freeplayers(float ratingscale, vector org, float sradius)
+{
+       entity head;
+       float distance;
+
+       FOR_EACH_PLAYER(head)
+       {
+               if ((head != self) && (head.team == self.team))
+               {
+                       if (head.freezetag_frozen)
+                       {
+                               distance = vlen(head.origin - org);
+                               if (distance > sradius)
+                                       continue;
+                               navigation_routerating(head, ratingscale, 2000);
+                       }
+                       else
+                       {
+                               // If teamate is not frozen still seek them out as fight better
+                               // in a group.
+                               navigation_routerating(head, ratingscale/3, 2000);
+                       }
+               }
+       }
+}
+
+void havocbot_role_ft_offense()
+{
+       entity head;
+       float unfrozen;
+
+       if(self.deadflag != DEAD_NO)
+               return;
+
+       if (!self.havocbot_role_timeout)
+               self.havocbot_role_timeout = time + random() * 10 + 20;
+
+       // Count how many players on team are unfrozen.
+       unfrozen = 0;
+       FOR_EACH_PLAYER(head)
+       {
+               if ((head.team == self.team) && (!head.freezetag_frozen))
+                       unfrozen++;
+       }
+
+       // If only one left on team or if role has timed out then start trying to free players.
+       if (((unfrozen == 0) && (!self.freezetag_frozen)) || (time > self.havocbot_role_timeout))
+       {
+               dprint("changing role to freeing\n");
+               self.havocbot_role = havocbot_role_ft_freeing;
+               self.havocbot_role_timeout = 0;
+               return;
+       }
+
+       if (time > self.bot_strategytime)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+
+               navigation_goalrating_start();
+               havocbot_goalrating_items(10000, self.origin, 10000);
+               havocbot_goalrating_enemyplayers(20000, self.origin, 10000);
+               havocbot_goalrating_freeplayers(9000, self.origin, 10000);
+               //havocbot_goalrating_waypoints(1, self.origin, 1000);
+               navigation_goalrating_end();
+       }
+}
+
+void havocbot_role_ft_freeing()
+{
+       if(self.deadflag != DEAD_NO)
+               return;
+
+       if (!self.havocbot_role_timeout)
+               self.havocbot_role_timeout = time + random() * 10 + 20;
+
+       if (time > self.havocbot_role_timeout)
+       {
+               dprint("changing role to offense\n");
+               self.havocbot_role = havocbot_role_ft_offense;
+               self.havocbot_role_timeout = 0;
+               return;
+       }
+
+       if (time > self.bot_strategytime)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+
+               navigation_goalrating_start();
+               havocbot_goalrating_items(8000, self.origin, 10000);
+               havocbot_goalrating_enemyplayers(10000, self.origin, 10000);
+               havocbot_goalrating_freeplayers(20000, self.origin, 10000);
+               //havocbot_goalrating_waypoints(1, self.origin, 1000);
+               navigation_goalrating_end();
+       }
+}
+
+
+// ==============
+// Hook Functions
+// ==============
+
 MUTATOR_HOOKFUNCTION(freezetag_RemovePlayer)
 {
-       if(self.freezetag_frozen == 0)
+       if(self.freezetag_frozen == 0 && self.health >= 1)
        {
                if(self.team == COLOR_TEAM1)
                        --redalive;
@@ -166,22 +275,21 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
                        --pinkalive;
                --totalalive;
 
-        freezetag_Freeze(frag_attacker);
+               freezetag_Freeze(frag_attacker);
        }
 
-    if(frag_attacker.classname == STR_PLAYER)
-        centerprint(frag_attacker, strcat("^2You froze ^7", frag_target.netname, ".\n"));
-
        if(frag_attacker == frag_target || frag_attacker == world)
        {
-        if(frag_target.classname == STR_PLAYER)
-            centerprint(frag_target, "^1You froze yourself.\n");
+               if(frag_target.classname == STR_PLAYER)
+                       centerprint(frag_target, "^1You froze yourself.\n");
                bprint("^7", frag_target.netname, "^1 froze himself.\n");
        }
        else
        {
-        if(frag_target.classname == STR_PLAYER)
-            centerprint(frag_target, strcat("^1You were frozen by ^7", frag_attacker.netname, ".\n"));
+               if(frag_target.classname == STR_PLAYER)
+                       centerprint(frag_target, strcat("^1You were frozen by ^7", frag_attacker.netname, ".\n"));
+               if(frag_attacker.classname == STR_PLAYER)
+                       centerprint(frag_attacker, strcat("^2You froze ^7", frag_target.netname, ".\n"));
                bprint("^7", frag_target.netname, "^1 was frozen by ^7", frag_attacker.netname, ".\n");
        }
 
@@ -194,7 +302,7 @@ MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
 
 MUTATOR_HOOKFUNCTION(freezetag_PlayerSpawn)
 {
-    freezetag_Unfreeze(world); // start by making sure that all ice blocks are removed
+       freezetag_Unfreeze(world); // start by making sure that all ice blocks are removed
 
        if(total_players == 1 && time > game_starttime) // only one player active on server, start a new match immediately
        if(!next_round && warmup && (time < warmup - autocvar_g_freezetag_warmup || time > warmup)) // not awaiting next round
@@ -335,6 +443,19 @@ MUTATOR_HOOKFUNCTION(freezetag_ForbidThrowCurrentWeapon)
        return 0;
 }
 
+MUTATOR_HOOKFUNCTION(freezetag_BotRoles)
+{
+       if not(self.deadflag)
+       {
+               if (random() < 0.5)
+                       self.havocbot_role = havocbot_role_ft_freeing;
+               else
+                       self.havocbot_role = havocbot_role_ft_offense;
+       }
+       
+       return TRUE;
+}
+
 MUTATOR_DEFINITION(gamemode_freezetag)
 {
        MUTATOR_HOOK(MakePlayerObserver, freezetag_RemovePlayer, CBC_ORDER_ANY);
@@ -346,6 +467,7 @@ MUTATOR_DEFINITION(gamemode_freezetag)
        MUTATOR_HOOK(PlayerPhysics, freezetag_PlayerPhysics, CBC_ORDER_FIRST);
        MUTATOR_HOOK(PlayerDamage_Calculate, freezetag_PlayerDamage_Calculate, CBC_ORDER_ANY);
        MUTATOR_HOOK(ForbidThrowCurrentWeapon, freezetag_ForbidThrowCurrentWeapon, CBC_ORDER_FIRST); //first, last or any? dunno.
+       MUTATOR_HOOK(HavocBot_ChooseRule, freezetag_BotRoles, CBC_ORDER_ANY);
 
        MUTATOR_ONADD
        {
index 1f6a82b84b7c9475a29f5d1fdfcd21aa5d624720..07c96671c67e28b0eddc529b6e83bb58aa79d369 100644 (file)
@@ -1,61 +1,23 @@
-void ka_SpawnBall(void);
-void ka_TouchEvent(void);
-void ka_RespawnBall(void);
-void ka_DropEvent(entity);
-void ka_TimeScoring(void);
-void ka_EventLog(string, entity);
+// ===========================================================
+//  Keepaway game mode coding, written by Samual and Diabolik
+//  Last updated: September, 2012
+// ===========================================================
 
-entity ka_ball;
-
-float ka_ballcarrier_waypointsprite_visible_for_player(entity);
-
-void ka_Initialize() // run at the start of a match, initiates game mode
+float ka_ballcarrier_waypointsprite_visible_for_player(entity e) // runs on waypoints which are attached to ballcarriers, updates once per frame 
 {
-       if(!g_keepaway)
-               return;
+       if(e.ballcarried)
+               if(other.classname == "spectator") 
+                       return FALSE; // we don't want spectators of the ballcarrier to see the attached waypoint on the top of their screen
                
-       precache_sound("keepaway/pickedup.wav");
-       precache_sound("keepaway/dropped.wav");
-       precache_sound("keepaway/respawn.wav");
-       precache_sound("keepaway/touch.wav");
-
-       ScoreRules_keepaway();
-       ka_SpawnBall();
-}
-
-void ka_Reset() // used to clear the ballcarrier whenever the match switches from warmup to normal
-{
-       if(self.owner)
-               if(self.owner.classname == "player")
-                       ka_DropEvent(self.owner);
+       // TODO: Make the ballcarrier lack a waypointsprite whenever they have the invisibility powerup
 
-       ka_RespawnBall();
+       return TRUE;
 }
 
-void ka_SpawnBall() // loads various values for the ball, runs only once at start of match
+void ka_EventLog(string mode, entity actor) // use an alias for easy changing and quick editing later
 {
-       if(!g_keepaway) { return; }
-       
-       entity e;
-       e = spawn();
-       e.model = "models/orbs/orbblue.md3";    
-       precache_model(e.model);
-       setmodel(e, e.model);
-       setsize(e, '-16 -16 -20', '16 16 20'); // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off
-       e.classname = "keepawayball";
-       e.damageforcescale = autocvar_g_keepawayball_damageforcescale;
-       e.takedamage = DAMAGE_YES;
-       e.solid = SOLID_TRIGGER;
-       e.movetype = MOVETYPE_BOUNCE;
-       e.glow_color = autocvar_g_keepawayball_trail_color;
-       e.glow_trail = TRUE;
-       e.flags = FL_ITEM;
-       e.reset = ka_Reset;
-       e.touch = ka_TouchEvent;
-       e.owner = world;
-       ka_ball = e;
-
-       InitializeEntity(e, ka_RespawnBall, INITPRIO_SETLOCATION); // is this the right priority? Neh, I have no idea.. Well-- it works! So. 
+       if(autocvar_sv_eventlog)
+               GameLogEcho(strcat(":ka:", mode, ((actor != world) ? (strcat(":", ftos(actor.playerid))) : "")));
 }
 
 void ka_RespawnBall() // runs whenever the ball needs to be relocated
@@ -87,6 +49,18 @@ void ka_RespawnBall() // runs whenever the ball needs to be relocated
        }
 }
 
+void ka_TimeScoring()
+{
+       if(self.owner.ballcarried)
+       { // add points for holding the ball after a certain amount of time
+               if(autocvar_g_keepaway_score_timepoints)
+                       PlayerScore_Add(self.owner, SP_SCORE, autocvar_g_keepaway_score_timepoints);
+                       
+               PlayerScore_Add(self.owner, SP_KEEPAWAY_BCTIME, (autocvar_g_keepaway_score_timeinterval / 1)); // interval is divided by 1 so that time always shows "seconds"
+               self.nextthink = time + autocvar_g_keepaway_score_timeinterval;
+       }
+}
+
 void ka_TouchEvent() // runs any time that the ball comes in contact with something
 {
        if(gameover) { return; }
@@ -185,35 +159,90 @@ void ka_DropEvent(entity plyr) // runs any time that a player is supposed to los
        WaypointSprite_Kill(plyr.waypointsprite_attachedforcarrier);
 }
 
-float ka_ballcarrier_waypointsprite_visible_for_player(entity e) // runs on waypoints which are attached to ballcarriers, updates once per frame 
+void ka_Reset() // used to clear the ballcarrier whenever the match switches from warmup to normal
 {
-       if(e.ballcarried)
-               if(other.classname == "spectator") 
-                       return FALSE; // we don't want spectators of the ballcarrier to see the attached waypoint on the top of their screen
-               
-       // TODO: Make the ballcarrier lack a waypointsprite whenever they have the invisibility powerup
+       if((self.owner) && (self.owner.classname == "player"))
+               ka_DropEvent(self.owner);
 
-       return TRUE;
+       ka_RespawnBall();
 }
 
-void ka_TimeScoring()
+
+// ================
+// Bot player logic
+// ================
+
+void havocbot_goalrating_ball(float ratingscale, vector org)
 {
-       if(self.owner.ballcarried)
-       { // add points for holding the ball after a certain amount of time
-               if(autocvar_g_keepaway_score_timepoints)
-                       PlayerScore_Add(self.owner, SP_SCORE, autocvar_g_keepaway_score_timepoints);
-                       
-               PlayerScore_Add(self.owner, SP_KEEPAWAY_BCTIME, (autocvar_g_keepaway_score_timeinterval / 1)); // interval is divided by 1 so that time always shows "seconds"
-               self.nextthink = time + autocvar_g_keepaway_score_timeinterval;
+       float t;
+       entity ball_owner;
+       ball_owner = ka_ball.owner;
+
+       if (ball_owner == self)
+               return;
+
+       // If ball is carried by player then hunt them down.
+       if (ball_owner)
+       {
+               t = (self.health + self.armorvalue) / (ball_owner.health + ball_owner.armorvalue);
+               navigation_routerating(ball_owner, t * ratingscale, 2000);
        }
+
+       // Ball has been dropped so collect.
+       navigation_routerating(ka_ball, ratingscale, 2000);
 }
 
-void ka_EventLog(string mode, entity actor) // use an alias for easy changing and quick editing later
+void havocbot_role_ka_carrier()
 {
-       if(autocvar_sv_eventlog)
-               GameLogEcho(strcat(":ka:", mode, ((actor != world) ? (strcat(":", ftos(actor.playerid))) : "")));
+       if (self.deadflag != DEAD_NO)
+               return;
+
+       if (time > self.bot_strategytime)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+
+               navigation_goalrating_start();
+               havocbot_goalrating_items(10000, self.origin, 10000);
+               havocbot_goalrating_enemyplayers(20000, self.origin, 10000);
+               //havocbot_goalrating_waypoints(1, self.origin, 1000);
+               navigation_goalrating_end();
+       }
+
+       if (!self.ballcarried)
+       {
+               self.havocbot_role = havocbot_role_ka_collector;
+               self.bot_strategytime = 0;
+       }
+}
+
+void havocbot_role_ka_collector()
+{
+       if (self.deadflag != DEAD_NO)
+               return;
+
+       if (time > self.bot_strategytime)
+       {
+               self.bot_strategytime = time + autocvar_bot_ai_strategyinterval;
+
+               navigation_goalrating_start();
+               havocbot_goalrating_items(10000, self.origin, 10000);
+               havocbot_goalrating_enemyplayers(1000, self.origin, 10000);
+               havocbot_goalrating_ball(20000, self.origin);
+               navigation_goalrating_end();
+       }
+
+       if (self.ballcarried)
+       {
+               self.havocbot_role = havocbot_role_ka_carrier;
+               self.bot_strategytime = 0;
+       }
 }
 
+
+// ==============
+// Hook Functions
+// ==============
+
 MUTATOR_HOOKFUNCTION(ka_Scoring)
 {
        if((frag_attacker != frag_target) && (frag_attacker.classname == "player"))
@@ -314,6 +343,70 @@ MUTATOR_HOOKFUNCTION(ka_PlayerPowerups)
        return 0;
 }
 
+MUTATOR_HOOKFUNCTION(ka_BotRoles)
+{
+       if (self.ballcarried)
+               self.havocbot_role = havocbot_role_ka_carrier;
+       else
+               self.havocbot_role = havocbot_role_ka_collector;
+       return TRUE;
+}
+
+
+// ==============
+// Initialization
+// ==============
+
+void ka_SpawnBall() // loads various values for the ball, runs only once at start of match
+{
+       if(!g_keepaway) { return; }
+       
+       entity e;
+       e = spawn();
+       e.model = "models/orbs/orbblue.md3";    
+       precache_model(e.model);
+       setmodel(e, e.model);
+       setsize(e, '-16 -16 -20', '16 16 20'); // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off
+       e.classname = "keepawayball";
+       e.damageforcescale = autocvar_g_keepawayball_damageforcescale;
+       e.takedamage = DAMAGE_YES;
+       e.solid = SOLID_TRIGGER;
+       e.movetype = MOVETYPE_BOUNCE;
+       e.glow_color = autocvar_g_keepawayball_trail_color;
+       e.glow_trail = TRUE;
+       e.flags = FL_ITEM;
+       e.reset = ka_Reset;
+       e.touch = ka_TouchEvent;
+       e.owner = world;
+       ka_ball = e;
+
+       InitializeEntity(e, ka_RespawnBall, INITPRIO_SETLOCATION); // is this the right priority? Neh, I have no idea.. Well-- it works! So. 
+}
+
+void ka_ScoreRules()
+{
+       ScoreRules_basics(0, SFL_SORT_PRIO_PRIMARY, 0, TRUE); // SFL_SORT_PRIO_PRIMARY
+       ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_PICKUPS,                     "pickups",              0);
+       ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_CARRIERKILLS,        "bckills",              0);
+       ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_BCTIME,                      "bctime",               SFL_SORT_PRIO_SECONDARY);
+       ScoreRules_basics_end();
+}
+
+void ka_Initialize() // run at the start of a match, initiates game mode
+{
+       if(!g_keepaway)
+               return;
+               
+       precache_sound("keepaway/pickedup.wav");
+       precache_sound("keepaway/dropped.wav");
+       precache_sound("keepaway/respawn.wav");
+       precache_sound("keepaway/touch.wav");
+
+       ka_ScoreRules();
+       ka_SpawnBall();
+}
+
+
 MUTATOR_DEFINITION(gamemode_keepaway)
 {
        MUTATOR_HOOK(MakePlayerObserver, ka_RemovePlayer, CBC_ORDER_ANY);
@@ -324,6 +417,7 @@ MUTATOR_DEFINITION(gamemode_keepaway)
        MUTATOR_HOOK(PlayerDamage_Calculate, ka_PlayerDamage, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerPowerups, ka_PlayerPowerups, CBC_ORDER_ANY);
        MUTATOR_HOOK(PlayerUseKey, ka_PlayerUseKey, CBC_ORDER_ANY);
+       MUTATOR_HOOK(HavocBot_ChooseRule, ka_BotRoles, CBC_ORDER_ANY);
 
        MUTATOR_ONADD
        {
diff --git a/qcsrc/server/mutators/gamemode_keepaway.qh b/qcsrc/server/mutators/gamemode_keepaway.qh
new file mode 100644 (file)
index 0000000..062fc9e
--- /dev/null
@@ -0,0 +1,10 @@
+// these are needed since mutators are compiled last
+
+entity ka_ball;
+
+#define SP_KEEPAWAY_PICKUPS 4
+#define SP_KEEPAWAY_CARRIERKILLS 5
+#define SP_KEEPAWAY_BCTIME 6
+
+void() havocbot_role_ka_carrier;
+void() havocbot_role_ka_collector;
index ec8de781e257fa754167f2c028c3e6c5dc5ea3cc..808d1a29b35229d56931b9645642a524d95e833b 100644 (file)
@@ -117,7 +117,7 @@ void GiveBall(entity plyr, entity ball)
        ball.owner = ball.pusher = plyr; //"owner" is set to the player carrying, "pusher" to the last player who touched it
        ball.team = plyr.team;
        plyr.ballcarried = ball;
-       ball.dropperid = plyr.playerid;
+       ball.nb_dropper = plyr;
 
        plyr.effects |= autocvar_g_nexball_basketball_effects_default;
        ball.effects &~= autocvar_g_nexball_basketball_effects_default;
@@ -137,15 +137,15 @@ void GiveBall(entity plyr, entity ball)
                ball.nextthink = time + autocvar_g_nexball_basketball_delay_hold;
        }
        
-    ownr = self;
-    self = plyr;    
-    WEPSET_COPY_EE(self.weaponentity, self);
-    self.weaponentity.switchweapon = self.weapon;
-    WEPSET_COPY_EW(self, WEP_PORTO);
-    weapon_action(WEP_PORTO, WR_RESETPLAYER);
-    self.switchweapon = WEP_PORTO;
-    W_SwitchWeapon(WEP_PORTO);
-    self = ownr;
+       ownr = self;
+       self = plyr;    
+       WEPSET_COPY_EE(self.weaponentity, self);
+       self.weaponentity.switchweapon = self.weapon;
+       WEPSET_COPY_EW(self, WEP_PORTO);
+       weapon_action(WEP_PORTO, WR_RESETPLAYER);
+       self.switchweapon = WEP_PORTO;
+       W_SwitchWeapon(WEP_PORTO);
+       self = ownr;
 }
 
 void DropBall(entity ball, vector org, vector vel)
@@ -160,7 +160,7 @@ void DropBall(entity ball, vector org, vector vel)
        ball.flags &~= FL_ONGROUND;
        ball.scale = ball_scale;
        ball.velocity = vel;
-       ball.ctf_droptime = time;
+       ball.nb_droptime = time;
        ball.touch = basketball_touch;
        ball.think = ResetBall;
        ball.nextthink = min(time + autocvar_g_nexball_delay_idle, ball.teamtime);
@@ -201,7 +201,7 @@ void InitBall(void)
 
 void ResetBall(void)
 {
-       if(self.cnt < 2)    // step 1
+       if(self.cnt < 2)        // step 1
        {
                if(time == self.teamtime)
                        bprint("The ", ColoredTeamName(self.team), " held the ball for too long.\n");
@@ -213,14 +213,14 @@ void ResetBall(void)
                self.cnt = 2;
                self.nextthink = time;
        }
-       else if(self.cnt < 4)      // step 2 and 3
+       else if(self.cnt < 4)     // step 2 and 3
        {
 //             dprint("Step ", ftos(self.cnt), ": Calculated velocity: ", vtos(self.spawnorigin - self.origin), ", time: ", ftos(time), "\n");
                self.velocity = (self.spawnorigin - self.origin) * (self.cnt - 1); // 1 or 0.5 second movement
                self.nextthink = time + 0.5;
                self.cnt += 1;
        }
-       else     // step 4
+       else     // step 4
        {
 //             dprint("Step 4: time: ", ftos(time), "\n");
                if(vlen(self.origin - self.spawnorigin) > 10)  // should not happen anymore
@@ -257,22 +257,22 @@ void football_touch(void)
        self.pusher = other;
        self.team = other.team;
 
-       if(autocvar_g_nexball_football_physics == -1)    // MrBougo try 1, before decompiling Rev's original
+       if(autocvar_g_nexball_football_physics == -1)   // MrBougo try 1, before decompiling Rev's original
        {
                if(vlen(other.velocity))
                        self.velocity = other.velocity * 1.5 + '0 0 1' * autocvar_g_nexball_football_boost_up;
        }
-       else if(autocvar_g_nexball_football_physics == 1)      // MrBougo's modded Rev style: partially independant of the height of the aiming point
+       else if(autocvar_g_nexball_football_physics == 1)         // MrBougo's modded Rev style: partially independant of the height of the aiming point
        {
                makevectors(other.v_angle);
                self.velocity = other.velocity + v_forward * autocvar_g_nexball_football_boost_forward + '0 0 1' * autocvar_g_nexball_football_boost_up;
        }
-       else if(autocvar_g_nexball_football_physics == 2)      // 2nd mod try: totally independant. Really playable!
+       else if(autocvar_g_nexball_football_physics == 2)         // 2nd mod try: totally independant. Really playable!
        {
                makevectors(other.v_angle_y * '0 1 0');
                self.velocity = other.velocity + v_forward * autocvar_g_nexball_football_boost_forward + v_up * autocvar_g_nexball_football_boost_up;
        }
-       else     // Revenant's original style (from the original mod's disassembly, acknowledged by Revenant)
+       else     // Revenant's original style (from the original mod's disassembly, acknowledged by Revenant)
        {
                makevectors(other.v_angle);
                self.velocity = other.velocity + v_forward * autocvar_g_nexball_football_boost_forward + v_up * autocvar_g_nexball_football_boost_up;
@@ -287,7 +287,7 @@ void basketball_touch(void)
                football_touch();
                return;
        }
-       if(!self.cnt && other.classname == "player" && (other.playerid != self.dropperid || time > self.ctf_droptime + autocvar_g_nexball_delay_collect))
+       if(!self.cnt && other.classname == "player" && (other != self.nb_dropper || time > self.nb_droptime + autocvar_g_nexball_delay_collect))
        {
                if(other.health <= 0)
                        return;
@@ -331,7 +331,7 @@ void GoalTouch(void)
        else
                pname = "Someone (?)";
 
-       if(ball.team == self.team)         //owngoal (regular goals)
+       if(ball.team == self.team)               //owngoal (regular goals)
        {
                LogNB("owngoal", ball.pusher);
                bprint("Boo! ", pname, "^7 scored a goal against their own team!\n");
@@ -355,7 +355,7 @@ void GoalTouch(void)
                        bprint("The ball was returned.\n");
                pscore = 0;
        }
-       else                               //score
+       else                                                       //score
        {
                LogNB(strcat("goal:", ftos(self.team)), ball.pusher);
                bprint("Goaaaaal! ", pname, "^7 scored a point for the ", ColoredTeamName(ball.team), ".\n");
@@ -392,7 +392,7 @@ void GoalTouch(void)
 }
 
 //=======================//
-//       team ents       //
+//        team ents       //
 //=======================//
 void spawnfunc_nexball_team(void)
 {
@@ -465,7 +465,7 @@ void nb_delayedinit(void)
 
 
 //=======================//
-//      spawnfuncs       //
+//       spawnfuncs       //
 //=======================//
 
 void SpawnBall(void)
@@ -526,7 +526,7 @@ void SpawnBall(void)
 
 void spawnfunc_nexball_basketball(void)
 {
-    nexball_mode |= NBM_BASKETBALL;
+       nexball_mode |= NBM_BASKETBALL;
        self.classname = "nexball_basketball";
        if not(balls & BALL_BASKET)
        {
@@ -549,7 +549,7 @@ void spawnfunc_nexball_basketball(void)
 
 void spawnfunc_nexball_football(void)
 {
-    nexball_mode |= NBM_FOOTBALL;
+       nexball_mode |= NBM_FOOTBALL;
        self.classname = "nexball_football";
        self.solid = SOLID_TRIGGER;
        balls |= BALL_FOOT;
@@ -629,11 +629,11 @@ void spawnfunc_ball_basketball(void)
 // The "red goal" is defended by blue team. A ball in there counts as a point for red.
 void spawnfunc_ball_redgoal(void)
 {
-       spawnfunc_nexball_bluegoal();    // I blame Revenant
+       spawnfunc_nexball_bluegoal();   // I blame Revenant
 }
 void spawnfunc_ball_bluegoal(void)
 {
-       spawnfunc_nexball_redgoal();    // but he didn't mean to cause trouble :p
+       spawnfunc_nexball_redgoal();    // but he didn't mean to cause trouble :p
 }
 void spawnfunc_ball_fault(void)
 {
@@ -645,32 +645,32 @@ void spawnfunc_ball_bound(void)
 }
 
 //=======================//
-//      Weapon code      //
+//       Weapon code     //
 //=======================//
 
 
 void W_Nexball_Think()
 {
-    //dprint("W_Nexball_Think\n");
-    //vector new_dir = steerlib_arrive(self.enemy.origin, 2500);
-    vector new_dir = normalize(self.enemy.origin - self.origin);
-    vector old_dir = normalize(self.velocity);     
-    float _speed = vlen(self.velocity);    
-    vector new_vel = normalize(old_dir + (new_dir * autocvar_g_nexball_safepass_turnrate)) * _speed;
-    //vector new_vel = (new_dir * autocvar_g_nexball_safepass_turnrate
-    
-    self.velocity = new_vel;
-    
-    self.nextthink = time;
+       //dprint("W_Nexball_Think\n");
+       //vector new_dir = steerlib_arrive(self.enemy.origin, 2500);
+       vector new_dir = normalize(self.enemy.origin + '0 0 50' - self.origin);
+       vector old_dir = normalize(self.velocity);       
+       float _speed = vlen(self.velocity);     
+       vector new_vel = normalize(old_dir + (new_dir * autocvar_g_nexball_safepass_turnrate)) * _speed;
+       //vector new_vel = (new_dir * autocvar_g_nexball_safepass_turnrate
+       
+       self.velocity = new_vel;
+       
+       self.nextthink = time;
 }
 
 void W_Nexball_Touch(void)
 {
        entity ball, attacker;
        attacker = self.owner;
-    //self.think = SUB_Null;
-    //self.enemy = world;
-    
+       //self.think = SUB_Null;
+       //self.enemy = world;
+       
        PROJECTILE_TOUCH;
        if(attacker.team != other.team || autocvar_g_nexball_basketball_teamsteal)
                if((ball = other.ballcarried) && (attacker.classname == "player"))
@@ -725,7 +725,7 @@ void W_Nexball_Attack(float t)
                mul = mi + (ma - mi) * mul; // range from the minimal power to the maximal power
        }
        
-    DropBall(ball, w_shotorg, W_CalculateProjectileVelocity(self.velocity, w_shotdir * autocvar_g_balance_nexball_primary_speed * mul, FALSE));
+       DropBall(ball, w_shotorg, W_CalculateProjectileVelocity(self.velocity, w_shotdir * autocvar_g_balance_nexball_primary_speed * mul, FALSE));
        
 
        //TODO: use the speed_up cvar too ??
@@ -735,16 +735,16 @@ void W_Nexball_Attack2(void)
 {
        if(self.ballcarried.enemy)
        {
-           entity _ball = self.ballcarried;
-        W_SetupShot(self, FALSE, 4, "nexball/shoot1.wav", CH_WEAPON_A, 0);
-           DropBall(_ball, w_shotorg, trigger_push_calculatevelocity(_ball.origin, _ball.enemy, 32));
-        _ball.think = W_Nexball_Think;
-        _ball.nextthink = time;
-           return;
+               entity _ball = self.ballcarried;
+               W_SetupShot(self, FALSE, 4, "nexball/shoot1.wav", CH_WEAPON_A, 0);
+               DropBall(_ball, w_shotorg, trigger_push_calculatevelocity(_ball.origin, _ball.enemy, 32));
+               _ball.think = W_Nexball_Think;
+               _ball.nextthink = time;
+               return;
        }
-    
-    if(!autocvar_g_nexball_tackling)
-        return;
+       
+       if(!autocvar_g_nexball_tackling)
+               return;
        
        entity missile;
        if(!(balls & BALL_BASKET))
@@ -776,29 +776,29 @@ void W_Nexball_Attack2(void)
 var const float() nullfunc;
 float ball_customize()
 {
-    if(!self.owner)
-    {
-        self.effects &~= EF_FLAME;
-        self.scale = 1;
-        self.customizeentityforclient = nullfunc;
-        return TRUE;
-    }        
-    
-    if(other == self.owner)
-    {
-        self.scale = autocvar_g_nexball_viewmodel_scale;
-        if(self.enemy)
-            self.effects |= EF_FLAME;
-        else
-            self.effects &~= EF_FLAME;
-    }    
-    else
-    {
-        self.effects &~= EF_FLAME;
-        self.scale = 1;
-    }
-        
-    return TRUE;
+       if(!self.owner)
+       {
+               self.effects &~= EF_FLAME;
+               self.scale = 1;
+               self.customizeentityforclient = nullfunc;
+               return TRUE;
+       }               
+       
+       if(other == self.owner)
+       {
+               self.scale = autocvar_g_nexball_viewmodel_scale;
+               if(self.enemy)
+                       self.effects |= EF_FLAME;
+               else
+                       self.effects &~= EF_FLAME;
+       }       
+       else
+       {
+               self.effects &~= EF_FLAME;
+               self.scale = 1;
+       }
+               
+       return TRUE;
 }
 
 float w_nexball_weapon(float req)
@@ -879,75 +879,75 @@ MUTATOR_HOOKFUNCTION(nexball_BuildMutatorsPrettyString)
 
 MUTATOR_HOOKFUNCTION(nexball_PlayerPreThink)
 {
-    makevectors(self.v_angle);
-    if(nexball_mode & NBM_BASKETBALL)
-    {        
-        if(self.ballcarried)
-        {
-            // 'view ball'
-            self.ballcarried.velocity = self.velocity;            
-            self.ballcarried.customizeentityforclient = ball_customize;
-            
-            setorigin(self.ballcarried, self.origin + self.view_ofs + 
-                      v_forward * autocvar_g_nexball_viewmodel_offset_x + 
-                      v_right * autocvar_g_nexball_viewmodel_offset_y + 
-                      v_up * autocvar_g_nexball_viewmodel_offset_z);    
-                      
-            // 'safe passing'
-            if(autocvar_g_nexball_safepass_maxdist)
-            {
-                if(self.ballcarried.wait < time && self.ballcarried.enemy)
-                {
-                    //centerprint(self, sprintf("Lost lock on %s", self.ballcarried.enemy.netname));
-                    self.ballcarried.enemy = world;
-                }
-                    
-                
-                //tracebox(self.origin + self.view_ofs, '-2 -2 -2', '2 2 2', self.origin + self.view_ofs + v_forward * autocvar_g_nexball_safepass_maxdist);
-                crosshair_trace(self);
-                if( trace_ent && 
-                    trace_ent.flags & FL_CLIENT &&
-                    trace_ent.deadflag == DEAD_NO &&
-                    trace_ent.team == self.team &&
-                    vlen(trace_ent.origin - self.origin) <= autocvar_g_nexball_safepass_maxdist )
-                {
-                    
-                    //if(self.ballcarried.enemy != trace_ent)
-                    //    centerprint(self, sprintf("Locked to %s", trace_ent.netname));
-                    self.ballcarried.enemy = trace_ent;
-                    self.ballcarried.wait = time + autocvar_g_nexball_safepass_holdtime;
-                    
-                    
-                }
-            }
-        }
-        else
-        {            
-            if(!WEPSET_EMPTY_E(self.weaponentity))
-            {
-                WEPSET_COPY_EE(self, self.weaponentity);
-                weapon_action(WEP_PORTO, WR_RESETPLAYER);
-                self.switchweapon = self.weaponentity.switchweapon;
-                W_SwitchWeapon(self.switchweapon);
-                
+       makevectors(self.v_angle);
+       if(nexball_mode & NBM_BASKETBALL)
+       {               
+               if(self.ballcarried)
+               {
+                       // 'view ball'
+                       self.ballcarried.velocity = self.velocity;                      
+                       self.ballcarried.customizeentityforclient = ball_customize;
+                       
+                       setorigin(self.ballcarried, self.origin + self.view_ofs + 
+                                         v_forward * autocvar_g_nexball_viewmodel_offset_x + 
+                                         v_right * autocvar_g_nexball_viewmodel_offset_y + 
+                                         v_up * autocvar_g_nexball_viewmodel_offset_z);        
+                                         
+                       // 'safe passing'
+                       if(autocvar_g_nexball_safepass_maxdist)
+                       {
+                               if(self.ballcarried.wait < time && self.ballcarried.enemy)
+                               {
+                                       //centerprint(self, sprintf("Lost lock on %s", self.ballcarried.enemy.netname));
+                                       self.ballcarried.enemy = world;
+                               }
+                                       
+                               
+                               //tracebox(self.origin + self.view_ofs, '-2 -2 -2', '2 2 2', self.origin + self.view_ofs + v_forward * autocvar_g_nexball_safepass_maxdist);
+                               crosshair_trace(self);
+                               if( trace_ent && 
+                                       trace_ent.flags & FL_CLIENT &&
+                                       trace_ent.deadflag == DEAD_NO &&
+                                       trace_ent.team == self.team &&
+                                       vlen(trace_ent.origin - self.origin) <= autocvar_g_nexball_safepass_maxdist )
+                               {
+                                       
+                                       //if(self.ballcarried.enemy != trace_ent)
+                                       //      centerprint(self, sprintf("Locked to %s", trace_ent.netname));
+                                       self.ballcarried.enemy = trace_ent;
+                                       self.ballcarried.wait = time + autocvar_g_nexball_safepass_holdtime;
+                                       
+                                       
+                               }
+                       }
+               }
+               else
+               {                       
+                       if(!WEPSET_EMPTY_E(self.weaponentity))
+                       {
+                               WEPSET_COPY_EE(self, self.weaponentity);
+                               weapon_action(WEP_PORTO, WR_RESETPLAYER);
+                               self.switchweapon = self.weaponentity.switchweapon;
+                               W_SwitchWeapon(self.switchweapon);
+                               
                WEPSET_CLEAR_E(self.weaponentity);
-            }
-        }
-        
-    }
-    return FALSE;
+                       }
+               }
+               
+       }
+       return FALSE;
 }
 
 MUTATOR_HOOKFUNCTION(nexball_PlayerSpawn)
-{    
-    WEPSET_CLEAR_E(self.weaponentity);
-    
-    if(nexball_mode & NBM_BASKETBALL)
-        WEPSET_OR_EW(self, WEP_PORTO);
-    else
-        WEPSET_CLEAR_E(self);
+{      
+       WEPSET_CLEAR_E(self.weaponentity);
+       
+       if(nexball_mode & NBM_BASKETBALL)
+               WEPSET_OR_EW(self, WEP_PORTO);
+       else
+               WEPSET_CLEAR_E(self);
 
-    return FALSE;
+       return FALSE;
 }
 
 MUTATOR_DEFINITION(gamemode_nexball)
@@ -971,9 +971,9 @@ MUTATOR_DEFINITION(gamemode_nexball)
                // General settings
                /*
                CVTOV(g_nexball_football_boost_forward);   //100
-               CVTOV(g_nexball_football_boost_up);        //200
-               CVTOV(g_nexball_delay_idle);               //10
-               CVTOV(g_nexball_football_physics);         //0
+               CVTOV(g_nexball_football_boost_up);             //200
+               CVTOV(g_nexball_delay_idle);                       //10
+               CVTOV(g_nexball_football_physics);               //0
                */
                radar_showennemies = autocvar_g_nexball_radar_showallplayers;
 
index 545ec96d75295764b09b55c6368cbf95f633a71c..4b0b7eaa15c67a3dd686ce2e00474803730684a8 100644 (file)
@@ -27,4 +27,7 @@ float balls;
 float ball_scale;
 float nb_teams;
 
-.float teamtime;
\ No newline at end of file
+.entity nb_dropper;
+.float nb_droptime;
+
+.float teamtime;
diff --git a/qcsrc/server/mutators/gamemode_onslaught.qc b/qcsrc/server/mutators/gamemode_onslaught.qc
new file mode 100644 (file)
index 0000000..060447c
--- /dev/null
@@ -0,0 +1,1703 @@
+float autocvar_g_onslaught_spawn_at_controlpoints;
+float autocvar_g_onslaught_spawn_at_generator;
+float autocvar_g_onslaught_controlpoints_proxycap;
+float autocvar_g_onslaught_controlpoints_proxycap_distance = 512;
+float autocvar_g_onslaught_controlpoints_proxycap_dps = 100;
+
+void onslaught_generator_updatesprite(entity e);
+void onslaught_controlpoint_updatesprite(entity e);
+void onslaught_link_checkupdate();
+
+.entity sprite;
+.string target2;
+.float iscaptured;
+.float islinked;
+.float isgenneighbor_red;
+.float isgenneighbor_blue;
+.float iscpneighbor_red;
+.float iscpneighbor_blue;
+.float isshielded;
+.float lasthealth;
+.float lastteam;
+.float lastshielded;
+.float lastcaptured;
+
+.string model1, model2, model3;
+
+entity ons_red_generator;
+entity ons_blue_generator;
+
+void ons_gib_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce)
+{
+       self.velocity = self.velocity + vforce;
+}
+
+.float giblifetime;
+void ons_throwgib_think()
+{
+       float d;
+
+       self.nextthink = time + 0.05;
+
+       d = self.giblifetime - time;
+
+       if(d<0)
+       {
+               self.think = SUB_Remove;
+               return;
+       }
+       if(d<1)
+               self.alpha = d;
+
+       if(d>2)
+       if(random()<0.6)
+               pointparticles(particleeffectnum("onslaught_generator_gib_flame"), self.origin, '0 0 0', 1);
+}
+
+void ons_throwgib(vector v_from, vector v_to, string smodel, float f_lifetime, float b_burn)
+{
+       entity gib;
+
+       gib = spawn();
+
+       setmodel(gib, smodel);
+       setorigin(gib, v_from);
+       gib.solid = SOLID_BBOX;
+       gib.movetype = MOVETYPE_BOUNCE;
+       gib.takedamage = DAMAGE_YES;
+       gib.event_damage = ons_gib_damage;
+       gib.health = -1;
+       gib.effects = EF_LOWPRECISION;
+       gib.flags = FL_NOTARGET;
+       gib.velocity = v_to;
+       gib.giblifetime = time + f_lifetime;
+
+       if (b_burn)
+       {
+               gib.think = ons_throwgib_think;
+               gib.nextthink = time + 0.05;
+       }
+       else
+               SUB_SetFade(gib, gib.giblifetime, 2);
+}
+
+void onslaught_updatelinks()
+{
+       entity l, links;
+       float stop, t1, t2, t3, t4;
+       // first check if the game has ended
+       dprint("--- updatelinks ---\n");
+       links = findchain(classname, "onslaught_link");
+       // mark generators as being shielded and networked
+       l = findchain(classname, "onslaught_generator");
+       while (l)
+       {
+               if (l.iscaptured)
+                       dprint(etos(l), " (generator) belongs to team ", ftos(l.team), "\n");
+               else
+                       dprint(etos(l), " (generator) is destroyed\n");
+               l.islinked = l.iscaptured;
+               l.isshielded = l.iscaptured;
+               l = l.chain;
+       }
+       // mark points as shielded and not networked
+       l = findchain(classname, "onslaught_controlpoint");
+       while (l)
+       {
+               l.islinked = FALSE;
+               l.isshielded = TRUE;
+               l.isgenneighbor_red = FALSE;
+               l.isgenneighbor_blue = FALSE;
+               l.iscpneighbor_red = FALSE;
+               l.iscpneighbor_blue = FALSE;
+               dprint(etos(l), " (point) belongs to team ", ftos(l.team), "\n");
+               l = l.chain;
+       }
+       // flow power outward from the generators through the network
+       l = links;
+       while (l)
+       {
+               dprint(etos(l), " (link) connects ", etos(l.goalentity), " with ", etos(l.enemy), "\n");
+               l = l.chain;
+       }
+       stop = FALSE;
+       while (!stop)
+       {
+               stop = TRUE;
+               l = links;
+               while (l)
+               {
+                       // if both points are captured by the same team, and only one of
+                       // them is powered, mark the other one as powered as well
+                       if (l.enemy.iscaptured && l.goalentity.iscaptured)
+                               if (l.enemy.islinked != l.goalentity.islinked)
+                                       if (l.enemy.team == l.goalentity.team)
+                                       {
+                                               if (!l.goalentity.islinked)
+                                               {
+                                                       stop = FALSE;
+                                                       l.goalentity.islinked = TRUE;
+                                                       dprint(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)\n");
+                                               }
+                                               else if (!l.enemy.islinked)
+                                               {
+                                                       stop = FALSE;
+                                                       l.enemy.islinked = TRUE;
+                                                       dprint(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)\n");
+                                               }
+                                       }
+                       l = l.chain;
+               }
+       }
+       // now that we know which points are powered we can mark their neighbors
+       // as unshielded if team differs
+       l = links;
+       while (l)
+       {
+               if (l.goalentity.islinked)
+               {
+                       if (l.goalentity.team != l.enemy.team)
+                       {
+                               dprint(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)\n");
+                               l.enemy.isshielded = FALSE;
+                       }
+                       if(l.goalentity.classname == "onslaught_generator")
+                       {
+                               if(l.goalentity.team == COLOR_TEAM1)
+                                       l.enemy.isgenneighbor_red = TRUE;
+                               else if(l.goalentity.team == COLOR_TEAM2)
+                                       l.enemy.isgenneighbor_blue = TRUE;
+                       }
+                       else
+                       {
+                               if(l.goalentity.team == COLOR_TEAM1)
+                                       l.enemy.iscpneighbor_red = TRUE;
+                               else if(l.goalentity.team == COLOR_TEAM2)
+                                       l.enemy.iscpneighbor_blue = TRUE;
+                       }
+               }
+               if (l.enemy.islinked)
+               {
+                       if (l.goalentity.team != l.enemy.team)
+                       {
+                               dprint(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)\n");
+                               l.goalentity.isshielded = FALSE;
+                       }
+                       if(l.enemy.classname == "onslaught_generator")
+                       {
+                               if(l.enemy.team == COLOR_TEAM1)
+                                       l.goalentity.isgenneighbor_red = TRUE;
+                               else if(l.enemy.team == COLOR_TEAM2)
+                                       l.goalentity.isgenneighbor_blue = TRUE;
+                       }
+                       else
+                       {
+                               if(l.enemy.team == COLOR_TEAM1)
+                                       l.goalentity.iscpneighbor_red = TRUE;
+                               else if(l.enemy.team == COLOR_TEAM2)
+                                       l.goalentity.iscpneighbor_blue = TRUE;
+                       }
+               }
+               l = l.chain;
+       }
+       // now update the takedamage and alpha variables on generator shields
+       l = findchain(classname, "onslaught_generator");
+       while (l)
+       {
+               if (l.isshielded)
+               {
+                       dprint(etos(l), " (generator) is shielded\n");
+                       l.enemy.alpha = 1;
+                       l.takedamage = DAMAGE_NO;
+                       l.bot_attack = FALSE;
+               }
+               else
+               {
+                       dprint(etos(l), " (generator) is not shielded\n");
+                       l.enemy.alpha = -1;
+                       l.takedamage = DAMAGE_AIM;
+                       l.bot_attack = TRUE;
+               }
+               l = l.chain;
+       }
+       // now update the takedamage and alpha variables on control point icons
+       l = findchain(classname, "onslaught_controlpoint");
+       while (l)
+       {
+               if (l.isshielded)
+               {
+                       dprint(etos(l), " (point) is shielded\n");
+                       l.enemy.alpha = 1;
+                       if (l.goalentity)
+                       {
+                               l.goalentity.takedamage = DAMAGE_NO;
+                               l.goalentity.bot_attack = FALSE;
+                       }
+               }
+               else
+               {
+                       dprint(etos(l), " (point) is not shielded\n");
+                       l.enemy.alpha = -1;
+                       if (l.goalentity)
+                       {
+                               l.goalentity.takedamage = DAMAGE_AIM;
+                               l.goalentity.bot_attack = TRUE;
+                       }
+               }
+               onslaught_controlpoint_updatesprite(l);
+               l = l.chain;
+       }
+       // count generators owned by each team
+       t1 = t2 = t3 = t4 = 0;
+       l = findchain(classname, "onslaught_generator");
+       while (l)
+       {
+               if (l.iscaptured)
+               {
+                       if (l.team == COLOR_TEAM1) t1 = 1;
+                       if (l.team == COLOR_TEAM2) t2 = 1;
+                       if (l.team == COLOR_TEAM3) t3 = 1;
+                       if (l.team == COLOR_TEAM4) t4 = 1;
+               }
+               onslaught_generator_updatesprite(l);
+               l = l.chain;
+       }
+       // see if multiple teams remain (if not, it's game over)
+       if (t1 + t2 + t3 + t4 < 2)
+               dprint("--- game over ---\n");
+       else
+               dprint("--- done updating links ---\n");
+}
+
+float onslaught_controlpoint_can_be_linked(entity cp, float t)
+{
+       if(t == COLOR_TEAM1)
+       {
+               if(cp.isgenneighbor_red)
+                       return 2;
+               if(cp.iscpneighbor_red)
+                       return 1;
+       }
+       else if(t == COLOR_TEAM2)
+       {
+               if(cp.isgenneighbor_blue)
+                       return 2;
+               if(cp.iscpneighbor_blue)
+                       return 1;
+       }
+       return 0;
+       /*
+          entity e;
+       // check to see if this player has a legitimate claim to capture this
+       // control point - more specifically that there is a captured path of
+       // points leading back to the team generator
+       e = findchain(classname, "onslaught_link");
+       while (e)
+       {
+       if (e.goalentity == cp)
+       {
+       dprint(etos(e), " (link) connects to ", etos(e.enemy), " (point)");
+       if (e.enemy.islinked)
+       {
+       dprint(" which is linked");
+       if (e.enemy.team == t)
+       {
+       dprint(" and has the correct team!\n");
+       return 1;
+       }
+       else
+       dprint(" but has the wrong team\n");
+       }
+       else
+       dprint("\n");
+       }
+       else if (e.enemy == cp)
+       {
+       dprint(etos(e), " (link) connects to ", etos(e.goalentity), " (point)");
+       if (e.goalentity.islinked)
+       {
+       dprint(" which is linked");
+       if (e.goalentity.team == t)
+       {
+       dprint(" and has a team!\n");
+       return 1;
+       }
+       else
+       dprint(" but has the wrong team\n");
+       }
+       else
+       dprint("\n");
+       }
+       e = e.chain;
+       }
+       return 0;
+        */
+}
+
+float onslaught_controlpoint_attackable(entity cp, float t)
+       // -2: SAME TEAM, attackable by enemy!
+       // -1: SAME TEAM!
+       // 0: off limits
+       // 1: attack it
+       // 2: touch it
+       // 3: attack it (HIGH PRIO)
+       // 4: touch it (HIGH PRIO)
+{
+       float a;
+
+       if(cp.isshielded)
+       {
+               return 0;
+       }
+       else if(cp.goalentity)
+       {
+               // if there's already an icon built, nothing happens
+               if(cp.team == t)
+               {
+                       a = onslaught_controlpoint_can_be_linked(cp, COLOR_TEAM1 + COLOR_TEAM2 - t);
+                       if(a) // attackable by enemy?
+                               return -2; // EMERGENCY!
+                       return -1;
+               }
+               // we know it can be linked, so no need to check
+               // but...
+               a = onslaught_controlpoint_can_be_linked(cp, t);
+               if(a == 2) // near our generator?
+                       return 3; // EMERGENCY!
+               return 1;
+       }
+       else
+       {
+               // free point
+               if(onslaught_controlpoint_can_be_linked(cp, t))
+               {
+                       a = onslaught_controlpoint_can_be_linked(cp, COLOR_TEAM1 + COLOR_TEAM2 - t);
+                       if(a == 2)
+                               return 4; // GET THIS ONE NOW!
+                       else
+                               return 2; // TOUCH ME
+               }
+       }
+       return 0;
+}
+
+float overtime_msg_time;
+void onslaught_generator_think()
+{
+       float d;
+       entity e;
+       self.nextthink = ceil(time + 1);
+       if (!gameover)
+       {
+               if (autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60)
+               {
+                       if (!overtime_msg_time)
+                       {
+                               FOR_EACH_PLAYER(e)
+                                       centerprint(e, "^3Now playing ^1OVERTIME^3!\n^3Generators start now to decay.\n^3The more control points your team holds,\n^3the faster the enemy generator decays.");
+                               overtime_msg_time = time;
+                       }
+                       // self.max_health / 300 gives 5 minutes of overtime.
+                       // control points reduce the overtime duration.
+                       sound(self, CH_TRIGGER, "onslaught/generator_decay.wav", VOL_BASE, ATTN_NORM);
+                       d = 1;
+                       e = findchain(classname, "onslaught_controlpoint");
+                       while (e)
+                       {
+                               if (e.team != self.team)
+                                       if (e.islinked)
+                                               d = d + 1;
+                               e = e.chain;
+                       }
+                       
+                       if(autocvar_g_campaign && autocvar__campaign_testrun)
+                               d = d * self.max_health;
+                       else
+                               d = d * self.max_health / max(30, 60 * autocvar_timelimit_suddendeath);
+                       
+                       Damage(self, self, self, d, DEATH_HURTTRIGGER, self.origin, '0 0 0');
+               }
+               else if (overtime_msg_time)
+                       overtime_msg_time = 0;
+        
+        if(!self.isshielded && self.wait < time)
+        {
+            self.wait = time + 5;
+            FOR_EACH_PLAYER(e)
+            {
+                if(e.team == self.team)
+                {
+                    centerprint(e, "^1Your generator is NOT shielded!\n^7Re-capture controlpoints to shield it!");
+                    soundto(MSG_ONE, e, CHAN_AUTO, "kh/alarm.wav", VOL_BASE, ATTN_NONE);    // FIXME: Uniqe sound?
+                }                                  
+            }                                              
+        }    
+       }
+}
+
+void onslaught_generator_ring_spawn(vector org)
+{
+       modeleffect_spawn("models/onslaught/shockwavetransring.md3", 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, -16, 0.1, 1.25, 0.25);
+}
+
+void onslaught_generator_ray_think()
+{
+       self.nextthink = time + 0.05;
+       if(self.count > 10)
+       {
+               self.think = SUB_Remove;
+               return;
+       }
+
+       if(self.count > 5)
+               self.alpha -= 0.1;
+       else
+               self.alpha += 0.1;
+
+       self.scale += 0.2;
+       self.count +=1;
+}
+
+void onslaught_generator_ray_spawn(vector org)
+{
+       entity e;
+       e = spawn();
+       setmodel(e, "models/onslaught/ons_ray.md3");
+       setorigin(e, org);
+       e.angles = randomvec() * 360;
+       e.alpha = 0;
+       e.scale = random() * 5 + 8;
+       e.think = onslaught_generator_ray_think;
+       e.nextthink = time + 0.05;
+}
+
+void onslaught_generator_shockwave_spawn(vector org)
+{
+       shockwave_spawn("models/onslaught/shockwave.md3", org, -64, 0.75, 0.5);
+}
+
+void onslaught_generator_damage_think()
+{
+       if(self.owner.health < 0)
+       {
+               self.think = SUB_Remove;
+               return;
+       }
+       self.nextthink = time+0.1;
+
+       // damaged fx (less probable the more damaged is the generator)
+       if(random() < 0.9 - self.owner.health / self.owner.max_health)
+               if(random() < 0.01)
+               {
+                       pointparticles(particleeffectnum("electro_ballexplode"), self.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1);
+                       sound(self, CH_TRIGGER, "onslaught/electricity_explode.wav", VOL_BASE, ATTN_NORM);
+               }
+               else
+                       pointparticles(particleeffectnum("torch_small"), self.origin + randompos('-60 -60 -20', '60 60 60'), '0 0 0', 1);
+}
+
+void onslaught_generator_damage_spawn(entity gd_owner)
+{
+       entity e;
+       e = spawn();
+       e.owner = gd_owner;
+       e.health = self.owner.health;
+       setorigin(e, gd_owner.origin);
+       e.think = onslaught_generator_damage_think;
+       e.nextthink = time+1;
+}
+
+void onslaught_generator_deaththink()
+{
+       vector org;
+       float i;
+
+       if not (self.count)
+               self.count = 40;
+
+       // White shockwave
+       if(self.count==40||self.count==20)
+       {
+               onslaught_generator_ring_spawn(self.origin);
+               sound(self, CH_TRIGGER, "onslaught/shockwave.wav", VOL_BASE, ATTN_NORM);
+       }
+
+       // Throw some gibs
+       if(random() < 0.3)
+       {
+               i = random();
+               if(i < 0.3)
+                       ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 11 + '0 0 20', "models/onslaught/gen_gib1.md3", 6, TRUE);
+               else if(i > 0.7)
+                       ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 12 + '0 0 20', "models/onslaught/gen_gib2.md3", 6, TRUE);
+               else
+                       ons_throwgib(self.origin + '0 0 40', (100 * randomvec() - '1 1 1') * 13 + '0 0 20', "models/onslaught/gen_gib3.md3", 6, TRUE);
+       }
+
+       // Spawn fire balls
+       for(i=0;i < 10;++i)
+       {
+               org = self.origin + randompos('-30 -30 -30' * i + '0 0 -20', '30 30 30' * i + '0 0 20');
+               pointparticles(particleeffectnum("onslaught_generator_gib_explode"), org, '0 0 0', 1);
+       }
+
+       // Short explosion sound + small explosion
+       if(random() < 0.25)
+       {
+               te_explosion(self.origin);
+               sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
+       }
+
+       // Particles
+       org = self.origin + randompos(self.mins + '8 8 8', self.maxs + '-8 -8 -8');
+       pointparticles(particleeffectnum("onslaught_generator_smallexplosion"), org, '0 0 0', 1);
+
+       // rays
+       if(random() > 0.25 )
+       {
+               onslaught_generator_ray_spawn(self.origin);
+       }
+
+       // Final explosion
+       if(self.count==1)
+       {
+               org = self.origin;
+               te_explosion(org);
+               onslaught_generator_shockwave_spawn(org);
+               pointparticles(particleeffectnum("onslaught_generator_finalexplosion"), org, '0 0 0', 1);
+               sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
+       }
+       else
+               self.nextthink = time + 0.05;
+
+       self.count = self.count - 1;
+}
+
+void onslaught_generator_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+       float i;
+       if (damage <= 0)
+               return;
+       if(inWarmupStage)
+               return;
+       if (attacker != self)
+       {
+               if (self.isshielded)
+               {
+                       // this is protected by a shield, so ignore the damage
+                       if (time > self.pain_finished)
+                               if (attacker.classname == "player")
+                               {
+                                       play2(attacker, "onslaught/damageblockedbyshield.wav");
+                                       self.pain_finished = time + 1;
+                               }
+                       return;
+               }
+               if (time > self.pain_finished)
+               {
+                       self.pain_finished = time + 10;
+                       bprint(ColoredTeamName(self.team), " generator under attack!\n");
+                       play2team(self.team, "onslaught/generator_underattack.wav");
+               }
+       }
+       self.health = self.health - damage;
+       WaypointSprite_UpdateHealth(self.sprite, self.health);
+       // choose an animation frame based on health
+       self.frame = 10 * bound(0, (1 - self.health / self.max_health), 1);
+       // see if the generator is still functional, or dying
+       if (self.health > 0)
+       {
+#ifdef ONSLAUGHT_SPAM
+               float h, lh;
+               lh = ceil(self.lasthealth / 100) * 100;
+               h = ceil(self.health / 100) * 100;
+               if(lh != h)
+                       bprint(ColoredTeamName(self.team), " generator has less than ", ftos(h), " health remaining\n");
+#endif
+               self.lasthealth = self.health;
+       }
+       else if not(inWarmupStage)
+       {
+               if (attacker == self)
+                       bprint(ColoredTeamName(self.team), " generator spontaneously exploded due to overtime!\n");
+               else
+               {
+                       string t;
+                       t = ColoredTeamName(attacker.team);
+                       bprint(ColoredTeamName(self.team), " generator destroyed by ", t, "!\n");
+               }
+               self.iscaptured = FALSE;
+               self.islinked = FALSE;
+               self.isshielded = FALSE;
+               self.takedamage = DAMAGE_NO; // can't be hurt anymore
+               self.event_damage = SUB_Null; // won't do anything if hurt
+               self.count = 0; // reset counter
+               self.think = onslaught_generator_deaththink; // explosion sequence
+               self.nextthink = time; // start exploding immediately
+               self.think(); // do the first explosion now
+
+               WaypointSprite_UpdateMaxHealth(self.sprite, 0);
+
+               onslaught_updatelinks();
+       }
+
+       if(self.health <= 0)
+               setmodel(self, "models/onslaught/generator_dead.md3");
+       else if(self.health < self.max_health * 0.10)
+               setmodel(self, "models/onslaught/generator_dmg9.md3");
+       else if(self.health < self.max_health * 0.20)
+               setmodel(self, "models/onslaught/generator_dmg8.md3");
+       else if(self.health < self.max_health * 0.30)
+               setmodel(self, "models/onslaught/generator_dmg7.md3");
+       else if(self.health < self.max_health * 0.40)
+               setmodel(self, "models/onslaught/generator_dmg6.md3");
+       else if(self.health < self.max_health * 0.50)
+               setmodel(self, "models/onslaught/generator_dmg5.md3");
+       else if(self.health < self.max_health * 0.60)
+               setmodel(self, "models/onslaught/generator_dmg4.md3");
+       else if(self.health < self.max_health * 0.70)
+               setmodel(self, "models/onslaught/generator_dmg3.md3");
+       else if(self.health < self.max_health * 0.80)
+               setmodel(self, "models/onslaught/generator_dmg2.md3");
+       else if(self.health < self.max_health * 0.90)
+               setmodel(self, "models/onslaught/generator_dmg1.md3");
+       setsize(self, '-52 -52 -14', '52 52 75');
+
+       // Throw some flaming gibs on damage, more damage = more chance for gib
+       if(random() < damage/220)
+       {
+               sound(self, CH_TRIGGER, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
+               i = random();
+               if(i < 0.3)
+                       ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib1.md3", 5, TRUE);
+               else if(i > 0.7)
+                       ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib2.md3", 5, TRUE);
+               else
+                       ons_throwgib(hitloc + '0 0 20', force * -1, "models/onslaught/gen_gib3.md3", 5, TRUE);
+       }
+       else
+       {
+               // particles on every hit
+               pointparticles(particleeffectnum("sparks"), hitloc, force * -1, 1);
+
+               //sound on every hit
+               if (random() < 0.5)
+                       sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE, ATTN_NORM);
+               else
+                       sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE, ATTN_NORM);
+       }
+
+       //throw some gibs on damage
+       if(random() < damage/200+0.2)
+               if(random() < 0.5)
+                       ons_throwgib(hitloc + '0 0 20', randomvec()*360, "models/onslaught/gen_gib1.md3", 5, FALSE);
+}
+
+// update links after a delay
+void onslaught_generator_delayed()
+{
+       onslaught_updatelinks();
+       // now begin normal thinking
+       self.think = onslaught_generator_think;
+       self.nextthink = time;
+}
+
+string onslaught_generator_waypointsprite_for_team(entity e, float t)
+{
+       if(t == e.team)
+       {
+               if(e.team == COLOR_TEAM1)
+                       return "ons-gen-red";
+               else if(e.team == COLOR_TEAM2)
+                       return "ons-gen-blue";
+       }
+       if(e.isshielded)
+               return "ons-gen-shielded";
+       if(e.team == COLOR_TEAM1)
+               return "ons-gen-red";
+       else if(e.team == COLOR_TEAM2)
+               return "ons-gen-blue";
+       return "";
+}
+
+void onslaught_generator_updatesprite(entity e)
+{
+       string s1, s2, s3;
+       s1 = onslaught_generator_waypointsprite_for_team(e, COLOR_TEAM1);
+       s2 = onslaught_generator_waypointsprite_for_team(e, COLOR_TEAM2);
+       s3 = onslaught_generator_waypointsprite_for_team(e, -1);
+       WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);
+
+       if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded)
+       {
+               e.lastteam = e.team + 2;
+               e.lastshielded = e.isshielded;
+               if(e.lastshielded)
+               {
+                       if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)
+                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, FALSE));
+                       else
+                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5');
+               }
+               else
+               {
+                       if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)
+                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, FALSE));
+                       else
+                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75');
+               }
+               WaypointSprite_Ping(e.sprite);
+       }
+}
+
+string onslaught_controlpoint_waypointsprite_for_team(entity e, float t)
+{
+       float a;
+       if(t != -1)
+       {
+               a = onslaught_controlpoint_attackable(e, t);
+               if(a == 3 || a == 4) // ATTACK/TOUCH THIS ONE NOW
+               {
+                       if(e.team == COLOR_TEAM1)
+                               return "ons-cp-atck-red";
+                       else if(e.team == COLOR_TEAM2)
+                               return "ons-cp-atck-blue";
+                       else
+                               return "ons-cp-atck-neut";
+               }
+               else if(a == -2) // DEFEND THIS ONE NOW
+               {
+                       if(e.team == COLOR_TEAM1)
+                               return "ons-cp-dfnd-red";
+                       else if(e.team == COLOR_TEAM2)
+                               return "ons-cp-dfnd-blue";
+               }
+               else if(e.team == t || a == -1 || a == 1) // own point, or fire at it
+               {
+                       if(e.team == COLOR_TEAM1)
+                               return "ons-cp-red";
+                       else if(e.team == COLOR_TEAM2)
+                               return "ons-cp-blue";
+               }
+               else if(a == 2) // touch it
+                       return "ons-cp-neut";
+       }
+       else
+       {
+               if(e.team == COLOR_TEAM1)
+                       return "ons-cp-red";
+               else if(e.team == COLOR_TEAM2)
+                       return "ons-cp-blue";
+               else
+                       return "ons-cp-neut";
+       }
+       return "";
+}
+
+void onslaught_controlpoint_updatesprite(entity e)
+{
+       string s1, s2, s3;
+       s1 = onslaught_controlpoint_waypointsprite_for_team(e, COLOR_TEAM1);
+       s2 = onslaught_controlpoint_waypointsprite_for_team(e, COLOR_TEAM2);
+       s3 = onslaught_controlpoint_waypointsprite_for_team(e, -1);
+       WaypointSprite_UpdateSprites(e.sprite, s1, s2, s3);
+
+       float sh;
+       sh = !(onslaught_controlpoint_can_be_linked(e, COLOR_TEAM1) || onslaught_controlpoint_can_be_linked(e, COLOR_TEAM2));
+
+       if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured)
+       {
+               if(e.iscaptured) // don't mess up build bars!
+               {
+                       if(sh)
+                       {
+                               WaypointSprite_UpdateMaxHealth(e.sprite, 0);
+                       }
+                       else
+                       {
+                               WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health);
+                               WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health);
+                       }
+               }
+               if(e.lastshielded)
+               {
+                       if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)
+                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, FALSE));
+                       else
+                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5');
+               }
+               else
+               {
+                       if(e.team == COLOR_TEAM1 || e.team == COLOR_TEAM2)
+                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, FALSE));
+                       else
+                               WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75');
+               }
+               WaypointSprite_Ping(e.sprite);
+
+               e.lastteam = e.team + 2;
+               e.lastshielded = sh;
+               e.lastcaptured = e.iscaptured;
+       }
+}
+
+void onslaught_generator_reset()
+{
+       self.team = self.team_saved;
+       self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health;
+       self.takedamage = DAMAGE_AIM;
+       self.bot_attack = TRUE;
+       self.iscaptured = TRUE;
+       self.islinked = TRUE;
+       self.isshielded = TRUE;
+       self.enemy.solid = SOLID_NOT;
+       self.think = onslaught_generator_delayed;
+       self.nextthink = time + 0.2;
+       setmodel(self, "models/onslaught/generator.md3");
+       setsize(self, '-52 -52 -14', '52 52 75');
+
+       if (!self.noalign)
+        droptofloor();
+
+       WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
+       WaypointSprite_UpdateHealth(self.sprite, self.health);
+}
+
+/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64)
+  Base generator.
+
+  spawnfunc_onslaught_link entities can target this.
+
+keys:
+"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET.
+"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
+ */
+void spawnfunc_onslaught_generator()
+{
+       if (!g_onslaught)
+       {
+               remove(self);
+               return;
+       }
+
+       //entity e;
+       precache_model("models/onslaught/generator.md3");
+       precache_model("models/onslaught/generator_shield.md3");
+       precache_model("models/onslaught/generator_dmg1.md3");
+       precache_model("models/onslaught/generator_dmg2.md3");
+       precache_model("models/onslaught/generator_dmg3.md3");
+       precache_model("models/onslaught/generator_dmg4.md3");
+       precache_model("models/onslaught/generator_dmg5.md3");
+       precache_model("models/onslaught/generator_dmg6.md3");
+       precache_model("models/onslaught/generator_dmg7.md3");
+       precache_model("models/onslaught/generator_dmg8.md3");
+       precache_model("models/onslaught/generator_dmg9.md3");
+       precache_model("models/onslaught/generator_dead.md3");
+       precache_model("models/onslaught/shockwave.md3");
+       precache_model("models/onslaught/shockwavetransring.md3");
+       precache_model("models/onslaught/gen_gib1.md3");
+       precache_model("models/onslaught/gen_gib2.md3");
+       precache_model("models/onslaught/gen_gib3.md3");
+       precache_model("models/onslaught/ons_ray.md3");
+       precache_sound("onslaught/generator_decay.wav");
+       precache_sound("weapons/grenade_impact.wav");
+       precache_sound("weapons/rocket_impact.wav");
+       precache_sound("onslaught/generator_underattack.wav");
+       precache_sound("onslaught/shockwave.wav");
+       precache_sound("onslaught/ons_hit1.wav");
+       precache_sound("onslaught/ons_hit2.wav");
+       precache_sound("onslaught/electricity_explode.wav");
+       if (!self.team)
+               objerror("team must be set");
+       
+       if(self.team == COLOR_TEAM1)
+        ons_red_generator = self;
+
+       if(self.team == COLOR_TEAM2)
+        ons_blue_generator = self;
+        
+       self.team_saved = self.team;
+       self.colormap = 1024 + (self.team - 1) * 17;
+       self.solid = SOLID_BBOX;
+       self.movetype = MOVETYPE_NONE;
+       self.lasthealth = self.max_health = self.health = autocvar_g_onslaught_gen_health;
+       setmodel(self, "models/onslaught/generator.md3");
+       setsize(self, '-52 -52 -14', '52 52 75');
+       setorigin(self, self.origin);
+       self.takedamage = DAMAGE_AIM;
+       self.bot_attack = TRUE;
+       self.event_damage = onslaught_generator_damage;
+       self.iscaptured = TRUE;
+       self.islinked = TRUE;
+       self.isshielded = TRUE;
+       // helper entity that create fx when generator is damaged
+       onslaught_generator_damage_spawn(self);
+       // spawn shield model which indicates whether this can be damaged
+       self.enemy = spawn();
+       setattachment(self.enemy , self, "");
+       self.enemy.classname = "onslaught_generator_shield";
+       self.enemy.solid = SOLID_NOT;
+       self.enemy.movetype = MOVETYPE_NONE;
+       self.enemy.effects = EF_ADDITIVE;
+       setmodel(self.enemy, "models/onslaught/generator_shield.md3");
+       //setorigin(e, self.origin);
+       self.enemy.colormap = self.colormap;
+       self.enemy.team = self.team;
+       //self.think = onslaught_generator_delayed;
+       //self.nextthink = time + 0.2;
+       InitializeEntity(self, onslaught_generator_delayed, INITPRIO_LAST);
+
+       WaypointSprite_SpawnFixed(string_null, self.origin + '0 0 128', self, sprite, RADARICON_NONE, '0 0 0');
+       WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY);
+       WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
+       WaypointSprite_UpdateHealth(self.sprite, self.health);
+
+       waypoint_spawnforitem(self);
+
+       onslaught_updatelinks();
+       
+       self.reset = onslaught_generator_reset;
+}
+
+.float waslinked;
+.float cp_bob_spd;
+.vector cp_origin, cp_bob_origin, cp_bob_dmg;
+
+float ons_notification_time_team1;
+float ons_notification_time_team2;
+
+void onslaught_controlpoint_icon_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+       entity oself;
+       float nag;
+
+       if (damage <= 0)
+               return;
+       if (self.owner.isshielded)
+       {
+               // this is protected by a shield, so ignore the damage
+               if (time > self.pain_finished)
+                       if (attacker.classname == "player")
+                       {
+                               play2(attacker, "onslaught/damageblockedbyshield.wav");
+                               self.pain_finished = time + 1;
+                       }
+               return;
+       }
+
+       if (attacker.classname == "player")
+       {
+               nag = FALSE;
+               if(self.team == COLOR_TEAM1)
+               {
+                       if(time - ons_notification_time_team1 > 10)
+                       {
+                               nag = TRUE;
+                               ons_notification_time_team1 = time;
+                       }
+               }
+               else if(self.team == COLOR_TEAM2)
+               {
+                       if(time - ons_notification_time_team2 > 10)
+                       {
+                               nag = TRUE;
+                               ons_notification_time_team2 = time;
+                       }
+               }
+               else
+                       nag = TRUE;
+
+               if(nag)
+                       play2team(self.team, "onslaught/controlpoint_underattack.wav");
+       }
+
+       self.health = self.health - damage;
+       if(self.owner.iscaptured)
+               WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
+       else
+               WaypointSprite_UpdateBuildFinished(self.owner.sprite, time + (self.max_health - self.health) / (self.count / sys_frametime));
+       self.pain_finished = time + 1;
+       self.punchangle = (2 * randomvec() - '1 1 1') * 45;
+       self.cp_bob_dmg_z = (2 * random() - 1) * 15;
+       // colormod flash when shot
+       self.colormod = '2 2 2';
+       // particles on every hit
+       pointparticles(particleeffectnum("sparks"), hitloc, force*-1, 1);
+       //sound on every hit
+       if (random() < 0.5)
+               sound(self, CH_TRIGGER, "onslaught/ons_hit1.wav", VOL_BASE+0.3, ATTN_NORM);
+       else
+               sound(self, CH_TRIGGER, "onslaught/ons_hit2.wav", VOL_BASE+0.3, ATTN_NORM);
+
+       if (self.health < 0)
+       {
+               sound(self, CH_TRIGGER, "weapons/grenade_impact.wav", VOL_BASE, ATTN_NORM);
+               pointparticles(particleeffectnum("rocket_explode"), self.origin, '0 0 0', 1);
+               {
+                       string t;
+                       t = ColoredTeamName(attacker.team);
+                       bprint(ColoredTeamName(self.team), " ", self.message, " control point destroyed by ", t, "\n");
+                       ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 25, "models/onslaught/controlpoint_icon_gib1.md3", 3, FALSE);
+                       ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE);
+                       ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 45, "models/onslaught/controlpoint_icon_gib2.md3", 3, FALSE);
+                       ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
+                       ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
+                       ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
+                       ons_throwgib(self.origin, (2 * randomvec() - '1 1 1') * 75, "models/onslaught/controlpoint_icon_gib4.md3", 3, FALSE);
+               }
+               self.owner.goalentity = world;
+               self.owner.islinked = FALSE;
+               self.owner.iscaptured = FALSE;
+               self.owner.team = 0;
+               self.owner.colormap = 1024;
+
+               WaypointSprite_UpdateMaxHealth(self.owner.sprite, 0);
+
+               onslaught_updatelinks();
+
+               // Use targets now (somebody make sure this is in the right place..)
+               oself = self;
+               self = self.owner;
+               activator = self;
+               SUB_UseTargets ();
+               self = oself;
+
+
+               self.owner.waslinked = self.owner.islinked;
+               if(self.owner.model != "models/onslaught/controlpoint_pad.md3")
+                       setmodel(self.owner, "models/onslaught/controlpoint_pad.md3");
+               //setsize(self, '-32 -32 0', '32 32 8');
+
+               remove(self);
+       }
+}
+
+void onslaught_controlpoint_icon_think()
+{
+       entity oself;
+       self.nextthink = time + sys_frametime;
+       
+       if(autocvar_g_onslaught_controlpoints_proxycap)
+       {        
+        float _enemy_count;
+        float _friendly_count;
+        float _dist;
+        entity _player;
+        
+        FOR_EACH_PLAYER(_player)
+        {
+            if(!_player.deadflag)
+            {
+                _dist = vlen(_player.origin - self.origin);
+                if(_dist < autocvar_g_onslaught_controlpoints_proxycap_distance)
+                {
+                    if(_player.team == self.team)
+                        ++_friendly_count;
+                    else
+                        ++_enemy_count;
+                }
+            }
+        }
+
+        _friendly_count = _friendly_count * (autocvar_g_onslaught_controlpoints_proxycap_dps * sys_frametime);
+        _enemy_count = _enemy_count * (autocvar_g_onslaught_controlpoints_proxycap_dps * sys_frametime);
+        
+        self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health);
+        if(self.health <= 0)
+        {
+            onslaught_controlpoint_icon_damage(self, self, 1, 0, self.origin, '0 0 0');
+            return;
+        }
+    }
+    
+       if (time > self.pain_finished + 5)
+       {
+               if(self.health < self.max_health)
+               {
+                       self.health = self.health + self.count;
+                       if (self.health >= self.max_health)
+                               self.health = self.max_health;
+                       WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
+               }
+       }
+       if (self.health < self.max_health * 0.25)
+               setmodel(self, "models/onslaught/controlpoint_icon_dmg3.md3");
+       else if (self.health < self.max_health * 0.50)
+               setmodel(self, "models/onslaught/controlpoint_icon_dmg2.md3");
+       else if (self.health < self.max_health * 0.75)
+               setmodel(self, "models/onslaught/controlpoint_icon_dmg1.md3");
+       else if (self.health < self.max_health * 0.90)
+               setmodel(self, "models/onslaught/controlpoint_icon.md3");
+       // colormod flash when shot
+       self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));
+
+       if(self.owner.islinked != self.owner.waslinked)
+       {
+               // unteam the spawnpoint if needed
+               float t;
+               t = self.owner.team;
+               if(!self.owner.islinked)
+                       self.owner.team = 0;
+
+               oself = self;
+               self = self.owner;
+               activator = self;
+               SUB_UseTargets ();
+               self = oself;
+
+               self.owner.team = t;
+
+               self.owner.waslinked = self.owner.islinked;
+       }
+
+       if (self.punchangle_x > 0)
+       {
+               self.punchangle_x = self.punchangle_x - 60 * sys_frametime;
+               if (self.punchangle_x < 0)
+                       self.punchangle_x = 0;
+       }
+       else if (self.punchangle_x < 0)
+       {
+               self.punchangle_x = self.punchangle_x + 60 * sys_frametime;
+               if (self.punchangle_x > 0)
+                       self.punchangle_x = 0;
+       }
+
+       if (self.punchangle_y > 0)
+       {
+               self.punchangle_y = self.punchangle_y - 60 * sys_frametime;
+               if (self.punchangle_y < 0)
+                       self.punchangle_y = 0;
+       }
+       else if (self.punchangle_y < 0)
+       {
+               self.punchangle_y = self.punchangle_y + 60 * sys_frametime;
+               if (self.punchangle_y > 0)
+                       self.punchangle_y = 0;
+       }
+
+       if (self.punchangle_z > 0)
+       {
+               self.punchangle_z = self.punchangle_z - 60 * sys_frametime;
+               if (self.punchangle_z < 0)
+                       self.punchangle_z = 0;
+       }
+       else if (self.punchangle_z < 0)
+       {
+               self.punchangle_z = self.punchangle_z + 60 * sys_frametime;
+               if (self.punchangle_z > 0)
+                       self.punchangle_z = 0;
+       }
+
+       self.angles_x = self.punchangle_x;
+       self.angles_y = self.punchangle_y + self.mangle_y;
+       self.angles_z = self.punchangle_z;
+       self.mangle_y = self.mangle_y + 45 * sys_frametime;
+
+       self.cp_bob_origin_z = 4 * PI * (1 - cos(self.cp_bob_spd));
+       self.cp_bob_spd = self.cp_bob_spd + 1.875 * sys_frametime;
+       if(self.cp_bob_dmg_z > 0)
+               self.cp_bob_dmg_z = self.cp_bob_dmg_z - 3 * sys_frametime;
+       else
+               self.cp_bob_dmg_z = 0;
+       setorigin(self,self.cp_origin + self.cp_bob_origin + self.cp_bob_dmg);
+
+       // damaged fx
+       if(random() < 0.6 - self.health / self.max_health)
+       {
+               pointparticles(particleeffectnum("electricity_sparks"), self.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1);
+
+               if(random() > 0.8)
+                       sound(self, CH_PAIN, "onslaught/ons_spark1.wav", VOL_BASE, ATTN_NORM);
+               else if (random() > 0.5)
+                       sound(self, CH_PAIN, "onslaught/ons_spark2.wav", VOL_BASE, ATTN_NORM);
+       }
+}
+
+void onslaught_controlpoint_icon_buildthink()
+{
+       entity oself;
+       float a;
+
+       self.nextthink = time + sys_frametime;
+
+       // only do this if there is power
+       a = onslaught_controlpoint_can_be_linked(self.owner, self.owner.team);
+       if(!a)
+               return;
+    
+       self.health = self.health + self.count;
+
+       if (self.health >= self.max_health)
+       {
+               self.health = self.max_health;
+               self.count = autocvar_g_onslaught_cp_regen * sys_frametime; // slow repair rate from now on
+               self.think = onslaught_controlpoint_icon_think;
+               sound(self, CH_TRIGGER, "onslaught/controlpoint_built.wav", VOL_BASE, ATTN_NORM);
+               bprint(ColoredTeamName(self.team), " captured ", self.owner.message, " control point\n");
+               self.owner.iscaptured = TRUE;
+
+               WaypointSprite_UpdateMaxHealth(self.owner.sprite, self.max_health);
+               WaypointSprite_UpdateHealth(self.owner.sprite, self.health);
+
+               onslaught_updatelinks();
+
+               // Use targets now (somebody make sure this is in the right place..)
+               oself = self;
+               self = self.owner;
+               activator = self;
+               SUB_UseTargets ();
+               self = oself;
+               self.cp_origin = self.origin;
+               self.cp_bob_origin = '0 0 0.1';
+               self.cp_bob_spd = 0;
+       }
+       self.alpha = self.health / self.max_health;
+       // colormod flash when shot
+       self.colormod = '1 1 1' * (2 - bound(0, (self.pain_finished - time) / 10, 1));
+       if(self.owner.model != "models/onslaught/controlpoint_pad2.md3")
+               setmodel(self.owner, "models/onslaught/controlpoint_pad2.md3");
+       //setsize(self, '-32 -32 0', '32 32 8');
+
+       if(random() < 0.9 - self.health / self.max_health)
+               pointparticles(particleeffectnum("rage"), self.origin + 10 * randomvec(), '0 0 -1', 1);
+}
+
+
+
+
+void onslaught_controlpoint_touch()
+{
+       entity e;
+       float a;
+       if (other.classname != "player")
+               return;
+       a = onslaught_controlpoint_attackable(self, other.team);
+       if(a != 2 && a != 4)
+               return;
+       // we've verified that this player has a legitimate claim to this point,
+       // so start building the captured point icon (which only captures this
+       // point if it successfully builds without being destroyed first)
+       self.goalentity = e = spawn();
+       e.classname = "onslaught_controlpoint_icon";
+       e.owner = self;
+       e.max_health = autocvar_g_onslaught_cp_health;
+       e.health = autocvar_g_onslaught_cp_buildhealth;
+       e.solid = SOLID_BBOX;
+       e.movetype = MOVETYPE_NONE;
+       setmodel(e, "models/onslaught/controlpoint_icon.md3");
+       setsize(e, '-32 -32 -32', '32 32 32');
+       setorigin(e, self.origin + '0 0 96');
+       e.takedamage = DAMAGE_AIM;
+       e.bot_attack = TRUE;
+       e.event_damage = onslaught_controlpoint_icon_damage;
+       e.team = other.team;
+       e.colormap = 1024 + (e.team - 1) * 17;
+       e.think = onslaught_controlpoint_icon_buildthink;
+       e.nextthink = time + sys_frametime;
+       e.count = (e.max_health - e.health) * sys_frametime / autocvar_g_onslaught_cp_buildtime; // how long it takes to build
+       sound(e, CH_TRIGGER, "onslaught/controlpoint_build.wav", VOL_BASE, ATTN_NORM);
+       self.team = e.team;
+       self.colormap = e.colormap;
+       WaypointSprite_UpdateBuildFinished(self.sprite, time + (e.max_health - e.health) / (e.count / sys_frametime));
+       onslaught_updatelinks();
+}
+
+void onslaught_controlpoint_reset()
+{
+       if(self.goalentity && self.goalentity != world)
+               remove(self.goalentity);
+       self.goalentity = world;
+       self.team = 0;
+       self.colormap = 1024;
+       self.iscaptured = FALSE;
+       self.islinked = FALSE;
+       self.isshielded = TRUE;
+       self.enemy.solid = SOLID_NOT;
+       self.enemy.colormap = self.colormap;
+       self.think = self.enemy.think = SUB_Null;
+       self.nextthink = 0; // don't like SUB_Null :P
+       setmodel(self, "models/onslaught/controlpoint_pad.md3");
+       //setsize(self, '-32 -32 0', '32 32 8');
+
+       WaypointSprite_UpdateMaxHealth(self.sprite, 0);
+
+       onslaught_updatelinks();
+
+       activator = self;
+       SUB_UseTargets(); // to reset the structures, playerspawns etc.
+}
+
+/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128)
+  Control point. Be sure to give this enough clearance so that the shootable part has room to exist
+
+  This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity.
+
+keys:
+"targetname" - name that spawnfunc_onslaught_link entities will use to target this.
+"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities.
+"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc)
+ */
+ /*
+void onslaught_controlpoint_think()
+{
+    self.nextthink = time;
+       //if(autocvar_g_onslaught_controlpoints_proxycap)
+                   
+    float _enemy_count;
+    float _friendly_count;
+    float _dist;
+    entity _player;
+    
+    FOR_EACH_PLAYER(_player)
+    {
+        if(!_player.deadflag)
+        {
+            _dist = vlen(_player.origin - self.origin);
+            if(_dist < autocvar_g_onslaught_controlpoints_proxycap_distance)
+            {
+                if(_player.team == self.team)
+                    ++_friendly_count;
+                else
+                    ++_enemy_count;
+            }
+        }
+    }
+
+    _friendly_count = _friendly_count * (autocvar_g_onslaught_controlpoints_proxycap_dps * sys_frametime);
+    _enemy_count = _enemy_count * (autocvar_g_onslaught_controlpoints_proxycap_dps * sys_frametime);
+    
+    self.health = bound(0, self.health + (_friendly_count - _enemy_count), self.max_health);
+    if(self.health <= 0)
+    {
+        onslaught_controlpoint_icon_damage(self, self, 1, 0, self.origin, '0 0 0');
+        return;
+    }
+    
+    if(self.health == max_health)
+       {
+           
+       }
+}
+*/
+
+void spawnfunc_onslaught_controlpoint()
+{
+       //entity e;
+       if (!g_onslaught)
+       {
+               remove(self);
+               return;
+       }
+       precache_model("models/onslaught/controlpoint_pad.md3");
+       precache_model("models/onslaught/controlpoint_pad2.md3");
+       precache_model("models/onslaught/controlpoint_shield.md3");
+       precache_model("models/onslaught/controlpoint_icon.md3");
+       precache_model("models/onslaught/controlpoint_icon_dmg1.md3");
+       precache_model("models/onslaught/controlpoint_icon_dmg2.md3");
+       precache_model("models/onslaught/controlpoint_icon_dmg3.md3");
+       precache_model("models/onslaught/controlpoint_icon_gib1.md3");
+       precache_model("models/onslaught/controlpoint_icon_gib2.md3");
+       precache_model("models/onslaught/controlpoint_icon_gib4.md3");
+       precache_sound("onslaught/controlpoint_build.wav");
+       precache_sound("onslaught/controlpoint_built.wav");
+       precache_sound("weapons/grenade_impact.wav");
+       precache_sound("onslaught/damageblockedbyshield.wav");
+       precache_sound("onslaught/controlpoint_underattack.wav");
+       precache_sound("onslaught/ons_spark1.wav");
+       precache_sound("onslaught/ons_spark2.wav");
+       self.solid = SOLID_BBOX;
+       self.movetype = MOVETYPE_NONE;
+       setmodel(self, "models/onslaught/controlpoint_pad.md3");
+       //setsize(self, '-32 -32 0', '32 32 8');
+       if (!self.noalign)
+        droptofloor();
+       
+       setorigin(self, self.origin);
+       self.touch = onslaught_controlpoint_touch;
+       self.team = 0;
+       self.colormap = 1024;
+       self.iscaptured = FALSE;
+       self.islinked = FALSE;
+       self.isshielded = TRUE;
+       // spawn shield model which indicates whether this can be damaged
+       self.enemy = spawn();
+       self.enemy.classname = "onslaught_controlpoint_shield";
+       self.enemy.solid = SOLID_NOT;
+       self.enemy.movetype = MOVETYPE_NONE;
+       self.enemy.effects = EF_ADDITIVE;
+       setmodel(self.enemy , "models/onslaught/controlpoint_shield.md3");
+       
+       setattachment(self.enemy , self, "");
+       //setsize(e, '-32 -32 0', '32 32 128');
+
+       //setorigin(e, self.origin);
+       self.enemy.colormap = self.colormap;
+
+       waypoint_spawnforitem(self);
+
+       WaypointSprite_SpawnFixed(string_null, self.origin + '0 0 128', self, sprite, RADARICON_NONE, '0 0 0');
+       WaypointSprite_UpdateRule(self.sprite, COLOR_TEAM2, SPRITERULE_TEAMPLAY);
+
+       onslaught_updatelinks();
+       
+       self.reset = onslaught_controlpoint_reset;
+}
+
+float onslaught_link_send(entity to, float sendflags)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_RADARLINK);
+       WriteByte(MSG_ENTITY, sendflags);
+       if(sendflags & 1)
+       {
+               WriteCoord(MSG_ENTITY, self.goalentity.origin_x);
+               WriteCoord(MSG_ENTITY, self.goalentity.origin_y);
+               WriteCoord(MSG_ENTITY, self.goalentity.origin_z);
+       }
+       if(sendflags & 2)
+       {
+               WriteCoord(MSG_ENTITY, self.enemy.origin_x);
+               WriteCoord(MSG_ENTITY, self.enemy.origin_y);
+               WriteCoord(MSG_ENTITY, self.enemy.origin_z);
+       }
+       if(sendflags & 4)
+       {
+               WriteByte(MSG_ENTITY, self.clientcolors); // which is goalentity's color + enemy's color * 16
+       }
+       return TRUE;
+}
+
+void onslaught_link_checkupdate()
+{
+       // TODO check if the two sides have moved (currently they won't move anyway)
+       float redpower, bluepower;
+
+       redpower = bluepower = 0;
+       if(self.goalentity.islinked)
+       {
+               if(self.goalentity.team == COLOR_TEAM1)
+                       redpower = 1;
+               else if(self.goalentity.team == COLOR_TEAM2)
+                       bluepower = 1;
+       }
+       if(self.enemy.islinked)
+       {
+               if(self.enemy.team == COLOR_TEAM1)
+                       redpower = 2;
+               else if(self.enemy.team == COLOR_TEAM2)
+                       bluepower = 2;
+       }
+
+       float cc;
+       if(redpower == 1 && bluepower == 2)
+               cc = (COLOR_TEAM1 - 1) * 0x01 + (COLOR_TEAM2 - 1) * 0x10;
+       else if(redpower == 2 && bluepower == 1)
+               cc = (COLOR_TEAM1 - 1) * 0x10 + (COLOR_TEAM2 - 1) * 0x01;
+       else if(redpower)
+               cc = (COLOR_TEAM1 - 1) * 0x11;
+       else if(bluepower)
+               cc = (COLOR_TEAM2 - 1) * 0x11;
+       else
+               cc = 0;
+
+       //print(etos(self), " rp=", ftos(redpower), " bp=", ftos(bluepower), " ");
+       //print("cc=", ftos(cc), "\n");
+
+       if(cc != self.clientcolors)
+       {
+               self.clientcolors = cc;
+               self.SendFlags |= 4;
+       }
+
+       self.nextthink = time;
+}
+
+void onslaught_link_delayed()
+{
+       self.goalentity = find(world, targetname, self.target);
+       self.enemy = find(world, targetname, self.target2);
+       if (!self.goalentity)
+               objerror("can not find target\n");
+       if (!self.enemy)
+               objerror("can not find target2\n");
+       dprint(etos(self.goalentity), " linked with ", etos(self.enemy), "\n");
+       self.SendFlags |= 3;
+       self.think = onslaught_link_checkupdate;
+       self.nextthink = time;
+}
+
+/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16)
+  Link between control points.
+
+  This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams.
+
+keys:
+"target" - first control point.
+"target2" - second control point.
+ */
+void spawnfunc_onslaught_link()
+{
+       if (!g_onslaught)
+       {
+               remove(self);
+               return;
+       }
+       if (self.target == "" || self.target2 == "")
+               objerror("target and target2 must be set\n");
+       InitializeEntity(self, onslaught_link_delayed, INITPRIO_FINDTARGET);
+       Net_LinkEntity(self, FALSE, 0, onslaught_link_send);
+}
+
+MUTATOR_HOOKFUNCTION(ons_BuildMutatorsString)
+{
+       ret_string = strcat(ret_string, ":ONS");
+       return 0;
+}
+
+MUTATOR_HOOKFUNCTION(ons_BuildMutatorsPrettyString)
+{
+       ret_string = strcat(ret_string, ", Onslught");
+       return 0;
+}
+
+MUTATOR_HOOKFUNCTION(ons_Spawn_Score)
+{
+    
+    /*
+    float _neer_home = (random() > 0.5 ? TRUE : FALSE);
+    
+       RandomSelection_Init();
+       
+       if(self.team == COLOR_TEAM1)
+        RandomSelection_Add(ons_red_generator, 0, string_null, 1, 1);
+       
+       if(self.team == COLOR_TEAM2)
+        RandomSelection_Add(ons_blue_generator, 0, string_null, 1, 1);
+       
+       entity _cp = findchain(classname, "onslaught_controlpoint"):
+       while _cp;
+       {
+           if(_cp.team == self.team)            
+            RandomSelection_Add(_cp, 0, string_null, 1, 1);
+               
+               _cp = _cp.chain;
+       }
+
+       if(RandomSelection_chosen_ent)
+       {
+               self.tur_head = RandomSelection_chosen_ent;
+               spawn_score_x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND;
+       }
+       else if(self.team == spawn_spot.team)
+               spawn_score_x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate
+    
+    */
+    
+       return 0;
+}
+
+MUTATOR_HOOKFUNCTION(ons_PlayerSpawn)
+{
+    if(!autocvar_g_onslaught_spawn_at_controlpoints)
+        return 0;
+        
+    if(random() < 0.5)  // 50/50 chane to use default spawnsystem.
+        return 0;
+    
+    float _close_to_home = ((random() > 0.5) ? TRUE : FALSE);
+    entity _best, _trg_gen;
+    float _score, _best_score = MAX_SHOT_DISTANCE;
+    
+       RandomSelection_Init();
+    
+       if(self.team == COLOR_TEAM1)
+       {
+           if(!_close_to_home)
+            _trg_gen = ons_blue_generator;
+        else    
+            _trg_gen  = ons_red_generator;        
+       }
+       
+       if(self.team == COLOR_TEAM2)
+       {
+           if(_close_to_home)
+            _trg_gen = ons_blue_generator;
+        else    
+            _trg_gen  = ons_red_generator;        
+       }
+       
+       entity _cp = findchain(classname, "onslaught_controlpoint");
+       while(_cp)
+       {
+           if(_cp.team == self.team)            
+        {            
+            _score = vlen(_trg_gen.origin - _cp.origin);
+            if(_score < _best_score)
+            {
+                _best = _cp;
+                _best_score = _score;            
+            }
+        }              
+               _cp = _cp.chain;
+       }
+       
+    vector _loc;        
+    float i;    
+    if(_best)
+    {
+        for(i = 0; i < 10; ++i)
+        {
+            _loc = _best.origin + '0 0 96';
+            _loc += ('0 1 0' * random()) * 128; 
+            tracebox(_loc, PL_MIN, PL_MAX, _loc, MOVE_NORMAL, self);
+            if(trace_fraction == 1.0 && !trace_startsolid)
+            {
+                setorigin(self, _loc);
+                self.angles = normalize(_loc - _best.origin) * RAD2DEG;
+                return 0;
+            }
+        }
+    }
+    else
+    {
+        if(!autocvar_g_onslaught_spawn_at_generator)
+            return 0;
+        
+        _trg_gen = ((self.team == COLOR_TEAM1) ? ons_red_generator : ons_blue_generator);
+        
+        for(i = 0; i < 10; ++i)
+        {
+            _loc = _trg_gen.origin + '0 0 96';
+            _loc += ('0 1 0' * random()) * 128; 
+            tracebox(_loc, PL_MIN, PL_MAX, _loc, MOVE_NORMAL, self);
+            if(trace_fraction == 1.0 && !trace_startsolid)
+            {
+                setorigin(self, _loc);
+                self.angles = normalize(_loc - _trg_gen.origin) * RAD2DEG;
+                return 0;
+            }
+        }
+    }
+    
+    return 0;
+}
+
+MUTATOR_DEFINITION(gamemode_onslaught)
+{
+       //MUTATOR_HOOK(PlayerDies, nexball_BallDrop, CBC_ORDER_ANY);
+       //MUTATOR_HOOK(MakePlayerObserver, nexball_BallDrop, CBC_ORDER_ANY);
+       //MUTATOR_HOOK(ClientDisconnect, nexball_BallDrop, CBC_ORDER_ANY);
+       //MUTATOR_HOOK(PlayerPreThink, nexball_PlayerPreThink, CBC_ORDER_ANY);
+       MUTATOR_HOOK(BuildMutatorsPrettyString, ons_BuildMutatorsPrettyString, CBC_ORDER_ANY);
+       MUTATOR_HOOK(BuildMutatorsString, ons_BuildMutatorsString, CBC_ORDER_ANY);
+       MUTATOR_HOOK(PlayerSpawn, ons_PlayerSpawn, CBC_ORDER_ANY);
+       //MUTATOR_HOOK(Spawn_Score, ons_Spawn_Score, CBC_ORDER_ANY);
+       
+       MUTATOR_ONADD
+       {
+               //InitializeEntity(world, nb_delayedinit, INITPRIO_GAMETYPE);
+       }
+
+       return 0;
+}
diff --git a/qcsrc/server/mutators/mutator_superspec.qc b/qcsrc/server/mutators/mutator_superspec.qc
new file mode 100644 (file)
index 0000000..8b6bf05
--- /dev/null
@@ -0,0 +1,530 @@
+#define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1"
+#define _ISLOCAL ((edict_num(1) == self) ? TRUE : FALSE)
+
+#define ASF_STRENGTH        1
+#define ASF_SHIELD          2
+#define ASF_MEGA_AR         4
+#define ASF_MEGA_HP         8
+#define ASF_FLAG_GRAB       16
+#define ASF_OBSERVER_ONLY   32
+#define ASF_SHOWWHAT        64
+#define ASF_SSIM            128
+#define ASF_ALL             0xFFFFFF
+.float autospec_flags;
+
+#define SSF_SILENT          1
+#define SSF_VERBOSE         2
+#define SSF_ITEMMSG         4
+.float superspec_flags;
+
+.string superspec_itemfilter; //"classname1 classname2 ..."
+
+float _spectate(entity _player)
+{
+       if(SpectateNext(_player) == 1)
+       {
+               PutObserverInServer();
+               self.classname = "spectator";
+       }
+
+       return TRUE;
+}
+
+void superspec_save_client_conf()
+{
+       string fn = "superspec-local.options";
+       float fh;
+
+
+       if not(_ISLOCAL)
+       {
+               if(self.crypto_idfp == "")
+                       return;
+
+               fn = sprintf("superspec-%s.options", uri_escape(self.crypto_idfp));
+       }
+
+       fh = fopen(fn, FILE_WRITE);
+       if(fh < 0)
+       {
+               dprint("^1ERROR: ^7 superspec can not open ", fn, " for writing.\n");
+       }
+       else
+       {
+               fputs(fh, _SSMAGIX);
+               fputs(fh, "\n");
+               fputs(fh, ftos(self.autospec_flags));
+               fputs(fh, "\n");
+               fputs(fh, ftos(self.superspec_flags));
+               fputs(fh, "\n");
+               fputs(fh, self.superspec_itemfilter);
+               fputs(fh, "\n");
+               fclose(fh);
+       }
+}
+
+void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel)
+{
+
+       sprint(_to, strcat(_con_title, _msg));
+
+       if(_to.superspec_flags & SSF_SILENT)
+               return;
+
+       if(_spamlevel > 1)
+               if not(_to.superspec_flags & SSF_VERBOSE)
+                       return;
+
+       centerprint(_to, strcat(_center_title, _msg));
+}
+
+float superspec_filteritem(entity _for, entity _item)
+{
+       float i;
+
+       if(!_for.superspec_itemfilter)
+               return TRUE;
+
+       if(_for.superspec_itemfilter == "")
+               return TRUE;
+
+       float l = tokenize_console(_for.superspec_itemfilter);
+       for(i = 0; i < l; ++i)
+       {
+               if(argv(i) == _item.classname)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(superspec_ItemTouch)
+{
+       entity _oldself = self;
+       entity _item = self;
+
+       FOR_EACH_SPEC(self)
+       {
+               if(self.superspec_flags & SSF_ITEMMSG)
+                       if(superspec_filteritem(self, _item))
+                       {
+                               if(self.superspec_flags & SSF_VERBOSE)
+                                       superspec_msg("", "", self, sprintf("Player %s^7 just picked up ^3%s\n", other.netname, _item.netname), 1);
+                               else
+                                       superspec_msg("", "", self, sprintf("Player %s^7 just picked up ^3%s\n^8(%s^8)\n", other.netname, _item.netname, _item.classname), 1);
+                               if(self.autospec_flags& ASF_SSIM && self.enemy != other)
+                               {
+                                       _spectate(other);
+
+                                       self = _oldself;
+                                       return FALSE;
+                               }
+                       }
+
+
+               if((self.autospec_flags & ASF_SHIELD && _item.invincible_finished) ||
+                               (self.autospec_flags & ASF_STRENGTH && _item.strength_finished) ||
+                               (self.autospec_flags& ASF_MEGA_AR && _item.classname == "item_armor_large") ||
+                               (self.autospec_flags& ASF_MEGA_HP && _item.classname == "item_health_mega") ||
+                               (self.autospec_flags& ASF_FLAG_GRAB && _item.classname == "item_flag_team"))
+               {
+
+                       if((self.enemy != other) || self.classname == "observer")
+                       {
+                               if(self.autospec_flags & ASF_OBSERVER_ONLY && self.classname != "observer")
+                               {
+                                       if(self.superspec_flags & SSF_VERBOSE)
+                                               superspec_msg("", "", self, sprintf("^8Ignored that %s^8 grabbed %s^8 since the observer_only option is ON\n", other.netname, _item.netname), 2);
+                               }
+                               else
+                               {
+                                       if(self.autospec_flags & ASF_SHOWWHAT)
+                                               superspec_msg("", "", self, sprintf("^7Following %s^7 due to picking up %s\n", other.netname, _item.netname), 2);
+
+                                       _spectate(other);
+                               }
+                       }
+               }
+       }
+
+       self = _oldself;
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(superspec_SV_ParseClientCommand)
+{
+#define OPTIONINFO(flag,var,test,text,long,short) \
+    var = strcat(var, ((flag & test) ? "^2[ON]  ^7" : "^1[OFF] ^7")); \
+    var = strcat(var, text," ^7(^3 ", long, "^7 | ^3", short, " ^7)\n")
+
+       if(MUTATOR_RETURNVALUE) // command was already handled?
+               return FALSE;
+
+       if(self.classname == "player")
+               return FALSE;
+
+       if(cmd_name == "superspec_itemfilter")
+       {
+               if(argv(1) == "help")
+               {
+                       string _aspeco;
+                       _aspeco = strcat(_aspeco, "^7 superspec_itemfilter ^3\"item_classname1 item_classname2\"^7 only show thise items when ^2superspec ^3item_message^7 is on\n");
+                       _aspeco = strcat(_aspeco, "^3 clear^7 Remove the filter (show all pickups)\n");
+                       _aspeco = strcat(_aspeco, "^3 show ^7 Display current filter\n");
+                       superspec_msg("^3superspec_itemfilter help:\n\n\n", "\n^3superspec_itemfilter help:\n", self, _aspeco, 1);
+               }
+               else if(argv(1) == "clear")
+               {
+                       if(self.superspec_itemfilter != "")
+                               strunzone(self.superspec_itemfilter);
+
+                       self.superspec_itemfilter = "";
+               }
+               else if(argv(1) == "show" || argv(1) == "")
+               {
+                       if(self.superspec_itemfilter == "")
+                       {
+                               superspec_msg("^3superspec_itemfilter^7 is ^1not^7 set", "\n^3superspec_itemfilter^7 is ^1not^7 set\n", self, "", 1);
+                               return TRUE;
+                       }
+                       float i;
+                       float l = tokenize_console(self.superspec_itemfilter);
+                       string _msg;
+                       for(i = 0; i < l; ++i)
+                               _msg = strcat(_msg, "^3#", ftos(i), " ^7", argv(i), "\n");
+                               //_msg = sprintf("^3#%d^7 %s\n%s", i, _msg, argv(i));
+
+                       _msg = strcat(_msg,"\n");
+
+                       superspec_msg("^3superspec_itemfilter is:\n\n\n", "\n^3superspec_itemfilter is:\n", self, _msg, 1);
+               }
+               else
+               {
+                       if(self.superspec_itemfilter != "")
+                               strunzone(self.superspec_itemfilter);
+
+                       self.superspec_itemfilter = strzone(argv(1));
+               }
+
+
+               return TRUE;
+       }
+
+       if(cmd_name == "superspec")
+       {
+               string _aspeco;
+
+               if(cmd_argc > 1)
+               {
+                       float i, _bits, _start = 1;
+                       if(argv(1) == "help")
+                       {
+                               _aspeco = "";
+                               _aspeco = strcat(_aspeco, "use cmd superspec [option] [on|off] to set options\n\n");
+                               _aspeco = strcat(_aspeco, "^3 silent ^7(short^5 si^7) supress ALL mesagess from superspectate.\n");
+                               _aspeco = strcat(_aspeco, "^3 verrbose ^7(short^5 ve^7) makes superspectate print some additional information.\n");
+                               _aspeco = strcat(_aspeco, "^3 item_message ^7(short^5 im^7) makes superspectate print items that was picked up.\n");
+                               _aspeco = strcat(_aspeco, "^7    Use cmd superspec_itemfilter \"item_class1 item_class2\" to set up a filter of what to show with ^3item_message.\n");
+                               superspec_msg("^2Available Super Spectate ^3options:\n\n\n", "\n^2Available Super Spectate ^3options:\n", self, _aspeco, 1);
+                               return TRUE;
+                       }
+
+                       if(argv(1) == "clear")
+                       {
+                               self.superspec_flags = 0;
+                               _start = 2;
+                       }
+
+                       for(i = _start; i < cmd_argc; ++i)
+                       {
+                               if(argv(i) == "on" || argv(i) == "1")
+                               {
+                                       self.superspec_flags |= _bits;
+                                       _bits = 0;
+                               }
+                               else if(argv(i) == "off" || argv(i) == "0")
+                               {
+                                       if(_start == 1)
+                                               self.superspec_flags &~= _bits;
+
+                                       _bits = 0;
+                               }
+                               else
+                               {
+                                       if((argv(i) == "silent") || (argv(i) == "si")) _bits |= SSF_SILENT ;
+                                       if((argv(i) == "verbose") || (argv(i) == "ve")) _bits |= SSF_VERBOSE;
+                                       if((argv(i) == "item_message") || (argv(i) == "im")) _bits |= SSF_ITEMMSG;
+                               }
+                       }
+               }
+
+
+               OPTIONINFO(self.superspec_flags, _aspeco, SSF_SILENT, "Silent", "silent", "si");
+               OPTIONINFO(self.superspec_flags, _aspeco, SSF_VERBOSE, "Verbose", "verbose", "ve");
+               OPTIONINFO(self.superspec_flags, _aspeco, SSF_ITEMMSG, "Item pickup messages", "item_message", "im");
+
+               superspec_msg("^3Current Super Spectate options are:\n\n\n\n\n", "\n^3Current Super Spectate options are:\n", self, _aspeco, 1);
+               return TRUE;
+
+       }
+
+/////////////////////
+
+       if(cmd_name == "autospec")
+       {
+               string _aspeco;
+               if(cmd_argc > 1)
+               {
+                       if(argv(1) == "help")
+                       {
+                               _aspeco = "";
+                               _aspeco = strcat(_aspeco, "use cmd autospec [option] [on|off] to set options\n\n");
+                               _aspeco = strcat(_aspeco, "^3 strength ^7(short^5 st^7) for automatic spectate on strength powerup\n");
+                               _aspeco = strcat(_aspeco, "^3 shield ^7(short^5 sh^7) for automatic spectate on shield powerup\n");
+                               _aspeco = strcat(_aspeco, "^3 mega_health ^7(short^5 mh^7) for automatic spectate on mega health\n");
+                               _aspeco = strcat(_aspeco, "^3 mega_armor ^7(short^5 ma^7) for automatic spectate on mega armor\n");
+                               _aspeco = strcat(_aspeco, "^3 flag_grab ^7(short^5 fg^7) for automatic spectate on CTF flag grab\n");
+                               _aspeco = strcat(_aspeco, "^3 observer_only (short^5 oo^7) for automatic spectate only if in observer mode\n");
+                               _aspeco = strcat(_aspeco, "^3 show_what (short^5 sw^7) to display what event triggerd autospectate\n");
+                               _aspeco = strcat(_aspeco, "^3 item_msg ^7(short^5 im^7) to autospec when item_message in superspectate is triggerd\n");
+                               _aspeco = strcat(_aspeco, "^3 all ^7(short ^5aa^7) turn everything on/off\n");
+                               superspec_msg("^2Available Auto Spectate ^3options:\n\n\n", "\n^2Available Auto Spectate ^3options:\n", self, _aspeco, 1);
+                               return TRUE;
+                       }
+
+                       float i, _bits, _start = 1;
+                       if(argv(1) == "clear")
+                       {
+                               self.autospec_flags = 0;
+                               _start = 2;
+                       }
+
+                       for(i = _start; i < cmd_argc; ++i)
+                       {
+                               if(argv(i) == "on" || argv(i) == "1")
+                               {
+                                       self.autospec_flags |= _bits;
+                                       _bits = 0;
+                               }
+                               else if(argv(i) == "off" || argv(i) == "0")
+                               {
+                                       if(_start == 1)
+                                               self.autospec_flags &~= _bits;
+
+                                       _bits = 0;
+                               }
+                               else
+                               {
+                                       if((argv(i) == "strength") || (argv(i) == "st")) _bits |= ASF_STRENGTH;
+                                       if((argv(i) == "shield") || (argv(i) == "sh")) _bits |= ASF_SHIELD;
+                                       if((argv(i) == "mega_health") || (argv(i) == "mh")) _bits |= ASF_MEGA_HP;
+                                       if((argv(i) == "mega_armor") || (argv(i) == "ma")) _bits |= ASF_MEGA_AR;
+                                       if((argv(i) == "flag_grab") || (argv(i) == "fg")) _bits |= ASF_FLAG_GRAB;
+                                       if((argv(i) == "observer_only") || (argv(i) == "oo")) _bits |= ASF_OBSERVER_ONLY;
+                                       if((argv(i) == "show_what") || (argv(i) == "sw")) _bits |= ASF_SHOWWHAT;
+                                       if((argv(i) == "item_msg") || (argv(i) == "im")) _bits |= ASF_SSIM;
+                                       if((argv(i) == "all") || (argv(i) == "aa")) _bits |= ASF_ALL;
+                               }
+                       }
+               }
+
+               OPTIONINFO(self.autospec_flags, _aspeco, ASF_STRENGTH, "Strength", "strength", "st");
+               OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHIELD, "Shield", "shield", "sh");
+               OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_HP, "Mega Health", "mega_health", "mh");
+               OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_AR, "Mega Armor", "mega_armor", "ma");
+               OPTIONINFO(self.autospec_flags, _aspeco, ASF_FLAG_GRAB, "Flag grab", "flag_grab","fg");
+               OPTIONINFO(self.autospec_flags, _aspeco, ASF_OBSERVER_ONLY, "Only switch if Observer", "observer_only", "oo");
+               OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHOWWHAT, "Show what item triggered spectate", "show_what", "sw");
+               OPTIONINFO(self.autospec_flags, _aspeco, ASF_SSIM, "Switch on superspec item message", "item_msg", "im");
+
+               superspec_msg("^3Current auto spectate options are:\n\n\n\n\n", "\n^3Current auto spectate options are:\n", self, _aspeco, 1);
+               return TRUE;
+       }
+
+       if(cmd_name == "followpowerup")
+       {
+               entity _player;
+               FOR_EACH_PLAYER(_player)
+               {
+                       if(_player.strength_finished > time || _player.invincible_finished > time)
+                               return _spectate(_player);
+               }
+
+               superspec_msg("", "", self, "No active powerups\n", 1);
+               return TRUE;
+       }
+
+       if(cmd_name == "followstrength")
+       {
+               entity _player;
+               FOR_EACH_PLAYER(_player)
+               {
+                       if(_player.strength_finished > time)
+                               return _spectate(_player);
+               }
+
+               superspec_msg("", "", self, "No active Strength\n", 1);
+               return TRUE;
+       }
+
+       if(cmd_name == "followstshield")
+       {
+               entity _player;
+               FOR_EACH_PLAYER(_player)
+               {
+                       if(_player.invincible_finished > time)
+                               return _spectate(_player);
+               }
+
+               superspec_msg("", "", self, "No active Shield\n", 1);
+               return TRUE;
+       }
+
+       if(cmd_name == "followfc")
+       {
+               if(!g_ctf)
+                       return TRUE;
+
+               entity _player;
+               float _team;
+
+               if(cmd_argc == 2)
+               {
+                       if(argv(1) == "red")
+                               _team = COLOR_TEAM1;
+                       else
+                               _team = COLOR_TEAM2;
+               }
+
+               FOR_EACH_PLAYER(_player)
+               {
+                       if(_player.flagcarried && (_player.team == _team || _team == 0))
+                               return _spectate(_player);
+               }
+
+               superspec_msg("", "", self, "No active flag carrier\n", 1);
+               return TRUE;
+       }
+
+       return FALSE;
+#undef OPTIONINFO
+}
+
+MUTATOR_HOOKFUNCTION(superspec_BuildMutatorsString)
+{
+       ret_string = strcat(ret_string, ":SS");
+       return 0;
+}
+
+MUTATOR_HOOKFUNCTION(superspec_BuildMutatorsPrettyString)
+{
+       ret_string = strcat(ret_string, ", Super Spectators");
+       return 0;
+}
+
+/*
+MUTATOR_HOOKFUNCTION(superspec_PlayerSpawn)
+{
+
+    return FALSE;
+}
+*/
+
+void superspec_hello()
+{
+       if(self.enemy.crypto_idfp == "")
+               centerprint(self.enemy, "Your client have/allow no crypto id, superspec options will not be saved/restored.");
+       else
+               centerprint(self.enemy, sprintf("Hello %s\nSince your client has a Crypto ID, your superspec preferenses will be presisted on this server.", self.enemy.netname));
+
+       remove(self);
+}
+
+MUTATOR_HOOKFUNCTION(superspec_ClientConnect)
+{
+       string fn = "superspec-local.options";
+       float fh;
+
+       self.superspec_flags = SSF_VERBOSE;
+       self.superspec_itemfilter = "";
+
+       entity _hello = spawn();
+       _hello.enemy = self;
+       _hello.think = superspec_hello;
+       _hello.nextthink = time + 5;
+
+       if not(_ISLOCAL)
+       {
+               if(self.crypto_idfp == "")
+                       return FALSE;
+
+               fn = sprintf("superspec-%s.options", uri_escape(self.crypto_idfp));
+       }
+
+       fh = fopen(fn, FILE_READ);
+       if(fh < 0)
+       {
+               dprint("^1ERROR: ^7 superspec can not open ", fn, " for reading.\n");
+       }
+       else
+       {
+               string _magic = fgets(fh);
+               if(_magic != _SSMAGIX)
+               {
+                       dprint("^1ERROR^7 While reading superspec options file: unkown magic\n");
+               }
+               else
+               {
+                       self.autospec_flags = stof(fgets(fh));
+                       self.superspec_flags = stof(fgets(fh));
+                       self.superspec_itemfilter = strzone(fgets(fh));
+               }
+               fclose(fh);
+       }
+
+       return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(superspec_ClientDisconnect)
+{
+       superspec_save_client_conf();
+       return FALSE;
+}
+
+
+/*
+MUTATOR_HOOKFUNCTION(superspec_MakePlayerObserver)
+{
+    return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(superspec_PlayerPreThink)
+{
+    return FALSE;
+}
+*/
+
+MUTATOR_DEFINITION(mutator_superspec)
+{
+
+       MUTATOR_HOOK(BuildMutatorsString, superspec_BuildMutatorsString, CBC_ORDER_ANY);
+       MUTATOR_HOOK(BuildMutatorsPrettyString, superspec_BuildMutatorsPrettyString, CBC_ORDER_ANY);
+       MUTATOR_HOOK(SV_ParseClientCommand, superspec_SV_ParseClientCommand, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ItemTouch, superspec_ItemTouch, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ClientConnect, superspec_ClientConnect, CBC_ORDER_ANY);
+       //MUTATOR_HOOK(PlayerSpawn, superspec_PlayerSpawn, CBC_ORDER_ANY);
+       //MUTATOR_HOOK(PlayerPreThink, superspec_PlayerPreThink, CBC_ORDER_ANY);
+       //MUTATOR_HOOK(MakePlayerObserver, superspec_MakePlayerObserver, CBC_ORDER_ANY);
+       MUTATOR_HOOK(ClientDisconnect, superspec_ClientDisconnect, CBC_ORDER_ANY);
+
+       MUTATOR_ONADD
+       {
+       }
+
+       MUTATOR_ONREMOVE
+       {
+       }
+
+       return 0;
+}
index 4e7d9a6512bd47b8806d2f791fa5fc04cd9ab035..2ac6094d339d578d174da5ec3ed06eed91fcad20 100644 (file)
@@ -1,7 +1,9 @@
 MUTATOR_DECLARATION(gamemode_keyhunt);
 MUTATOR_DECLARATION(gamemode_freezetag);
 MUTATOR_DECLARATION(gamemode_keepaway);
+MUTATOR_DECLARATION(gamemode_ctf);
 MUTATOR_DECLARATION(gamemode_nexball);
+MUTATOR_DECLARATION(gamemode_onslaught);
 
 MUTATOR_DECLARATION(mutator_dodging);
 MUTATOR_DECLARATION(mutator_invincibleprojectiles);
@@ -10,5 +12,6 @@ MUTATOR_DECLARATION(mutator_nix);
 MUTATOR_DECLARATION(mutator_rocketflying);
 MUTATOR_DECLARATION(mutator_spawn_near_teammate);
 MUTATOR_DECLARATION(mutator_vampire);
+MUTATOR_DECLARATION(mutator_superspec);
 
 MUTATOR_DECLARATION(sandbox);
index 87220328fb8a58ba5aa435bbc274cdb6d62002aa..4608b56df10f1330011e64e358e7c155035a42ef 100644 (file)
@@ -5,7 +5,7 @@ string events_last;
 .float playerstats_addedglobalinfo;
 .string playerstats_id;
 
-void PlayerStats_Init()
+void PlayerStats_Init() // initiated before InitGameplayMode so that scores are added properly
 {
        string uri;
        playerstats_db = -1;
@@ -20,6 +20,7 @@ void PlayerStats_Init()
        serverflags |= SERVERFLAG_PLAYERSTATS;  
 
        PlayerStats_AddEvent(PLAYERSTATS_ALIVETIME);
+       PlayerStats_AddEvent(PLAYERSTATS_AVGLATENCY);
        PlayerStats_AddEvent(PLAYERSTATS_WINS);
        PlayerStats_AddEvent(PLAYERSTATS_MATCHES);
        PlayerStats_AddEvent(PLAYERSTATS_JOINS);
@@ -174,7 +175,7 @@ void PlayerStats_TeamScore(float t, string event_id, float value) // TODO: doesn
 
        The following keys are defined:
 
-       V: format version (always 1) - this MUST be the first line!
+       V: format version (always a fixed number) - this MUST be the first line!
        #: comment (MUST be ignored by any parser)
        R: release information on the server
        T: time at which the game ended
@@ -193,6 +194,7 @@ void PlayerStats_TeamScore(float t, string event_id, float value) // TODO: doesn
        e: followed by an event name, a space, and the event count/score
                event names can be:
                        alivetime: total playing time of the player
+                       avglatency: average network latency compounded throughout the match
                        wins: number of games won (can only be set if matches is set)
                        matches: number of matches played to the end (not aborted by map switch)
                        joins: number of matches joined (always 1 unless player never played during the match)
@@ -236,7 +238,7 @@ void PlayerStats_ready(entity fh, entity pass, float status)
        switch(status)
        {
                case URL_READY_CANWRITE:
-                       url_fputs(fh, "V 1\n");
+                       url_fputs(fh, "V 5\n");
 #ifdef WATERMARK
                        url_fputs(fh, sprintf("R %s\n", WATERMARK()));
 #endif
@@ -344,6 +346,7 @@ void PlayerStats_Accuracy(entity p)
 
         PlayerStats_Event(p, strcat("acc-", w.netname, "-frags"), a.(accuracy_frags[i-1]));
     }
+    //backtrace(strcat("adding player stat accuracy for ", p.netname, ".\n"));
 }
 
 void PlayerStats_AddGlobalInfo(entity p)
@@ -384,17 +387,14 @@ void PlayerStats_EndMatch(float finished)
        winner = PlayerScore_Sort(score_dummyfield);
        FOR_EACH_CLIENT(p) // spectators intentionally not included
        {
-               PlayerStats_Accuracy(p);
-               if(g_arena || g_lms || g_ca)
-               {
-                       if(p.alivetime <= 0)
-                               continue;
-               }
-               else
-               {
-                       if(p.classname != "player")
-                               continue;
-               }
+               //PlayerStats_Accuracy(p); // stats are already written with PlayerStats_AddGlobalInfo(entity), don't double them up.
+               
+               if((g_arena || g_lms || g_ca) && (p.alivetime <= 0)) { continue; }
+               else if(p.classname != "player") { continue; }
+
+               float latency = (p.latency_sum / p.latency_cnt);
+               if(latency) { PlayerStats_Event(p, PLAYERSTATS_AVGLATENCY, latency); }
+               
                PlayerScore_PlayerStats(p);
                PlayerStats_Event(p, PLAYERSTATS_SCOREBOARD_VALID, 1);
                if(finished)
index b99366434b9e214bbeff607a333dc81f1d6271b6..11a311028d9d13c70ffdbeb32999c852d5cc1183 100644 (file)
@@ -1,5 +1,6 @@
 // time the player was alive and kicking
 string PLAYERSTATS_ALIVETIME  = "alivetime";
+string PLAYERSTATS_AVGLATENCY = "avglatency";
 string PLAYERSTATS_WINS = "wins";
 string PLAYERSTATS_MATCHES = "matches";
 string PLAYERSTATS_JOINS = "joins";
index 76af253dca5c0f3bd3b5785134f9c378687fdf8c..75dae14bf5f28d666e9beb91e820e912389a1a54 100644 (file)
@@ -154,8 +154,11 @@ float Portal_TeleportPlayer(entity teleporter, entity player)
        // factor -1 allows chaining portals, but may be weird
        player.right_vector = -1 * AnglesTransform_Apply(transform, player.right_vector);
 
-       if(player.flagcarried)
-               DropFlag(player.flagcarried, player, world);
+       entity oldself = self;
+       self = player;
+       MUTATOR_CALLHOOK(PortalTeleport);
+       player = self;
+       self = oldself;
 
        if not(teleporter.enemy)
        {
index 1890eddd97a679ae3f279e258dcfe0eb55c6c9f4..028519372f025ae24aa923d472ba0a6593de86b7 100644 (file)
@@ -29,7 +29,9 @@ defs.qh               // Should rename this, it has fields and globals
 
 mutators/base.qh
 mutators/mutators.qh
+mutators/gamemode_ctf.qh
 mutators/gamemode_keyhunt.qh // TODO fix this
+mutators/gamemode_keepaway.qh
 mutators/gamemode_nexball.qh 
 mutators/mutator_dodging.qh
 
@@ -138,9 +140,9 @@ cl_client.qc
 t_plats.qc
 antilag.qc
 
-ctf.qc
+//ctf.qc
 domination.qc
-mode_onslaught.qc
+//mode_onslaught.qc
 //nexball.qc
 g_hook.qc
 
@@ -205,10 +207,12 @@ playerstats.qc
 ../common/explosion_equation.qc
 
 mutators/base.qc
-mutators/gamemode_nexball.qc
-mutators/gamemode_keyhunt.qc
+mutators/gamemode_ctf.qc
 mutators/gamemode_freezetag.qc
+mutators/gamemode_keyhunt.qc
 mutators/gamemode_keepaway.qc
+mutators/gamemode_nexball.qc
+mutators/gamemode_onslaught.qc
 mutators/mutator_invincibleproj.qc
 mutators/mutator_new_toys.qc
 mutators/mutator_nix.qc
@@ -217,6 +221,7 @@ mutators/mutator_rocketflying.qc
 mutators/mutator_vampire.qc
 mutators/mutator_spawn_near_teammate.qc
 mutators/sandbox.qc
+mutators/mutator_superspec.qc
 
 ../warpzonelib/anglestransform.qc
 ../warpzonelib/mathlib.qc
index 806e2450792d356ff0c1298bcbdc53fefd614780..c4021fc397ea3d2cf28c67e004bb9afdde68e1aa 100644 (file)
@@ -44,26 +44,6 @@ void ScoreRules_generic()
        ScoreRules_basics_end();
 }
 
-// g_ctf
-#define ST_CTF_CAPS 1
-#define SP_CTF_CAPS 4
-#define SP_CTF_PICKUPS 5
-#define SP_CTF_DROPS 6
-#define SP_CTF_FCKILLS 7
-#define SP_CTF_RETURNS 8
-void ScoreRules_ctf()
-{
-       CheckAllowedTeams(world);
-       ScoreRules_basics(2 + (c3>=0), SFL_SORT_PRIO_PRIMARY, 0, TRUE); // NOTE this assumes that the rogue team is team 3
-       ScoreInfo_SetLabel_TeamScore  (ST_CTF_CAPS,     "caps",      SFL_SORT_PRIO_PRIMARY);
-       ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPS,     "caps",      SFL_SORT_PRIO_SECONDARY);
-       ScoreInfo_SetLabel_PlayerScore(SP_CTF_PICKUPS,  "pickups",   0);
-       ScoreInfo_SetLabel_PlayerScore(SP_CTF_FCKILLS,  "fckills",   0);
-       ScoreInfo_SetLabel_PlayerScore(SP_CTF_RETURNS,  "returns",   0);
-       ScoreInfo_SetLabel_PlayerScore(SP_CTF_DROPS,    "drops",     SFL_LOWER_IS_BETTER);
-       ScoreRules_basics_end();
-}
-
 // g_domination
 #define ST_DOM_TICKS 1
 #define SP_DOM_TICKS 4
@@ -169,19 +149,6 @@ void ScoreRules_nexball(float teams)
        ScoreRules_basics_end();
 }
 
-// Keep Away stuff
-#define SP_KEEPAWAY_PICKUPS 4
-#define SP_KEEPAWAY_CARRIERKILLS 5
-#define SP_KEEPAWAY_BCTIME 6
-void ScoreRules_keepaway()
-{
-       ScoreRules_basics(0, SFL_SORT_PRIO_PRIMARY, 0, TRUE); // SFL_SORT_PRIO_PRIMARY
-       ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_PICKUPS,                     "pickups",              0);
-       ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_CARRIERKILLS,        "bckills",              0);
-       ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_BCTIME,                      "bctime",                       SFL_SORT_PRIO_SECONDARY);
-       ScoreRules_basics_end();
-}
-
 // FreezeTag stuff
 #define SP_FREEZETAG_REVIVALS 4
 void ScoreRules_freezetag()
index 007c6369ede68e3e5239d1b774076b1cf65e3840..63d91f6070ca3ecee2ecb56527f7a0c6710c9a98 100644 (file)
@@ -231,8 +231,6 @@ void StartFrame (void)
        CreatureFrame ();
        CheckRules_World ();
 
-       AuditTeams();
-
        RuneMatchGivePoints();
        bot_serverframe();
 
index e90055b27873b210ea33d4bdfc9731541a4e5224..bc8638dd2f123fa2830fc90ca1b2bdbb054d280d 100644 (file)
     #define ITS_POWERUP   64
 #define ISF_COLORMAP 16
 #define ISF_DROP 32
+#define ISF_ANGLES 64
 
 .float ItemStatus;
 
 #ifdef CSQC
 
-var float  autocvar_cl_ghost_items = 1;
+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';
-float  autocvar_cl_fullbright_items;
-vector autocvar_cl_staywep_color;
-float  autocvar_cl_staywep_alpha;
-float  autocvar_cl_simple_items;
-float  cl_simple_items;
-float  cl_ghost_items_alpha;
-
+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.ItemStatus & ITS_ANIMATE1)
-    {
-        self.angles += '0 180 0' * frametime;
-        setorigin(self, '0 0 10' + self.oldorigin + '0 0 8' * sin(time * 2));        
-    }    
-    
-    if(self.ItemStatus & ITS_ANIMATE2)
-    {
-        self.angles += '0 -90 0' * frametime;
-        setorigin(self, '0 0 8' + self.oldorigin + '0 0 4' * sin(time * 3));        
-    }
-    
     if(self.gravity)
+    {        
         Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
+        if(self.move_flags & FL_ONGROUND) 
+        { // For some reason move_avelocity gets set to '0 0 0' here ...
+            self.oldorigin = self.origin;
+            self.gravity = 0;
+
+            if(autocvar_cl_animate_items)   
+            { // ... so reset it if animations are requested. 
+                if(self.ItemStatus & ITS_ANIMATE1)
+                    self.move_avelocity = '0 180 0';
+                
+                if(self.ItemStatus & ITS_ANIMATE2)
+                    self.move_avelocity = '0 -90 0';
+            }
+        }
+    }
+    else if (autocvar_cl_animate_items)
+    {        
+        if(self.ItemStatus & ITS_ANIMATE1)
+        {
+            self.angles += self.move_avelocity * frametime;
+            setorigin(self, '0 0 10' + self.oldorigin + '0 0 8' * sin(time * 2));        
+        }    
+        
+        if(self.ItemStatus & ITS_ANIMATE2)
+        {
+            self.angles += self.move_avelocity * frametime;
+            setorigin(self, '0 0 8' + self.oldorigin + '0 0 4' * sin(time * 3));        
+        }
+    }
 }
 
 void ItemDrawSimple()
 {
     if(self.gravity)
+    {        
         Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);    
-}
-
-float csqcitems_started; // remove this after a release or two
-void csqcitems_start()
-{
-    if(autocvar_cl_ghost_items == 1)
-        cl_ghost_items_alpha = 0.55;
-    else
-        cl_ghost_items_alpha = bound(0, autocvar_cl_ghost_items, 1);
-    
-    csqcitems_started = TRUE;
+        
+        if(self.move_flags & FL_ONGROUND)
+            self.gravity = 0;
+    }
 }
 
 void ItemRead(float _IsNew)
 {
-    if(!csqcitems_started)
-        csqcitems_start();
-    
     float sf = ReadByte();
 
     if(sf & ISF_LOCATION)
@@ -78,6 +87,14 @@ void ItemRead(float _IsNew)
         self.oldorigin = self.origin;
     }
     
+    if(sf & ISF_ANGLES) 
+    {
+        self.angles_x = ReadCoord();
+        self.angles_y = ReadCoord();
+        self.angles_z = ReadCoord();        
+        self.move_angles = self.angles;
+    }
+    
     if(sf & ISF_STATUS) // need to read/write status frist so model can handle simple, fb etc.
     {
         self.ItemStatus = ReadByte();    
@@ -89,7 +106,7 @@ void ItemRead(float _IsNew)
         }
         else
         {
-            if (cl_ghost_items_alpha)
+            if (autocvar_cl_ghost_items_color)
             {
                 self.alpha = autocvar_cl_ghost_items;
                 self.colormod = self.glowmod = autocvar_cl_ghost_items_color;
@@ -104,8 +121,8 @@ void ItemRead(float _IsNew)
             
         if(self.ItemStatus & ITS_STAYWEP)
         {
-            self.colormod = self.glowmod = autocvar_cl_staywep_color;
-            self.alpha = autocvar_cl_staywep_alpha;
+            self.colormod = self.glowmod = autocvar_cl_weapon_stay_color;
+            self.alpha = autocvar_cl_weapon_stay_alpha;
             
         }
         
@@ -130,19 +147,21 @@ void ItemRead(float _IsNew)
         self.mdl = "";
         string _fn = ReadString();
         
-        if(cl_simple_items && (self.ItemStatus & ITS_ALLOWSI))
+        if(autocvar_cl_simple_items && (self.ItemStatus & ITS_ALLOWSI))
         {
             string _fn2 = substring(_fn, 0 , strlen(_fn) -4);
             self.draw = ItemDrawSimple;
                     
-            if(fexists(strcat(_fn2, "_simple.md3")))
-                self.mdl = strzone(strcat(_fn2, "_simple.md3"));
-            else if(fexists(strcat(_fn2, "_simple.dpm")))
-                self.mdl = strzone(strcat(_fn2, "_simple.dpm"));
-            else if(fexists(strcat(_fn2, "_simple.iqm")))
-                self.mdl = strzone(strcat(_fn2, "_simple.iqm"));
-            else if(fexists(strcat(_fn2, "_simple.obj")))
-                self.mdl = strzone(strcat(_fn2, "_simple.obj"));
+            
+            
+            if(fexists(sprintf("%s%s.md3", _fn2, autocvr_cl_simpleitems_postfix)))
+                self.mdl = strzone(sprintf("%s%s.md3", _fn2, autocvr_cl_simpleitems_postfix));
+            else if(fexists(sprintf("%s%s.dpm", _fn2, autocvr_cl_simpleitems_postfix)))
+                self.mdl = strzone(sprintf("%s%s.dpm", _fn2, autocvr_cl_simpleitems_postfix));
+            else if(fexists(sprintf("%s%s.iqm", _fn2, autocvr_cl_simpleitems_postfix)))
+                self.mdl = strzone(sprintf("%s%s.iqm", _fn2, autocvr_cl_simpleitems_postfix));
+            else if(fexists(sprintf("%s%s.obj", _fn2, autocvr_cl_simpleitems_postfix)))
+                self.mdl = strzone(sprintf("%s%s.obj", _fn2, autocvr_cl_simpleitems_postfix));
             else
             {
                 self.draw = ItemDraw;
@@ -167,6 +186,7 @@ void ItemRead(float _IsNew)
     if(sf & ISF_DROP)
     {
         self.gravity = 1;
+        self.move_angles = '0 0 0';
         self.move_movetype = MOVETYPE_TOSS;
         self.move_velocity_x = ReadCoord();
         self.move_velocity_y = ReadCoord();
@@ -181,8 +201,18 @@ void ItemRead(float _IsNew)
         }
         else
             self.move_time = max(self.move_time, time);
-    }    
+    }
+        
+    if(autocvar_cl_animate_items)
+    {        
+        if(self.ItemStatus & ITS_ANIMATE1)
+            self.move_avelocity = '0 180 0';
+                
+        if(self.ItemStatus & ITS_ANIMATE2)
+            self.move_avelocity = '0 -90 0';
+    }
 }
+
 #endif
 
 #ifdef SVQC
@@ -197,7 +227,6 @@ float ItemSend(entity to, float sf)
        WriteByte(MSG_ENTITY, ENT_CLIENT_ITEM); 
        WriteByte(MSG_ENTITY, sf);
 
-        
        //WriteByte(MSG_ENTITY, self.cnt);
     if(sf & ISF_LOCATION)
     {
@@ -205,6 +234,13 @@ float ItemSend(entity to, float sf)
         WriteCoord(MSG_ENTITY, self.origin_y);
         WriteCoord(MSG_ENTITY, self.origin_z);
     }
+    
+    if(sf & ISF_ANGLES)
+    {
+        WriteCoord(MSG_ENTITY, self.angles_x);
+        WriteCoord(MSG_ENTITY, self.angles_y);
+        WriteCoord(MSG_ENTITY, self.angles_z);
+    }
 
     if(sf & ISF_STATUS)
         WriteByte(MSG_ENTITY, self.ItemStatus);
@@ -841,7 +877,7 @@ float Item_GiveTo(entity item, entity player)
                        pickedup = TRUE;
                        // sound not available
                        // AnnounceTo(player, "speed");
-                       player.invincible_finished = max(player.invincible_finished, time) + autocvar_g_balance_powerup_strength_time;
+                       player.invincible_finished = max(player.invincible_finished, time) + autocvar_g_balance_powerup_invincible_time;
                }
        }
        else
@@ -921,7 +957,7 @@ float Item_GiveTo(entity item, entity player)
 void Item_Touch (void)
 {
        entity e, head;
-
+       
        // remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky)
        if(self.classname == "droppedweapon")
        {
@@ -940,6 +976,8 @@ void Item_Touch (void)
                return;
        if (self.owner == other)
                return;
+       if(MUTATOR_CALLHOOK(ItemTouch))
+               return;
 
        if (self.classname == "droppedweapon")
        {
@@ -1253,6 +1291,9 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime,
                        remove (self);
                        return;
                }
+               
+               if(self.angles != '0 0 0')
+            self.SendFlags |= ISF_ANGLES;
 
                self.reset = Item_Reset;
                // it's a level item
index eef4d28b8c67d644b9e3fc51b5efda64dda6febe..8923c19f18300e1f742acd0586e61539744c3c15 100644 (file)
@@ -2,6 +2,7 @@ float PUSH_ONCE                 = 1;
 float PUSH_SILENT              = 2;
 
 .float pushltime;
+.float istypefrag;
 .float height;
 
 void() SUB_UseTargets;
@@ -213,6 +214,7 @@ void trigger_push_touch()
 
                // reset tracking of who pushed you into a hazard (for kill credit)
                other.pushltime = 0;
+               other.istypefrag = 0;
        }
 
        if(self.enemy.target)
index 8d81ca263d60f04ff8df54a6535876d64e0e6e33..cd3c8d10607b54642413455633077681f03e17f2 100644 (file)
@@ -1225,24 +1225,32 @@ entity spawn_field(vector fmins, vector fmaxs)
 }
 
 
-float EntitiesTouching(entity e1, entity e2)
+entity LinkDoors_nextent(entity cur, entity near, entity pass)
 {
-       if (e1.absmin_x > e2.absmax_x)
+       while((cur = find(cur, classname, self.classname)) && ((cur.spawnflags & 4) || cur.enemy))
+       {
+       }
+       return cur;
+}
+
+float LinkDoors_isconnected(entity e1, entity e2, entity pass)
+{
+       float DELTA = 4;
+       if (e1.absmin_x > e2.absmax_x + DELTA)
                return FALSE;
-       if (e1.absmin_y > e2.absmax_y)
+       if (e1.absmin_y > e2.absmax_y + DELTA)
                return FALSE;
-       if (e1.absmin_z > e2.absmax_z)
+       if (e1.absmin_z > e2.absmax_z + DELTA)
                return FALSE;
-       if (e1.absmax_x < e2.absmin_x)
+       if (e2.absmin_x > e1.absmax_x + DELTA)
                return FALSE;
-       if (e1.absmax_y < e2.absmin_y)
+       if (e2.absmin_y > e1.absmax_y + DELTA)
                return FALSE;
-       if (e1.absmax_z < e2.absmin_z)
+       if (e2.absmin_z > e1.absmax_z + DELTA)
                return FALSE;
        return TRUE;
 }
 
-
 /*
 =============
 LinkDoors
@@ -1252,7 +1260,7 @@ LinkDoors
 */
 void LinkDoors()
 {
-       entity  t, starte;
+       entity  t;
        vector  cmins, cmaxs;
 
        if (self.enemy)
@@ -1272,68 +1280,70 @@ void LinkDoors()
                return;         // don't want to link this door
        }
 
-       cmins = self.absmin;
-       cmaxs = self.absmax;
-
-       starte = self;
-       t = self;
+       FindConnectedComponent(self, enemy, LinkDoors_nextent, LinkDoors_isconnected, world);
 
-       do
+       // set owner, and make a loop of the chain
+       dprint("LinkDoors: linking doors:");
+       for(t = self; ; t = t.enemy)
        {
-               self.owner = starte;                    // master door
-
-               if (self.health)
-                       starte.health = self.health;
-               IFTARGETED
-                       starte.targetname = self.targetname;
-               if (self.message != "")
-                       starte.message = self.message;
-
-               t = find(t, classname, self.classname);
-               if (!t)
+               dprint(" ", etos(t));
+               t.owner = self;
+               if(t.enemy == world)
                {
-                       self.enemy = starte;            // make the chain a loop
-
-               // shootable, or triggered doors just needed the owner/enemy links,
-               // they don't spawn a field
-
-                       self = self.owner;
+                       t.enemy = self;
+                       break;
+               }
+       }
+       dprint("\n");
 
-                       if (self.health)
-                               return;
-                       IFTARGETED
-                               return;
-                       if (self.items)
-                               return;
+       // collect health, targetname, message, size
+       cmins = self.absmin;
+       cmaxs = self.absmax;
+       for(t = self; ; t = t.enemy)
+       {
+               if(t.health && !self.health)
+                       self.health = t.health;
+               if(t.targetname && !self.targetname)
+                       self.targetname = t.targetname;
+               if(t.message != "" && self.message == "")
+                       self.message = t.message;
+               if (t.absmin_x < cmins_x)
+                       cmins_x = t.absmin_x;
+               if (t.absmin_y < cmins_y)
+                       cmins_y = t.absmin_y;
+               if (t.absmin_z < cmins_z)
+                       cmins_z = t.absmin_z;
+               if (t.absmax_x > cmaxs_x)
+                       cmaxs_x = t.absmax_x;
+               if (t.absmax_y > cmaxs_y)
+                       cmaxs_y = t.absmax_y;
+               if (t.absmax_z > cmaxs_z)
+                       cmaxs_z = t.absmax_z;
+               if(t.enemy == self)
+                       break;
+       }
 
-                       self.owner.trigger_field = spawn_field(cmins, cmaxs);
+       // distribute health, targetname, message
+       for(t = self; t; t = t.enemy)
+       {
+               t.health = self.health;
+               t.targetname = self.targetname;
+               t.message = self.message;
+               if(t.enemy == self)
+                       break;
+       }
 
-                       return;
-               }
+       // shootable, or triggered doors just needed the owner/enemy links,
+       // they don't spawn a field
 
-               if (EntitiesTouching(self,t))
-               {
-                       if (t.enemy)
-                               objerror ("cross connected doors");
-
-                       self.enemy = t;
-                       self = t;
-
-                       if (t.absmin_x < cmins_x)
-                               cmins_x = t.absmin_x;
-                       if (t.absmin_y < cmins_y)
-                               cmins_y = t.absmin_y;
-                       if (t.absmin_z < cmins_z)
-                               cmins_z = t.absmin_z;
-                       if (t.absmax_x > cmaxs_x)
-                               cmaxs_x = t.absmax_x;
-                       if (t.absmax_y > cmaxs_y)
-                               cmaxs_y = t.absmax_y;
-                       if (t.absmax_z > cmaxs_z)
-                               cmaxs_z = t.absmax_z;
-               }
-       } while (1 );
+       if (self.health)
+               return;
+       IFTARGETED
+               return;
+       if (self.items)
+               return;
 
+       self.trigger_field = spawn_field(cmins, cmaxs);
 }
 
 
index 82b5f4457a9b8c5aeea05b5e185085a72fb5160b..7f8cb82921ee3ae2a3b068ac331bbff891a13949 100644 (file)
@@ -122,12 +122,8 @@ void spawnfunc_target_give()
 //void spawnfunc_item_health_mega()  /* handled in t_items.qc */
 //void spawnfunc_item_invis()        /* not supported */
 //void spawnfunc_item_regen()        /* not supported */
-void spawnfunc_team_CTF_redflag()    { spawnfunc_item_flag_team1();    }
-void spawnfunc_team_CTF_blueflag()   { spawnfunc_item_flag_team2();    }
-void spawnfunc_team_CTF_redplayer()  { spawnfunc_info_player_team1();  }
-void spawnfunc_team_CTF_blueplayer() { spawnfunc_info_player_team2();  }
-void spawnfunc_team_CTF_redspawn()   { spawnfunc_info_player_team1();  }
-void spawnfunc_team_CTF_bluespawn()  { spawnfunc_info_player_team2();  }
+
+// CTF spawnfuncs handled in mutators/gamemode_ctf.qc now
 
 void spawnfunc_item_flight()         { spawnfunc_item_jetpack();       }
 
index 1705d8f8ea705edc6f71d4c5490bb32cd5d40c76..2cfdd8a7720f5bf665d22ed5d7916da177375887 100644 (file)
@@ -75,6 +75,11 @@ void spawn_tdeath(vector v0, entity e, vector v)
 #define TELEPORT_FLAGS_WARPZONE   0
 #define TELEPORT_FLAGS_PORTAL     (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH | TELEPORT_FLAG_FORCE_TDEATH)
 #define TELEPORT_FLAGS_TELEPORTER (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH)
+
+// types for .teleportable entity setting
+#define TELEPORT_NORMAL 1 // play sounds/effects etc
+#define TELEPORT_SIMPLE 2 // only do teleport, nothing special
+
 void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags)
 {
        entity telefragger;
@@ -87,7 +92,7 @@ void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angle
 
        makevectors (to_angles);
 
-       if(player.classname == "player") // don't play sounds or show particles for anything that isn't a player, maybe change later to block only observers
+       if(player.teleportable == TELEPORT_NORMAL) // don't play sounds or show particles for anything that isn't a player, maybe change later to block only observers
        {
                if(self.pushltime < time) // only show one teleport effect per teleporter per 0.2 seconds, for better fps
                {
@@ -131,10 +136,12 @@ void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angle
                {
                        player.pusher = teleporter.owner;
                        player.pushltime = time + autocvar_g_maxpushtime;
+                       player.istypefrag = player.BUTTON_CHAT;
                }
                else
                {
                        player.pushltime = 0;
+                       player.istypefrag = 0;
                }
 
                player.lastteleporttime = time;
@@ -195,20 +202,17 @@ void Teleport_Touch (void)
        if (self.active != ACTIVE_ACTIVE)
                return;
        
-       if not(other.iscreature)
-               return;
-
-       // for gameplay: vehicles can't teleport
-       if (other.vehicle_flags & VHF_ISVEHICLE)
+       if not(other.teleportable)
                return;
     
-    if(other.vehicle)
-        return;
-        
-    if(other.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
-        return;
+       if(other.vehicle)
+       if(!other.vehicle.teleportable)
+               return;
+                       
+       if(other.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
+               return;
         
-       if (other.deadflag != DEAD_NO)
+       if(other.deadflag != DEAD_NO)
                return;
 
        if(self.team)
index e04e2cad88f5cb33bd2639ff3f56df2593ccbdbf..ebb89aa5c9c0bf274742139a99d56f3ecf2eacb2 100644 (file)
@@ -22,7 +22,7 @@ void target_spawn_helper_setsize()
        setsize(self, self.mins, self.maxs);
 }
 
-void target_spawn_useon(entity e)
+void target_spawn_edit_entity(entity e, string msg, entity kt, entity t2, entity t3, entity t4, entity act)
 {
        float i, n, valuefieldpos;
        string key, value, valuefield, valueoffset, valueoffsetrandom;
@@ -30,15 +30,8 @@ void target_spawn_useon(entity e)
        vector data, data2;
        entity oldself;
        entity oldactivator;
-       entity kt, t2, t3, t4;
-
-       n = tokenize_console(self.message);
-       self.target_spawn_activator = activator;
 
-       kt = find(world, targetname, self.killtarget);
-       t2 = find(world, targetname, self.target2);
-       t3 = find(world, targetname, self.target3);
-       t4 = find(world, targetname, self.target4);
+       n = tokenize_console(msg);
 
        for(i = 0; i < n-1; i += 2)
        {
@@ -101,7 +94,7 @@ void target_spawn_useon(entity e)
                                }
                                else if(value == "activator")
                                {
-                                       valueent = activator;
+                                       valueent = act;
                                        value = "";
                                }
                                else if(value == "other")
@@ -111,8 +104,8 @@ void target_spawn_useon(entity e)
                                }
                                else if(value == "pusher")
                                {
-                                       if(time < activator.pushltime)
-                                               valueent = activator.pusher;
+                                       if(time < act.pushltime)
+                                               valueent = act.pusher;
                                        else
                                                valueent = world;
                                        value = "";
@@ -221,7 +214,7 @@ void target_spawn_useon(entity e)
                        oldactivator = activator;
 
                        self = e;
-                       activator = oldself.target_spawn_activator;
+                       activator = act;
 
                        self.target_spawn_spawnfunc();
 
@@ -237,6 +230,20 @@ void target_spawn_useon(entity e)
        }
 }
 
+void target_spawn_useon(entity e)
+{
+       self.target_spawn_activator = activator;
+       target_spawn_edit_entity(
+               e,
+               self.message,
+               find(world, targetname, self.killtarget),
+               find(world, targetname, self.target2),
+               find(world, targetname, self.target3),
+               find(world, targetname, self.target4),
+               activator
+       );
+}
+
 float target_spawn_cancreate()
 {
        float c;
index db01a8f5887d76f1438bc357c5dddd05570ba389..b8f2f3ac8746ea4e02daae0936833d2086954699 100644 (file)
@@ -6,22 +6,7 @@ float c1, c2, c3, c4;
 // # of bots on those teams
 float cb1, cb2, cb3, cb4;
 
-float audit_teams_time;
-
-float IsTeamBalanceForced()
-{
-       if(intermission_running)
-               return 0; // no rebalancing whatsoever please
-       if(!teamplay)
-               return 0;
-       if(autocvar_g_campaign)
-               return 0;
-       if(autocvar_bot_vs_human && (c3==-1 && c4==-1))
-               return 0;
-       if(!autocvar_g_balance_teams_force)
-               return -1;
-       return 1;
-}
+//float audit_teams_time;
 
 void TeamchangeFrags(entity e)
 {
@@ -68,7 +53,6 @@ string TeamNoName(float t)
 }
 
 void dom_init();
-void ctf_init();
 void runematch_init();
 void tdm_init();
 void entcs_init();
@@ -157,10 +141,9 @@ void InitGameplayMode()
        if(g_ctf)
        {
                ActivateTeamplay();
-               g_ctf_ignore_frags = autocvar_g_ctf_ignore_frags;
                fraglimit_override = autocvar_capturelimit_override;
                leadlimit_override = autocvar_captureleadlimit_override;
-               ctf_init();
+               MUTATOR_ADD(gamemode_ctf);
                have_team_spawns = -1; // request team spawns
        }
 
@@ -228,6 +211,7 @@ void InitGameplayMode()
        {
                ActivateTeamplay();
                have_team_spawns = -1; // request team spawns
+               MUTATOR_ADD(gamemode_onslaught);
        }
 
        if(g_race)
@@ -562,8 +546,6 @@ void CheckAllowedTeams (entity for_whom)
 
 float PlayerValue(entity p)
 {
-       if(IsTeamBalanceForced() == 1)
-               return 1;
        return 1;
        // FIXME: it always returns 1...
 }
@@ -643,11 +625,73 @@ void GetTeamCounts(entity ignore)
        }
 }
 
+float TeamSmallerEqThanTeam(float ta, float tb, entity e)
+{
+       // we assume that CheckAllowedTeams and GetTeamCounts have already been called
+       float f;
+       float ca = -1, cb = -1, cba = 0, cbb = 0, sa = 0, sb = 0;
+
+       switch(ta)
+       {
+               case 1: ca = c1; cba = cb1; sa = team1_score; break;
+               case 2: ca = c2; cba = cb2; sa = team2_score; break;
+               case 3: ca = c3; cba = cb3; sa = team3_score; break;
+               case 4: ca = c4; cba = cb4; sa = team4_score; break;
+       }
+       switch(tb)
+       {
+               case 1: cb = c1; cbb = cb1; sb = team1_score; break;
+               case 2: cb = c2; cbb = cb2; sb = team2_score; break;
+               case 3: cb = c3; cbb = cb3; sb = team3_score; break;
+               case 4: cb = c4; cbb = cb4; sb = team4_score; break;
+       }
+
+       // invalid
+       if(ca < 0 || cb < 0)
+               return FALSE;
+
+       // equal
+       if(ta == tb)
+               return TRUE;
+
+       if(clienttype(e) == CLIENTTYPE_REAL)
+       {
+               if(bots_would_leave)
+               {
+                       ca -= cba * 0.999;
+                       cb -= cbb * 0.999;
+               }
+       }
+       
+       // keep teams alive (teams of size 0 always count as smaller, ignoring score)
+       if(ca < 1)
+               if(cb >= 1)
+                       return TRUE;
+       if(ca >= 1)
+               if(cb < 1)
+                       return FALSE;
+
+       // first, normalize
+       f = max(ca, cb, 1);
+       ca /= f;
+       cb /= f;
+       f = max(sa, sb, 1);
+       sa /= f;
+       sb /= f;
+
+       // the more we're at the end of the match, the more take scores into account
+       f = bound(0, game_completion_ratio * autocvar_g_balance_teams_scorefactor, 1);
+       ca += (sa - ca) * f;
+       cb += (sb - cb) * f;
+
+       return ca <= cb;
+}
+
 // returns # of smallest team (1, 2, 3, 4)
 // NOTE: Assumes CheckAllowedTeams has already been called!
 float FindSmallestTeam(entity pl, float ignore_pl)
 {
-       float totalteams, balance_type, maxc;
+       float totalteams, t;
        totalteams = 0;
 
        // find out what teams are available
@@ -688,49 +732,26 @@ float FindSmallestTeam(entity pl, float ignore_pl)
        else
                GetTeamCounts(world);
 
-       // c1...c4 now have counts of each team
-       // figure out which is smallest, giving priority to the team the player is already on as a tie-breaker
-
-       // 2 gives priority to what team you're already on, 1 goes in order
-       // 2 doesn't seem to work though...
-       balance_type = 1;
-
-       if(bots_would_leave)
-       //if(pl.classname != "player")
-       if(clienttype(pl) != CLIENTTYPE_BOT)
-       {
-               c1 -= cb1 * 255.0/256.0;
-               c2 -= cb2 * 255.0/256.0;
-               c3 -= cb3 * 255.0/256.0;
-               c4 -= cb4 * 255.0/256.0;
-       }
-       maxc = max4(c1, c2, c3, c4);
-
        RandomSelection_Init();
-       if(balance_type == 1)
-       {
-               // 1: use team count, then score (note: can only use 8 significant bits of score)
-               if(c1 >= 0) RandomSelection_Add(world, 1, string_null, 1, (maxc - c1) + float2range01(-team1_score) / 256.0);
-               if(c2 >= 0) RandomSelection_Add(world, 2, string_null, 1, (maxc - c2) + float2range01(-team2_score) / 256.0);
-               if(c3 >= 0) RandomSelection_Add(world, 3, string_null, 1, (maxc - c3) + float2range01(-team3_score) / 256.0);
-               if(c4 >= 0) RandomSelection_Add(world, 4, string_null, 1, (maxc - c4) + float2range01(-team4_score) / 256.0);
-       }
-       else if(balance_type == 2)
-       {
-               // 1: use team count, if equal prefer own team
-               if(c1 >= 0) RandomSelection_Add(world, 1, string_null, 1, (maxc - c1) + (self.team == COLOR_TEAM1) / 512.0);
-               if(c2 >= 0) RandomSelection_Add(world, 2, string_null, 1, (maxc - c1) + (self.team == COLOR_TEAM2) / 512.0);
-               if(c3 >= 0) RandomSelection_Add(world, 3, string_null, 1, (maxc - c1) + (self.team == COLOR_TEAM3) / 512.0);
-               if(c4 >= 0) RandomSelection_Add(world, 4, string_null, 1, (maxc - c1) + (self.team == COLOR_TEAM4) / 512.0);
-       }
-       else if(balance_type == 3)
-       {
-               // 1: use team count, then score, if equal prefer own team (probably fails due to float accuracy problems)
-               if(c1 >= 0) RandomSelection_Add(world, 1, string_null, 1, (maxc - c1) + float2range01(-team1_score + 0.5 * (self.team == COLOR_TEAM1)) / 256.0);
-               if(c2 >= 0) RandomSelection_Add(world, 2, string_null, 1, (maxc - c2) + float2range01(-team2_score + 0.5 * (self.team == COLOR_TEAM2)) / 256.0);
-               if(c3 >= 0) RandomSelection_Add(world, 3, string_null, 1, (maxc - c3) + float2range01(-team3_score + 0.5 * (self.team == COLOR_TEAM3)) / 256.0);
-               if(c4 >= 0) RandomSelection_Add(world, 4, string_null, 1, (maxc - c4) + float2range01(-team4_score + 0.5 * (self.team == COLOR_TEAM4)) / 256.0);
-       }
+       
+       t = 1;
+       if(TeamSmallerEqThanTeam(2, t, pl))
+               t = 2;
+       if(TeamSmallerEqThanTeam(3, t, pl))
+               t = 3;
+       if(TeamSmallerEqThanTeam(4, t, pl))
+               t = 4;
+
+       // now t is the minimum, or A minimum!
+       if(t == 1 || TeamSmallerEqThanTeam(1, t, pl))
+               RandomSelection_Add(world, 1, string_null, 1, 1);
+       if(t == 2 || TeamSmallerEqThanTeam(2, t, pl))
+               RandomSelection_Add(world, 2, string_null, 1, 1);
+       if(t == 3 || TeamSmallerEqThanTeam(3, t, pl))
+               RandomSelection_Add(world, 3, string_null, 1, 1);
+       if(t == 4 || TeamSmallerEqThanTeam(4, t, pl))
+               RandomSelection_Add(world, 4, string_null, 1, 1);
+
        return RandomSelection_chosen_float;
 }
 
@@ -813,7 +834,7 @@ float JoinBestTeam(entity pl, float only_return_best, float forcebestteam)
 //void() ctf_playerchanged;
 void SV_ChangeTeam(float _color)
 {
-       float scolor, dcolor, steam, dteam, dbotcount, scount, dcount;
+       float scolor, dcolor, steam, dteam; //, dbotcount, scount, dcount;
 
        // in normal deathmatch we can just apply the color and we're done
        if(!teamplay) {
@@ -861,62 +882,15 @@ void SV_ChangeTeam(float _color)
                return; // changing teams is not allowed
        }
 
-       if(autocvar_g_balance_teams_prevent_imbalance)
+       // autocvar_g_balance_teams_prevent_imbalance only makes sense if autocvar_g_balance_teams is on, as it makes the team selection dialog pointless
+       if(autocvar_g_balance_teams && autocvar_g_balance_teams_prevent_imbalance)
        {
-               // only allow changing to a smaller or equal size team
-
-               // find out what teams are available
-               //CheckAllowedTeams();
-               // count how many players on each team
-               GetTeamCounts(world);
-
-               // get desired team
-               if(dteam == 1 && c1 >= 0)//dcolor == COLOR_TEAM1 - 1)
-               {
-                       dcount = c1;
-                       dbotcount = cb1;
-               }
-               else if(dteam == 2 && c2 >= 0)//dcolor == COLOR_TEAM2 - 1)
+               GetTeamCounts(self);
+               if(!TeamSmallerEqThanTeam(dteam, steam, self))
                {
-                       dcount = c2;
-                       dbotcount = cb2;
-               }
-               else if(dteam == 3 && c3 >= 0)//dcolor == COLOR_TEAM3 - 1)
-               {
-                       dcount = c3;
-                       dbotcount = cb3;
-               }
-               else if(dteam == 4 && c4 >= 0)//dcolor == COLOR_TEAM4 - 1)
-               {
-                       dcount = c4;
-                       dbotcount = cb4;
-               }
-               else
-               {
-                       sprint(self, "Cannot change to an invalid team\n");
-
+                       sprint(self, "Cannot change to a larger/better/shinier team\n");
                        return;
                }
-
-               // get starting team
-               if(steam == 1)//scolor == COLOR_TEAM1 - 1)
-                       scount = c1;
-               else if(steam == 2)//scolor == COLOR_TEAM2 - 1)
-                       scount = c2;
-               else if(steam == 3)//scolor == COLOR_TEAM3 - 1)
-                       scount = c3;
-               else // if(steam == 4)//scolor == COLOR_TEAM4 - 1)
-                       scount = c4;
-
-               if(scount) // started at a valid, nonempty team
-               {
-                       // check if we're trying to change to a larger team that doens't have bots to swap with
-                       if(dcount >= scount && dbotcount <= 0)
-                       {
-                               sprint(self, "Cannot change to a larger team\n");
-                               return; // can't change to a larger team
-                       }
-               }
        }
 
 //     bprint("allow change teams from ", ftos(steam), " to ", ftos(dteam), "\n");
@@ -935,7 +909,6 @@ void SV_ChangeTeam(float _color)
                if(self.deadflag == DEAD_NO)
                        Damage(self, self, self, 100000, DEATH_TEAMCHANGE, self.origin, '0 0 0');
        }
-       //ctf_playerchanged();
 }
 
 void ShufflePlayerOutOfTeam (float source_team)
@@ -1079,87 +1052,6 @@ void ShufflePlayerOutOfTeam (float source_team)
        centerprint(selected, strcat("You have been moved into a different team to improve team balance\nYou are now on: ", ColoredTeamName(selected.team)));
 }
 
-void CauseRebalance(float source_team, float howmany_toomany)
-{
-       if(IsTeamBalanceForced() == 1)
-       {
-               bprint("Rebalancing Teams\n");
-               ShufflePlayerOutOfTeam(source_team);
-       }
-}
-
-// part of g_balance_teams_force
-// occasionally perform an audit of the teams to make
-// sure they're more or less balanced in player count.
-void AuditTeams()
-{
-       float numplayers, numteams, smallest, toomany;
-       float balance;
-       balance = IsTeamBalanceForced();
-       if(balance == 0)
-               return;
-
-       if(audit_teams_time > time)
-               return;
-
-       audit_teams_time = time + 4 + random();
-
-//     bprint("Auditing teams\n");
-
-       CheckAllowedTeams(world);
-       GetTeamCounts(world);
-
-
-       numteams = numplayers = smallest = 0;
-       if(c1 >= 0)
-       {
-               numteams = numteams + 1;
-               numplayers = numplayers + c1;
-               smallest = c1;
-       }
-       if(c2 >= 0)
-       {
-               numteams = numteams + 1;
-               numplayers = numplayers + c2;
-               if(c2 < smallest)
-                       smallest = c2;
-       }
-       if(c3 >= 0)
-       {
-               numteams = numteams + 1;
-               numplayers = numplayers + c3;
-               if(c3 < smallest)
-                       smallest = c3;
-       }
-       if(c4 >= 0)
-       {
-               numteams = numteams + 1;
-               numplayers = numplayers + c4;
-               if(c4 < smallest)
-                       smallest = c4;
-       }
-
-       if(numplayers <= 0)
-               return; // no players to move around
-       if(numteams < 2)
-               return; // don't bother shuffling if for some reason there aren't any teams
-
-       toomany = smallest + 1;
-
-       if(c1 && c1 > toomany)
-               CauseRebalance(1, c1 - toomany);
-       if(c2 && c2 > toomany)
-               CauseRebalance(2, c2 - toomany);
-       if(c3 && c3 > toomany)
-               CauseRebalance(3, c3 - toomany);
-       if(c4 && c4 > toomany)
-               CauseRebalance(4, c4 - toomany);
-
-       // if teams are still unbalanced, balance them further in the next audit,
-       // which will happen sooner (keep doing rapid audits until things are in order)
-       audit_teams_time = time + 0.7 + random()*0.3;
-}
-
 // code from here on is just to support maps that don't have team entities
 void tdm_spawnteam (string teamname, float teamcolor)
 {
index 767c346620547b0dd565f58f093f46b675b1f8f8..31b984e391767845be993b5faecb3bc4779f1ae5 100644 (file)
@@ -255,6 +255,7 @@ void turret_ewheel_dinit()
     self.target_select_flags   = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
     self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
     self.iscreature = TRUE;
+    self.teleportable = TELEPORT_NORMAL;
     self.damagedbycontents = TRUE;
     self.movetype   = MOVETYPE_WALK;
     self.solid      = SOLID_SLIDEBOX;
index f47003f4bb19d0d581ed529e8d4c71246dba2a28..599eb5776968117caabdc4eed6b1b1b9610a23a0 100644 (file)
@@ -593,6 +593,7 @@ void turret_walker_dinit()
     self.target_select_flags   = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
     self.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_RANGELIMTS | TFL_TARGETSELECT_TEAMCHECK | TFL_TARGETSELECT_LOS;
     self.iscreature = TRUE;
+    self.teleportable = TELEPORT_NORMAL;
     self.damagedbycontents = TRUE;
     self.movetype   = MOVETYPE_WALK;
     self.solid      = SOLID_SLIDEBOX;
diff --git a/qcsrc/server/vehicles/bumblebee.qc b/qcsrc/server/vehicles/bumblebee.qc
new file mode 100644 (file)
index 0000000..4b0f1d1
--- /dev/null
@@ -0,0 +1,1092 @@
+#define BRG_SETUP 2
+#define BRG_START 4
+#define BRG_END 8
+
+#ifdef SVQC
+// Auto cvars
+float autocvar_g_vehicle_bumblebee_speed_forward;
+float autocvar_g_vehicle_bumblebee_speed_strafe;
+float autocvar_g_vehicle_bumblebee_speed_up;
+float autocvar_g_vehicle_bumblebee_speed_down;
+float autocvar_g_vehicle_bumblebee_turnspeed;
+float autocvar_g_vehicle_bumblebee_pitchspeed;
+float autocvar_g_vehicle_bumblebee_pitchlimit;
+float autocvar_g_vehicle_bumblebee_friction;
+
+float autocvar_g_vehicle_bumblebee_energy;
+float autocvar_g_vehicle_bumblebee_energy_regen;
+float autocvar_g_vehicle_bumblebee_energy_regen_pause;
+
+float autocvar_g_vehicle_bumblebee_health;
+float autocvar_g_vehicle_bumblebee_health_regen;
+float autocvar_g_vehicle_bumblebee_health_regen_pause;
+
+float autocvar_g_vehicle_bumblebee_shield;
+float autocvar_g_vehicle_bumblebee_shield_regen;
+float autocvar_g_vehicle_bumblebee_shield_regen_pause;
+
+float autocvar_g_vehicle_bumblebee_cannon_cost;
+float autocvar_g_vehicle_bumblebee_cannon_damage;
+float autocvar_g_vehicle_bumblebee_cannon_radius;
+float autocvar_g_vehicle_bumblebee_cannon_refire;
+float autocvar_g_vehicle_bumblebee_cannon_speed;
+float autocvar_g_vehicle_bumblebee_cannon_spread;
+float autocvar_g_vehicle_bumblebee_cannon_force;
+
+float autocvar_g_vehicle_bumblebee_cannon_ammo;
+float autocvar_g_vehicle_bumblebee_cannon_ammo_regen;
+float autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause;
+
+var float autocvar_g_vehicle_bumblebee_cannon_lock = 0;
+
+float autocvar_g_vehicle_bumblebee_cannon_turnspeed;
+float autocvar_g_vehicle_bumblebee_cannon_pitchlimit_down;
+float autocvar_g_vehicle_bumblebee_cannon_pitchlimit_up;
+float autocvar_g_vehicle_bumblebee_cannon_turnlimit_in;
+float autocvar_g_vehicle_bumblebee_cannon_turnlimit_out;
+
+
+float autocvar_g_vehicle_bumblebee_raygun_turnspeed;
+float autocvar_g_vehicle_bumblebee_raygun_pitchlimit_down;
+float autocvar_g_vehicle_bumblebee_raygun_pitchlimit_up;
+float autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides;
+
+float autocvar_g_vehicle_bumblebee_raygun_range;
+float autocvar_g_vehicle_bumblebee_raygun_dps;
+float autocvar_g_vehicle_bumblebee_raygun_aps;
+float autocvar_g_vehicle_bumblebee_raygun_fps;
+
+float autocvar_g_vehicle_bumblebee_raygun;
+float autocvar_g_vehicle_bumblebee_healgun_hps;
+float autocvar_g_vehicle_bumblebee_healgun_hmax;
+float autocvar_g_vehicle_bumblebee_healgun_aps;
+float autocvar_g_vehicle_bumblebee_healgun_amax;
+float autocvar_g_vehicle_bumblebee_healgun_sps;
+float autocvar_g_vehicle_bumblebee_healgun_locktime;
+
+float autocvar_g_vehicle_bumblebee_respawntime;
+
+float autocvar_g_vehicle_bumblebee_blowup_radius;
+float autocvar_g_vehicle_bumblebee_blowup_coredamage;
+float autocvar_g_vehicle_bumblebee_blowup_edgedamage;
+float autocvar_g_vehicle_bumblebee_blowup_forceintensity;
+var vector autocvar_g_vehicle_bumblebee_bouncepain;
+
+var float autocvar_g_vehicle_bumblebee = 0;
+
+
+float bumble_raygun_send(entity to, float sf);
+
+#define BUMB_MIN '-130 -130 -130'
+#define BUMB_MAX '130 130 130'
+
+void bumb_fire_cannon(entity _gun, string _tagname, entity _owner)
+{
+       vector v = gettaginfo(_gun, gettagindex(_gun, _tagname));
+       vehicles_projectile("bigplasma_muzzleflash", "weapons/flacexp3.wav",
+                                               v, normalize(v_forward + randomvec() * autocvar_g_vehicle_bumblebee_cannon_spread) * autocvar_g_vehicle_bumblebee_cannon_speed,
+                                               autocvar_g_vehicle_bumblebee_cannon_damage, autocvar_g_vehicle_bumblebee_cannon_radius, autocvar_g_vehicle_bumblebee_cannon_force,  0,
+                                               DEATH_BUMB_GUN, PROJECTILE_BUMBLE_GUN, 0, TRUE, TRUE, _owner);
+}
+
+float bumb_gunner_frame()
+{
+       entity vehic    = self.vehicle.owner;
+       entity gun      = self.vehicle;
+       entity gunner   = self;
+       self = vehic;
+
+
+       
+       
+       vehic.solid = SOLID_NOT;
+       //setorigin(gunner, vehic.origin);
+       gunner.velocity = vehic.velocity;
+       
+       float _in, _out;
+       vehic.angles_x *= -1;
+       makevectors(vehic.angles);
+       vehic.angles_x *= -1;
+       if((gun == vehic.gun1))
+       {
+               _in = autocvar_g_vehicle_bumblebee_cannon_turnlimit_in;
+               _out = autocvar_g_vehicle_bumblebee_cannon_turnlimit_out;
+               setorigin(gunner, vehic.origin + v_up * -16 + v_forward * -16 + v_right * 128);
+       }
+       else
+       {
+               _in = autocvar_g_vehicle_bumblebee_cannon_turnlimit_out;
+               _out = autocvar_g_vehicle_bumblebee_cannon_turnlimit_in;
+               setorigin(gunner, vehic.origin + v_up * -16 + v_forward * -16 + v_right * -128);                
+       }
+       
+       crosshair_trace(gunner);
+       vector _ct = trace_endpos;
+       vector ad;
+
+       if(autocvar_g_vehicle_bumblebee_cannon_lock)
+       {
+               if(gun.lock_time < time)
+                       gun.enemy = world;
+
+               if(trace_ent)
+                       if(trace_ent.movetype)
+                               if(trace_ent.takedamage)
+                                       if(!trace_ent.deadflag)
+                                       {
+                                               if(teamplay)
+                                               {
+                                                       if(trace_ent.team != gunner.team)
+                                                       {
+                                                               gun.enemy = trace_ent;
+                                                               gun.lock_time = time + 5;
+                                                       }
+                                               }
+                                               else
+                                               {
+                                                       gun.enemy = trace_ent;
+                                                       gun.lock_time = time + 5;
+                                               }
+                                       }
+       }
+
+       if(gun.enemy)
+       {
+               float i, distance, impact_time;
+
+               vector vf = real_origin(gun.enemy);
+               vector _vel = gun.enemy.velocity;
+               if(gun.enemy.movetype == MOVETYPE_WALK)
+                       _vel_z *= 0.1;
+
+
+               ad = vf;
+               for(i = 0; i < 4; ++i)
+               {
+                       distance = vlen(ad - gunner.origin);
+                       impact_time = distance / autocvar_g_vehicle_bumblebee_cannon_speed;
+                       ad = vf + _vel * impact_time;
+               }
+               trace_endpos = ad;
+
+
+               UpdateAuxiliaryXhair(gunner, ad, '1 0 1', 1);
+               vehicle_aimturret(vehic, trace_endpos, gun, "fire",
+                                                 autocvar_g_vehicle_bumblebee_cannon_pitchlimit_down * -1, autocvar_g_vehicle_bumblebee_cannon_pitchlimit_up,
+                                                 _out * -1,  _in,  autocvar_g_vehicle_bumblebee_cannon_turnspeed);
+
+       }
+       else
+               vehicle_aimturret(vehic, _ct, gun, "fire",
+                                                 autocvar_g_vehicle_bumblebee_cannon_pitchlimit_down * -1, autocvar_g_vehicle_bumblebee_cannon_pitchlimit_up,
+                                                 _out * -1,  _in,  autocvar_g_vehicle_bumblebee_cannon_turnspeed);
+
+       if(gunner.BUTTON_ATCK)
+               if(time > gun.attack_finished_single)
+                       if(gun.vehicle_energy >= autocvar_g_vehicle_bumblebee_cannon_cost)
+                       {
+                               gun.vehicle_energy -= autocvar_g_vehicle_bumblebee_cannon_cost;
+                               bumb_fire_cannon(gun, "fire", gunner);
+                               gun.delay = time;
+                               gun.attack_finished_single = time + autocvar_g_vehicle_bumblebee_cannon_refire;
+                       }
+
+       VEHICLE_UPDATE_PLAYER(gunner, health, bumblebee);
+
+       if(vehic.vehicle_flags & VHF_HASSHIELD)
+               VEHICLE_UPDATE_PLAYER(gunner, shield, bumblebee);
+
+       ad = gettaginfo(gun, gettagindex(gun, "fire"));
+       traceline(ad, ad + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, gun);
+
+       UpdateAuxiliaryXhair(gunner, trace_endpos, ('1 0 0' * gunner.vehicle_reload1) + ('0 1 0' *(1 - gunner.vehicle_reload1)), 0);
+
+       if(vehic.owner)
+               UpdateAuxiliaryXhair(vehic.owner, trace_endpos, ('1 0 0' * gunner.vehicle_reload1) + ('0 1 0' *(1 - gunner.vehicle_reload1)), ((gunner == vehic.gunner1) ? 1 : 2));
+
+       vehic.solid = SOLID_BBOX;
+       gunner.BUTTON_ATCK = gunner.BUTTON_ATCK2 = gunner.BUTTON_CROUCH = 0;
+       gunner.vehicle_energy = (gun.vehicle_energy / autocvar_g_vehicle_bumblebee_cannon_ammo) * 100;
+
+       self = gunner;
+       return 1;
+}
+
+void bumb_gunner_exit(float _exitflag)
+{
+
+
+       if(clienttype(self) == CLIENTTYPE_REAL)
+       {
+               msg_entity = self;
+               WriteByte(MSG_ONE, SVC_SETVIEWPORT);
+               WriteEntity(MSG_ONE, self);
+
+               WriteByte(MSG_ONE, SVC_SETVIEWANGLES);
+               WriteAngle(MSG_ONE, 0);
+               WriteAngle(MSG_ONE, self.vehicle.angles_y);
+               WriteAngle(MSG_ONE, 0);
+       }
+       
+       CSQCVehicleSetup(self, HUD_NORMAL);
+       setsize(self, PL_MIN, PL_MAX);
+
+       self.takedamage     = DAMAGE_AIM;
+       self.solid          = SOLID_SLIDEBOX;
+       self.movetype       = MOVETYPE_WALK;
+       self.effects        &~= EF_NODRAW;
+       self.alpha          = 1;
+       self.PlayerPhysplug = SUB_Null;
+       self.view_ofs       = PL_VIEW_OFS;
+       self.event_damage   = PlayerDamage;
+       self.hud            = HUD_NORMAL;
+       self.switchweapon   = self.vehicle.switchweapon;
+
+    vh_player = self;
+    vh_vehicle = self.vehicle;
+    MUTATOR_CALLHOOK(VehicleExit);
+    self = vh_player;
+    self.vehicle = vh_vehicle;
+
+       self.vehicle.vehicle_hudmodel.viewmodelforclient = self.vehicle;
+
+       fixedmakevectors(self.vehicle.owner.angles);
+
+       if(self == self.vehicle.owner.gunner1)
+       {
+               self.vehicle.owner.gunner1 = world;             
+       }
+       else if(self == self.vehicle.owner.gunner2)
+       {
+               self.vehicle.owner.gunner2 = world;     
+               v_right *= -1;
+       }       
+       else
+               dprint("^1self != gunner1 or gunner2, this is a BIG PROBLEM, tell tZork this happend.\n");
+               
+       vector spot = self.vehicle.owner.origin + + v_up * 128 + v_right * 300;
+       spot = vehicles_findgoodexit(spot);
+       //setorigin(self , spot);
+
+       self.velocity = 0.75 * self.vehicle.owner.velocity + normalize(spot - self.vehicle.owner.origin) * 200;
+       self.velocity_z += 10;
+
+       self.vehicle.phase = time + 5;
+       self.vehicle        = world;
+}
+
+float bumb_gunner_enter()
+{
+       RemoveGrapplingHook(other);
+       entity _gun, _gunner;
+       if(!self.gunner1)
+       {
+               _gun = self.gun1;
+               _gunner = self.gunner1;
+               self.gunner1 = other;
+       }
+       else if(!self.gunner2)
+       {
+               _gun = self.gun2;
+               _gunner = self.gunner2;
+               self.gunner2 = other;
+       }
+       else
+       {
+               dprint("^1ERROR:^7Tried to enter a fully occupied vehicle!\n");
+               return FALSE;
+       }
+
+       _gunner            = other;
+       _gunner.vehicle    = _gun;
+       _gun.switchweapon  = other.switchweapon;
+       _gun.vehicle_exit  = bumb_gunner_exit;
+
+       other.angles            = self.angles;
+       other.takedamage        = DAMAGE_NO;
+       other.solid             = SOLID_NOT;
+       other.movetype          = MOVETYPE_NOCLIP;
+       other.alpha             = -1;
+       other.event_damage      = SUB_Null;
+       other.view_ofs          = '0 0 0';
+       other.hud               = _gun.hud;
+       other.PlayerPhysplug    = _gun.PlayerPhysplug;
+       other.vehicle_ammo1     = self.vehicle_ammo1;
+       other.vehicle_ammo2     = self.vehicle_ammo2;
+       other.vehicle_reload1   = self.vehicle_reload1;
+       other.vehicle_reload2   = self.vehicle_reload2;
+       other.vehicle_energy    = self.vehicle_energy;
+       other.PlayerPhysplug    = bumb_gunner_frame;
+       other.flags             &~= FL_ONGROUND;
+
+       msg_entity = other;
+       WriteByte(MSG_ONE, SVC_SETVIEWPORT);
+       WriteEntity(MSG_ONE, _gun.vehicle_viewport);
+       WriteByte(MSG_ONE, SVC_SETVIEWANGLES);
+       WriteAngle(MSG_ONE, _gun.angles_x + self.angles_x);    // tilt
+       WriteAngle(MSG_ONE, _gun.angles_y + self.angles_y);    // yaw
+       WriteAngle(MSG_ONE, 0);                             // roll
+       _gun.vehicle_hudmodel.viewmodelforclient = other;
+
+       CSQCVehicleSetup(other, other.hud);
+       
+    vh_player = other;
+    vh_vehicle = _gun;
+    MUTATOR_CALLHOOK(VehicleEnter);
+    other = vh_player;
+    _gun = vh_vehicle;
+
+       return TRUE;
+}
+
+float vehicles_valid_pilot()
+{
+       if(other.classname != "player")
+               return FALSE;
+
+       if(other.deadflag != DEAD_NO)
+               return FALSE;
+
+       if(other.vehicle != world)
+               return FALSE;
+
+       if(clienttype(other) != CLIENTTYPE_REAL)
+               if(!autocvar_g_vehicles_allow_bots)
+                       return FALSE;
+
+       if(teamplay && other.team != self.team)
+               return FALSE;
+
+       return TRUE;
+}
+
+void bumb_touch()
+{
+
+       if(self.gunner1 != world && self.gunner2 != world)
+       {
+               vehicles_touch();
+               return;
+       }
+
+       if(vehicles_valid_pilot())
+       {
+               if(self.gun1.phase <= time)
+                       if(bumb_gunner_enter())
+                               return;
+
+               if(self.gun2.phase <= time)
+                       if(bumb_gunner_enter())
+                               return;
+       }
+
+       vehicles_touch();
+}
+
+void bumb_regen()
+{
+       if(self.gun1.delay + autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause < time)
+               self.gun1.vehicle_energy = min(autocvar_g_vehicle_bumblebee_cannon_ammo,
+                                                                          self.gun1.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * frametime);
+
+       if(self.gun2.delay + autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause < time)
+               self.gun2.vehicle_energy = min(autocvar_g_vehicle_bumblebee_cannon_ammo,
+                                                                          self.gun2.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * frametime);
+
+       if(self.vehicle_flags  & VHF_SHIELDREGEN)
+               vehicles_regen(self.dmg_time, vehicle_shield, autocvar_g_vehicle_bumblebee_shield, autocvar_g_vehicle_bumblebee_shield_regen_pause, autocvar_g_vehicle_bumblebee_shield_regen, frametime, TRUE);
+
+       if(self.vehicle_flags  & VHF_HEALTHREGEN)
+               vehicles_regen(self.dmg_time, vehicle_health, autocvar_g_vehicle_bumblebee_health, autocvar_g_vehicle_bumblebee_health_regen_pause, autocvar_g_vehicle_bumblebee_health_regen, frametime, FALSE);
+
+       if(self.vehicle_flags  & VHF_ENERGYREGEN)
+               vehicles_regen(self.wait, vehicle_energy, autocvar_g_vehicle_bumblebee_energy, autocvar_g_vehicle_bumblebee_energy_regen_pause, autocvar_g_vehicle_bumblebee_energy_regen, frametime, FALSE);
+
+}
+
+float bumb_pilot_frame()
+{
+       entity pilot, vehic;
+       vector newvel;
+
+       pilot = self;
+       vehic = self.vehicle;
+       self   = vehic;
+
+
+       if(vehic.deadflag != DEAD_NO)
+       {
+               self = pilot;
+               pilot.BUTTON_ATCK = pilot.BUTTON_ATCK2 = 0;
+               return 1;
+       }
+
+       bumb_regen();
+
+       crosshair_trace(pilot);
+
+       vector vang;
+       float ftmp;
+
+       vang = vehic.angles;
+       newvel = vectoangles(normalize(trace_endpos - self.origin + '0 0 32'));
+       vang_x *= -1;
+       newvel_x *= -1;
+       if(newvel_x > 180)  newvel_x -= 360;
+       if(newvel_x < -180) newvel_x += 360;
+       if(newvel_y > 180)  newvel_y -= 360;
+       if(newvel_y < -180) newvel_y += 360;
+
+       ftmp = shortangle_f(pilot.v_angle_y - vang_y, vang_y);
+       if(ftmp > 180)  ftmp -= 360;
+       if(ftmp < -180) ftmp += 360;
+       vehic.avelocity_y = bound(-autocvar_g_vehicle_bumblebee_turnspeed, ftmp + vehic.avelocity_y * 0.9, autocvar_g_vehicle_bumblebee_turnspeed);
+
+       // Pitch
+       ftmp = 0;
+       if(pilot.movement_x > 0 && vang_x < autocvar_g_vehicle_bumblebee_pitchlimit) 
+               ftmp = 4;
+       else if(pilot.movement_x < 0 && vang_x > -autocvar_g_vehicle_bumblebee_pitchlimit) 
+               ftmp = -8;
+
+       newvel_x = bound(-autocvar_g_vehicle_bumblebee_pitchlimit, newvel_x , autocvar_g_vehicle_bumblebee_pitchlimit);
+       ftmp = vang_x - bound(-autocvar_g_vehicle_bumblebee_pitchlimit, newvel_x + ftmp, autocvar_g_vehicle_bumblebee_pitchlimit);
+       vehic.avelocity_x = bound(-autocvar_g_vehicle_bumblebee_pitchspeed, ftmp + vehic.avelocity_x * 0.9, autocvar_g_vehicle_bumblebee_pitchspeed);
+
+       vehic.angles_x = anglemods(vehic.angles_x);
+       vehic.angles_y = anglemods(vehic.angles_y);
+       vehic.angles_z = anglemods(vehic.angles_z);
+
+       makevectors('0 1 0' * vehic.angles_y);
+       newvel = vehic.velocity * -autocvar_g_vehicle_bumblebee_friction;
+
+       if(pilot.movement_x != 0)
+       {
+               if(pilot.movement_x > 0)
+                       newvel += v_forward  * autocvar_g_vehicle_bumblebee_speed_forward;
+               else if(pilot.movement_x < 0)
+                       newvel -= v_forward  * autocvar_g_vehicle_bumblebee_speed_forward;
+       }
+
+       if(pilot.movement_y != 0)
+       {
+               if(pilot.movement_y < 0)
+                       newvel -= v_right * autocvar_g_vehicle_bumblebee_speed_strafe;
+               else if(pilot.movement_y > 0)
+                       newvel += v_right * autocvar_g_vehicle_bumblebee_speed_strafe;
+               ftmp = newvel * v_right;
+               ftmp *= frametime * 0.1;
+               vehic.angles_z = bound(-15, vehic.angles_z + ftmp, 15);
+       }
+       else
+       {
+               vehic.angles_z *= 0.95;
+               if(vehic.angles_z >= -1 && vehic.angles_z <= -1)
+                       vehic.angles_z = 0;
+       }
+
+       if(pilot.BUTTON_CROUCH)
+               newvel -=   v_up * autocvar_g_vehicle_bumblebee_speed_down;
+       else if(pilot.BUTTON_JUMP)
+               newvel +=  v_up * autocvar_g_vehicle_bumblebee_speed_up;
+
+       vehic.velocity  += newvel * frametime;
+       pilot.velocity = pilot.movement  = vehic.velocity;
+       
+
+       if(autocvar_g_vehicle_bumblebee_healgun_locktime)
+       {               
+               if(vehic.tur_head.lock_time < time || vehic.tur_head.enemy.deadflag)
+                       vehic.tur_head.enemy = world;
+
+               if(trace_ent)
+               if(trace_ent.movetype)
+               if(trace_ent.takedamage)
+               if(!trace_ent.deadflag)
+               {
+                       if(teamplay)
+                       {
+                               if(trace_ent.team == pilot.team)
+                               {
+                                       vehic.tur_head.enemy = trace_ent;
+                                       vehic.tur_head.lock_time = time + autocvar_g_vehicle_bumblebee_healgun_locktime;
+                               }
+                       }
+                       else
+                       {            
+                               vehic.tur_head.enemy = trace_ent;
+                               vehic.tur_head.lock_time = time + autocvar_g_vehicle_bumblebee_healgun_locktime;
+                       }
+               }
+                       
+               if(vehic.tur_head.enemy)
+               {
+                       trace_endpos = real_origin(vehic.tur_head.enemy);                       
+                       UpdateAuxiliaryXhair(pilot, trace_endpos, '0 0.75 0', 0);               
+               }
+       }
+       
+       vang = vehicle_aimturret(vehic, trace_endpos, self.gun3, "fire",
+                                         autocvar_g_vehicle_bumblebee_raygun_pitchlimit_down * -1,  autocvar_g_vehicle_bumblebee_raygun_pitchlimit_up,
+                                         autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides * -1,  autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides,  autocvar_g_vehicle_bumblebee_raygun_turnspeed);
+
+       if((pilot.BUTTON_ATCK || pilot.BUTTON_ATCK2) && (vehic.vehicle_energy > autocvar_g_vehicle_bumblebee_raygun_dps * sys_frametime || autocvar_g_vehicle_bumblebee_raygun == 0))
+       {
+               vehic.gun3.enemy.realowner = pilot;
+               vehic.gun3.enemy.effects &~= EF_NODRAW;
+               
+               vehic.gun3.enemy.hook_start = gettaginfo(vehic.gun3, gettagindex(vehic.gun3, "fire"));
+               vehic.gun3.enemy.SendFlags |= BRG_START;
+               
+               traceline(vehic.gun3.enemy.hook_start, vehic.gun3.enemy.hook_start + v_forward * autocvar_g_vehicle_bumblebee_raygun_range, MOVE_NORMAL, vehic);
+               
+               if(trace_ent)
+               {
+                       if(autocvar_g_vehicle_bumblebee_raygun)
+                       {
+                               Damage(trace_ent, vehic, pilot, autocvar_g_vehicle_bumblebee_raygun_dps * sys_frametime, DEATH_GENERIC, trace_endpos, v_forward * autocvar_g_vehicle_bumblebee_raygun_fps * sys_frametime);
+                               vehic.vehicle_energy -= autocvar_g_vehicle_bumblebee_raygun_aps * sys_frametime;
+                       }
+                       else
+                       {
+                               if(trace_ent.deadflag == DEAD_NO)
+                                       if((teamplay && trace_ent.team == pilot.team) || !teamplay)
+                                       {
+
+                                               if(trace_ent.vehicle_flags & VHF_ISVEHICLE)
+                                               {
+                                                       if(autocvar_g_vehicle_bumblebee_healgun_sps && trace_ent.vehicle_health <= trace_ent.tur_health)
+                                                               trace_ent.vehicle_shield = min(trace_ent.vehicle_shield + autocvar_g_vehicle_bumblebee_healgun_sps * frametime, trace_ent.tur_head.tur_health);
+
+                                                       if(autocvar_g_vehicle_bumblebee_healgun_hps)
+                                                               trace_ent.vehicle_health = min(trace_ent.vehicle_health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, trace_ent.tur_health);
+                                               }
+                                               else if(trace_ent.flags & FL_CLIENT)
+                                               {
+                                                       if(trace_ent.health <= autocvar_g_vehicle_bumblebee_healgun_hmax && autocvar_g_vehicle_bumblebee_healgun_hps)
+                                                               trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, autocvar_g_vehicle_bumblebee_healgun_hmax);
+
+                                                       if(trace_ent.armorvalue <= autocvar_g_vehicle_bumblebee_healgun_amax && autocvar_g_vehicle_bumblebee_healgun_aps)
+                                                               trace_ent.armorvalue = min(trace_ent.armorvalue + autocvar_g_vehicle_bumblebee_healgun_aps * frametime, autocvar_g_vehicle_bumblebee_healgun_amax);
+
+                                                       trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, autocvar_g_vehicle_bumblebee_healgun_hmax);
+                                               }
+                                               else if(trace_ent.turrcaps_flags & TFL_TURRCAPS_ISTURRET)
+                                               {
+                                                       if(trace_ent.health  <= trace_ent.tur_health && autocvar_g_vehicle_bumblebee_healgun_hps)
+                                                               trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * frametime, trace_ent.tur_health);
+                                                       //else ..hmmm what? ammo?
+
+                                                       trace_ent.SendFlags |= TNSF_STATUS;
+                                               }
+                                       }
+                       }
+               }
+               
+               vehic.gun3.enemy.hook_end = trace_endpos;
+               setorigin(vehic.gun3.enemy, trace_endpos);
+               vehic.gun3.enemy.SendFlags |= BRG_END;
+               
+               vehic.wait = time + 1;
+       }
+       else
+               vehic.gun3.enemy.effects |= EF_NODRAW;
+       /*{
+               if(vehic.gun3.enemy)
+                       remove(vehic.gun3.enemy);
+
+               vehic.gun3.enemy = world;
+       }
+       */
+       
+       VEHICLE_UPDATE_PLAYER(pilot, health, bumblebee);
+       VEHICLE_UPDATE_PLAYER(pilot, energy, bumblebee);
+
+       pilot.vehicle_ammo1 = (vehic.gun1.vehicle_energy / autocvar_g_vehicle_bumblebee_cannon_ammo) * 100;
+       pilot.vehicle_ammo2 = (vehic.gun2.vehicle_energy / autocvar_g_vehicle_bumblebee_cannon_ammo) * 100;
+
+       if(vehic.vehicle_flags & VHF_HASSHIELD)
+               VEHICLE_UPDATE_PLAYER(pilot, shield, bumblebee);
+               
+       vehic.angles_x *= -1;
+       makevectors(vehic.angles);
+       vehic.angles_x *= -1;
+       setorigin(pilot, vehic.origin + v_up * 48 + v_forward * 160);
+
+       pilot.BUTTON_ATCK = pilot.BUTTON_ATCK2 = pilot.BUTTON_CROUCH = 0;
+       self = pilot;
+
+       return 1;
+}
+
+void bumb_think()
+{
+       self.movetype = MOVETYPE_TOSS;
+               
+               //self.velocity = self.velocity * 0.5;
+       self.angles_z *= 0.8;
+       self.angles_x *= 0.8;
+       
+       self.nextthink = time + 0.05;
+       
+       if(!self.owner)
+       {
+               entity oldself = self;          
+               if(self.gunner1)
+               {
+                       self = self.gunner1;
+                       oldself.gun1.vehicle_exit(VHEF_EJECT);
+                       entity oldother = other;
+                       other = self;
+                       self = oldself;
+                       self.phase = 0;
+                       self.touch();
+                       other = oldother;
+                       return;
+               }
+               
+               if(self.gunner2)
+               {
+                       self = self.gunner2;
+                       oldself.gun2.vehicle_exit(VHEF_EJECT);
+                       entity oldother = other;
+                       other = self;
+                       self = oldself;
+                       self.phase = 0;
+                       self.touch();
+                       other = oldother;
+                       return;
+               }               
+       }
+       
+}
+
+void bumb_enter()
+{
+       self.touch = bumb_touch;
+       self.nextthink = 0;
+       self.movetype = MOVETYPE_BOUNCEMISSILE;
+       //setattachment(self.owner, self.vehicle_viewport, "");
+}
+
+void bumb_exit(float eject)
+{
+       self.touch = vehicles_touch;
+       self.think = bumb_think;
+       self.nextthink = time;
+       
+       if(!self.owner)
+               return;
+       
+       fixedmakevectors(self.angles);
+       vector spot;
+       if(vlen(self.velocity) > autocvar_g_vehicle_bumblebee_speed_forward * 0.5)              
+               spot = self.origin + v_up * 128 + v_forward * 200;
+       else
+               spot = self.origin + v_up * 128 - v_forward * 200;
+       
+       spot = vehicles_findgoodexit(spot);
+       
+
+       self.owner.velocity = 0.75 * self.vehicle.velocity + normalize(spot - self.vehicle.origin) * 200;
+       self.owner.velocity_z += 10;
+       setorigin(self.owner, spot);
+
+       /*if(eject)
+       {
+           spot = self.origin + v_forward * 100 + '0 0 64';
+           spot = vehicles_findgoodexit(spot);
+           //setorigin(self.owner , spot);
+           self.owner.velocity = (v_up + v_forward * 0.25) * 250;
+           self.owner.oldvelocity = self.owner.velocity;
+       }
+       else
+       {
+               if(vlen(self.velocity) > autocvar_g_vehicle_bumblebee_speed_forward * 0.5)              
+               {
+                       if(vlen(self.velocity) > autocvar_sv_maxairspeed)
+                               self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed;
+                       else
+                               self.owner.velocity = self.velocity + v_forward * 100;
+                       
+                       self.owner.velocity_z += 200;
+                       spot = self.origin + v_forward * 128 + '0 0 32';
+                       spot = vehicles_findgoodexit(spot);
+               }
+               else
+               {
+                       self.owner.velocity = self.velocity * 0.5;
+                       self.owner.velocity_z += 10;
+                       spot = self.origin - v_forward * 300 + '0 0 32';
+                       spot = vehicles_findgoodexit(spot);
+               }
+           self.owner.oldvelocity = self.owner.velocity;
+           //setorigin(self.owner , spot);
+       }
+       */
+       
+       antilag_clear(self.owner);
+    self.owner = world;
+}
+
+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_forceintensity,
+                                DEATH_WAKIBLOWUP, world);
+
+       sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
+       pointparticles(particleeffectnum("explosion_large"), randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1);
+       
+       if(self.owner.deadflag == DEAD_DYING)
+               self.owner.deadflag = DEAD_DEAD;
+       
+       remove(self);
+}
+
+void bumb_diethink()
+{
+       if(time >= self.wait)
+               self.think = bumb_blowup;
+
+       if(random() < 0.1)
+       {
+               sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
+               pointparticles(particleeffectnum("explosion_small"), randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1);
+       }
+
+       self.nextthink = time + 0.1;
+}
+
+void bumb_die()
+{
+       entity oldself = self;
+       
+       // Hide beam
+       if(self.gun3.enemy || !wasfreed(self.gun3.enemy))
+               self.gun3.enemy.effects |= EF_NODRAW;
+       
+       if(self.gunner1)
+       {
+               self = self.gunner1;
+               oldself.gun1.vehicle_exit(VHEF_EJECT);
+               self = oldself;
+       }
+
+       if(self.gunner2)
+       {
+               self = self.gunner2;
+               oldself.gun2.vehicle_exit(VHEF_EJECT);
+               self = oldself;
+       }
+
+       self.vehicle_exit(VHEF_EJECT);
+
+       fixedmakevectors(self.angles);
+       vehicle_tossgib(self.gun1, self.velocity + v_right * 300 + v_up * 100 + randomvec() * 200, "cannon_right", rint(random()), rint(random()), 6, randomvec() * 200);
+       vehicle_tossgib(self.gun2, self.velocity + v_right * -300 + v_up * 100 + randomvec() * 200, "cannon_left", rint(random()), rint(random()), 6, randomvec() * 200);
+       vehicle_tossgib(self.gun3, self.velocity + v_forward * 300 + v_up * -100 + randomvec() * 200, "raygun", rint(random()), rint(random()), 6, randomvec() * 300);
+
+       entity _body = vehicle_tossgib(self, self.velocity + randomvec() * 200, "", rint(random()), rint(random()), 6, randomvec() * 100);
+
+       if(random() > 0.5)
+               _body.touch = bumb_blowup;
+       else
+               _body.touch = SUB_Null;
+               
+       _body.think = bumb_diethink;
+       _body.nextthink = time;
+       _body.wait = time + 2 + (random() * 8);
+       _body.owner = self;
+       _body.enemy = self.enemy;
+       
+       pointparticles(particleeffectnum("explosion_medium"), findbetterlocation(self.origin, 16), '0 0 0', 1);
+       
+       self.health                     = 0;
+       self.event_damage       = SUB_Null;
+       self.solid                      = SOLID_CORPSE;
+       self.takedamage         = DAMAGE_NO;
+       self.deadflag           = DEAD_DYING;
+       self.movetype           = MOVETYPE_NONE;
+       self.effects            = EF_NODRAW;
+       self.colormod           = '0 0 0';
+       self.avelocity          = '0 0 0';
+       self.velocity           = '0 0 0';
+       self.touch                      = SUB_Null;
+       self.nextthink          = 0;
+
+       setorigin(self, self.pos1);
+
+}
+
+void bumb_impact()
+{
+    if(autocvar_g_vehicle_bumblebee_bouncepain_x)
+        vehilces_impact(autocvar_g_vehicle_bumblebee_bouncepain_x, 
+                                               autocvar_g_vehicle_bumblebee_bouncepain_y, 
+                                               autocvar_g_vehicle_bumblebee_bouncepain_z);
+}
+
+void bumb_spawn(float _f)
+{
+       /*
+       float i;
+       for(i=1; gettaginfo(self.gun1, i), gettaginfo_name; ++i)
+       {
+
+           dprint(" ------- ^1gettaginfo_name^2(",ftos(i),") ^3=", gettaginfo_name, "\n");
+       }
+       */
+       if(!self.gun1)
+       {
+               // for some reason, autosizing of the shiled entity refuses to work for this one so set it up in advance.
+               self.vehicle_shieldent = spawn();
+               self.vehicle_shieldent.effects = EF_LOWPRECISION;
+               setmodel(self.vehicle_shieldent, "models/vhshield.md3");
+               setattachment(self.vehicle_shieldent, self, "");
+               setorigin(self.vehicle_shieldent, real_origin(self) - self.origin);
+               self.vehicle_shieldent.scale       = 512 / vlen(self.maxs - self.mins);
+               self.vehicle_shieldent.think       = shieldhit_think;
+               self.vehicle_shieldent.alpha = -1;
+               self.vehicle_shieldent.effects = EF_LOWPRECISION | EF_NODRAW;
+
+               self.gun1 = spawn();
+               self.gun2 = spawn();
+               self.gun3 = spawn();
+
+               self.vehicle_flags |= VHF_MULTISLOT;
+
+               self.gun1.owner = self;
+               self.gun2.owner = self;
+               self.gun3.owner = self;
+
+               setmodel(self.gun1, "models/vehicles/bumblebee_plasma_right.dpm");
+               setmodel(self.gun2, "models/vehicles/bumblebee_plasma_left.dpm");
+               setmodel(self.gun3, "models/vehicles/bumblebee_ray.dpm");
+
+               setattachment(self.gun1, self, "cannon_right");
+               setattachment(self.gun2, self, "cannon_left");
+
+               // Angled bones are no fun, messes up gun-aim; so work arround it.
+               self.gun3.pos1 = self.angles;
+               self.angles = '0 0 0';
+               vector ofs = gettaginfo(self, gettagindex(self, "raygun"));
+               ofs -= self.origin;
+               setattachment(self.gun3, self, "");
+               setorigin(self.gun3, ofs);
+               self.angles = self.gun3.pos1;
+
+               vehicle_addplayerslot(self, self.gun1, HUD_BUMBLEBEE_GUN, "models/vehicles/wakizashi_cockpit.dpm", bumb_gunner_frame, bumb_gunner_exit);
+               vehicle_addplayerslot(self, self.gun2, HUD_BUMBLEBEE_GUN, "models/vehicles/wakizashi_cockpit.dpm", bumb_gunner_frame, bumb_gunner_exit);
+
+               setorigin(self.vehicle_hudmodel, '50 0 -5');    // Move cockpit forward - down.
+               setorigin(self.vehicle_viewport, '5 0 2');    // Move camera forward up
+
+               //fixme-model-bones
+               setorigin(self.gun1.vehicle_hudmodel, '90 -27 -23');
+               setorigin(self.gun1.vehicle_viewport, '-85 0 50');
+               //fixme-model-bones
+               setorigin(self.gun2.vehicle_hudmodel, '90 27 -23');
+               setorigin(self.gun2.vehicle_viewport, '-85 0 50');
+
+               self.scale = 1.5;
+               
+               // Raygun beam
+               if(self.gun3.enemy == world)
+               {                       
+                       self.gun3.enemy = spawn();
+                       Net_LinkEntity(self.gun3.enemy, TRUE, 0, bumble_raygun_send);
+                       self.gun3.enemy.SendFlags = BRG_SETUP;                  
+                       self.gun3.enemy.cnt = autocvar_g_vehicle_bumblebee_raygun;                      
+                       self.gun3.enemy.effects = EF_NODRAW | EF_LOWPRECISION;
+               }
+       }
+
+       self.vehicle_health = autocvar_g_vehicle_bumblebee_health;
+       self.vehicle_shield = autocvar_g_vehicle_bumblebee_shield;
+       self.solid          = SOLID_BBOX;
+       //self.movetype         = MOVETYPE_BOUNCEMISSILE;
+       self.movetype           = MOVETYPE_TOSS;
+       self.vehicle_impact = bumb_impact;
+       self.damageforcescale = 0.025;
+       
+       setorigin(self, self.origin + '0 0 25');
+}
+
+void spawnfunc_vehicle_bumblebee()
+{
+       if(!autocvar_g_vehicle_bumblebee)
+       {
+               remove(self);
+               return;
+       }
+
+       precache_model("models/vehicles/bumblebee_body.dpm");
+       precache_model("models/vehicles/bumblebee_plasma_left.dpm");
+       precache_model("models/vehicles/bumblebee_plasma_right.dpm");
+       precache_model("models/vehicles/bumblebee_ray.dpm");
+       precache_model("models/vehicles/wakizashi_cockpit.dpm");
+       precache_model("models/vehicles/spiderbot_cockpit.dpm");
+       precache_model("models/vehicles/raptor_cockpit.dpm");
+
+       if(autocvar_g_vehicle_bumblebee_energy)
+               if(autocvar_g_vehicle_bumblebee_energy_regen)
+                       self.vehicle_flags |= VHF_ENERGYREGEN;
+
+       if(autocvar_g_vehicle_bumblebee_shield)
+               self.vehicle_flags |= VHF_HASSHIELD;
+
+       if(autocvar_g_vehicle_bumblebee_shield_regen)
+               self.vehicle_flags |= VHF_SHIELDREGEN;
+
+       if(autocvar_g_vehicle_bumblebee_health_regen)
+               self.vehicle_flags |= VHF_HEALTHREGEN;
+
+       if not(vehicle_initialize(
+                          "Bumblebee", "models/vehicles/bumblebee_body.dpm",
+                          "", "models/vehicles/spiderbot_cockpit.dpm", "", "", "tag_viewport",
+                          HUD_BUMBLEBEE, BUMB_MIN, BUMB_MAX, FALSE,
+                          bumb_spawn, autocvar_g_vehicle_bumblebee_respawntime,
+                          bumb_pilot_frame, bumb_enter, bumb_exit,
+                          bumb_die, bumb_think, FALSE, autocvar_g_vehicle_bumblebee_health, autocvar_g_vehicle_bumblebee_shield))
+       {
+               remove(self);
+               return;
+       }
+}
+
+float bumble_raygun_send(entity to, float sf)
+{
+       WriteByte(MSG_ENTITY, ENT_CLIENT_BUMBLE_RAYGUN);
+
+       WriteByte(MSG_ENTITY, sf);
+       if(sf & BRG_SETUP)
+       {
+               WriteByte(MSG_ENTITY, num_for_edict(self.realowner));
+               WriteByte(MSG_ENTITY, self.realowner.team);
+               WriteByte(MSG_ENTITY, self.cnt);
+       }
+
+       if(sf & BRG_START)
+       {
+               WriteCoord(MSG_ENTITY, self.hook_start_x);
+               WriteCoord(MSG_ENTITY, self.hook_start_y);
+               WriteCoord(MSG_ENTITY, self.hook_start_z);
+       }
+
+       if(sf & BRG_END)
+       {
+               WriteCoord(MSG_ENTITY, self.hook_end_x);
+               WriteCoord(MSG_ENTITY, self.hook_end_y);
+               WriteCoord(MSG_ENTITY, self.hook_end_z);
+       }
+
+       return TRUE;
+}
+#endif // SVQC
+
+#ifdef CSQC
+/*
+.vector raygun_l1
+.vector raygun_l2;
+.vector raygun_l3;
+*/
+
+void bumble_raygun_draw()
+{
+       float _len;
+       vector _dir;
+       vector _vtmp1, _vtmp2;
+
+       _len = vlen(self.origin - self.move_origin);
+       _dir = normalize(self.move_origin - self.origin);
+       
+       if(self.total_damages < time)
+       {
+               boxparticles(self.traileffect, self, self.origin, self.origin + _dir * -64, _dir * -_len , _dir * -_len, 1, PARTICLES_USEALPHA);
+               boxparticles(self.lip, self, self.move_origin, self.move_origin + _dir * -64, _dir * -200 , _dir * -200, 1, PARTICLES_USEALPHA);
+               self.total_damages = time + 0.1;
+       }
+
+       float i, df, sz, al;
+       for(i = -0.1; i < 0.2; i += 0.1)
+       {
+               df = DRAWFLAG_NORMAL; //((random() < 0.5) ? DRAWFLAG_ADDITIVE : DRAWFLAG_SCREEN);
+               sz = 5 + random() * 5;
+               al = 0.25 + random() * 0.5;
+               _vtmp1 = self.origin + _dir * _len * (0.25 + i);
+               _vtmp1 += (randomvec() * (_len * 0.2) * (frametime * 2));       //self.raygun_l1;
+               Draw_CylindricLine(self.origin, _vtmp1, sz, "gfx/colors/white.tga", 1, 1, self.colormod, al, df, view_origin);
+
+               _vtmp2 = self.origin + _dir * _len * (0.5 + i);
+               _vtmp2 += (randomvec() * (_len * 0.2) * (frametime * 5));       //self.raygun_l2;
+               Draw_CylindricLine(_vtmp1, _vtmp2, sz, "gfx/colors/white.tga", 1, 1, self.colormod, al, df, view_origin);
+
+               _vtmp1 = self.origin + _dir * _len * (0.75 + i);
+               _vtmp1 += randomvec() * (_len * 0.2) * (frametime * 10);     //self.raygun_l3;
+               Draw_CylindricLine(_vtmp2, _vtmp1, sz, "gfx/colors/white.tga", 1, 1, self.colormod, al, df, view_origin);
+
+               Draw_CylindricLine(_vtmp1, self.move_origin +  randomvec() * 32, sz, "gfx/colors/white.tga", 1, 1, self.colormod, al, df, view_origin);
+       }
+}
+
+void bumble_raygun_read(float bIsNew)
+{
+       float sf = ReadByte();
+
+       if(sf & BRG_SETUP)
+       {
+               self.cnt  = ReadByte();
+               self.team = ReadByte();
+               self.cnt  = ReadByte();
+               
+               if(self.cnt)
+                       self.colormod = '1 0 0';
+               else
+                       self.colormod = '0 1 0';
+
+               self.traileffect = particleeffectnum("healray_muzzleflash");
+               self.lip = particleeffectnum("healray_impact");         
+
+               self.draw = bumble_raygun_draw;
+       }
+       
+       
+       if(sf & BRG_START)
+       {
+               self.origin_x = ReadCoord();
+               self.origin_y = ReadCoord();
+               self.origin_z = ReadCoord();
+               setorigin(self, self.origin);
+       }
+
+       if(sf & BRG_END)
+       {
+               self.move_origin_x = ReadCoord();
+               self.move_origin_y = ReadCoord();
+               self.move_origin_z = ReadCoord();
+       }
+}
+
+void bumblebee_draw()
+{
+
+}
+
+void bumblebee_draw2d()
+{
+
+}
+
+void bumblebee_read_extra()
+{
+
+}
+
+void vehicle_bumblebee_assemble()
+{
+
+}
+#endif //CSQC
index 147c8139c64ae171baef0f5c8f1837052ab77e3c..792be585023d064650cad8f9360b771d1d16a4f0 100644 (file)
@@ -6,6 +6,8 @@ void racer_exit(float eject);
 void racer_enter();
 
 // Auto cvars
+float autocvar_g_vehicle_racer;
+
 float autocvar_g_vehicle_racer_speed_afterburn;
 float autocvar_g_vehicle_racer_afterburn_cost;
 
@@ -72,6 +74,7 @@ float autocvar_g_vehicle_racer_bouncestop;
 vector autocvar_g_vehicle_racer_bouncepain;
 
 var vector racer_force_from_tag(string tag_name, float spring_length, float max_power);
+void racer_spawn(float _spawnflag);
 
 void racer_align4point(float _delta)
 {
@@ -126,7 +129,7 @@ void racer_fire_cannon(string tagname)
     bolt = vehicles_projectile("wakizashi_gun_muzzleflash", "weapons/lasergun_fire.wav",
                            v, normalize(v_forward + randomvec() * autocvar_g_vehicle_racer_cannon_spread) * autocvar_g_vehicle_racer_cannon_speed,
                            autocvar_g_vehicle_racer_cannon_damage, autocvar_g_vehicle_racer_cannon_radius, autocvar_g_vehicle_racer_cannon_force,  0,
-                           DEATH_WAKIGUN, PROJECTILE_WAKICANNON, 0, TRUE, TRUE);
+                           DEATH_WAKIGUN, PROJECTILE_WAKICANNON, 0, TRUE, TRUE, self.owner);
 
        // Fix z-aim (for chase mode)
     v = normalize(trace_endpos - bolt.origin);
@@ -240,7 +243,7 @@ void racer_fire_rocket(string tagname, entity trg)
     entity rocket = rocket = vehicles_projectile("wakizashi_rocket_launch", "weapons/rocket_fire.wav",
                            v, v_forward * autocvar_g_vehicle_racer_rocket_speed,
                            autocvar_g_vehicle_racer_rocket_damage, autocvar_g_vehicle_racer_rocket_radius, autocvar_g_vehicle_racer_rocket_force, 3,
-                           DEATH_WAKIROCKET, PROJECTILE_WAKIROCKET, 20, FALSE, FALSE);
+                           DEATH_WAKIROCKET, PROJECTILE_WAKIROCKET, 20, FALSE, FALSE, self.owner);
 
     rocket.lip              = autocvar_g_vehicle_racer_rocket_accel * sys_frametime;
     rocket.wait             = autocvar_g_vehicle_racer_rocket_turnrate;
@@ -333,7 +336,7 @@ float racer_frame()
     if (player.BUTTON_JUMP && racer.vehicle_energy >= (autocvar_g_vehicle_racer_afterburn_cost * frametime))
     {
         if(time - racer.wait > 0.2)
-            pointparticles(particleeffectnum("wakizashi_booster_smoke"), self.origin, '0 0 0', 1);
+            pointparticles(particleeffectnum("wakizashi_booster_smoke"), self.origin - v_forward * 32, v_forward  * vlen(self.velocity), 1);
 
         racer.wait = time;
         racer.vehicle_energy -= autocvar_g_vehicle_racer_afterburn_cost * frametime;
@@ -423,20 +426,20 @@ float racer_frame()
     player.vehicle_reload1 = bound(0, 100 * ((time - racer.lip) / (racer.delay - racer.lip)), 100);
 
     if(racer.vehicle_flags  & VHF_SHIELDREGEN)
-        vehicles_regen(dmg_time, vehicle_shield, autocvar_g_vehicle_racer_shield, autocvar_g_vehicle_racer_shield_regen_pause, autocvar_g_vehicle_racer_shield_regen, frametime, TRUE);
+        vehicles_regen(racer.dmg_time, vehicle_shield, autocvar_g_vehicle_racer_shield, autocvar_g_vehicle_racer_shield_regen_pause, autocvar_g_vehicle_racer_shield_regen, frametime, TRUE);
 
     if(racer.vehicle_flags  & VHF_HEALTHREGEN)
-        vehicles_regen(dmg_time, vehicle_health, autocvar_g_vehicle_racer_health, autocvar_g_vehicle_racer_health_regen_pause, autocvar_g_vehicle_racer_health_regen, frametime, FALSE);
+        vehicles_regen(racer.dmg_time, vehicle_health, autocvar_g_vehicle_racer_health, autocvar_g_vehicle_racer_health_regen_pause, autocvar_g_vehicle_racer_health_regen, frametime, FALSE);
 
     if(racer.vehicle_flags  & VHF_ENERGYREGEN)
-        vehicles_regen(wait, vehicle_energy, autocvar_g_vehicle_racer_energy, autocvar_g_vehicle_racer_energy_regen_pause, autocvar_g_vehicle_racer_energy_regen, frametime, FALSE);
+        vehicles_regen(racer.wait, vehicle_energy, autocvar_g_vehicle_racer_energy, autocvar_g_vehicle_racer_energy_regen_pause, autocvar_g_vehicle_racer_energy_regen, frametime, FALSE);
 
 
-    VEHICLE_UPDATE_PLAYER(health, racer);
-    VEHICLE_UPDATE_PLAYER(energy, racer);
+    VEHICLE_UPDATE_PLAYER(player, health, racer);
+    VEHICLE_UPDATE_PLAYER(player, energy, racer);
 
     if(racer.vehicle_flags & VHF_HASSHIELD)
-        VEHICLE_UPDATE_PLAYER(shield, racer);
+        VEHICLE_UPDATE_PLAYER(player, shield, racer);
 
     player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0;
     setorigin(player,racer.origin + '0 0 32');
@@ -530,26 +533,6 @@ void racer_impact()
         vehilces_impact(autocvar_g_vehicle_racer_bouncepain_x, autocvar_g_vehicle_racer_bouncepain_y, autocvar_g_vehicle_racer_bouncepain_z);
 }
 
-void racer_spawn()
-{
-    self.think          = racer_think;
-    self.nextthink      = time;
-    self.vehicle_health = autocvar_g_vehicle_racer_health;
-    self.vehicle_shield = autocvar_g_vehicle_racer_shield;
-
-    self.movetype       = MOVETYPE_TOSS;
-    self.solid          = SOLID_SLIDEBOX;
-    self.delay          = time;
-    self.scale          = 0.5;
-
-    setsize(self, RACER_MIN * 0.5, RACER_MAX * 0.5);
-    self.bouncefactor = autocvar_g_vehicle_racer_bouncefactor;
-    self.bouncestop = autocvar_g_vehicle_racer_bouncestop;
-    self.vehicle_impact = racer_impact;
-    //self.destvec = autocvar_g_vehicle_racer_bouncepain;
-}
-
-
 void racer_blowup()
 {
     self.deadflag    = DEAD_DEAD;
@@ -609,44 +592,51 @@ void racer_die()
        self.think     = racer_blowup;
        self.nextthink = 2 + time + random() * 3;
 }
-
-void racer_dinit()
+void racer_spawn(float _spawnflag)
 {
-    if not (vehicle_initialize(
-             "Wakizashi",
-             "models/vehicles/wakizashi.dpm",
-             "null", // we need this so tur_head is networked and usable for sounds
-             "models/vehicles/wakizashi_cockpit.dpm",
-             "", "", "tag_viewport",
-             HUD_WAKIZASHI,
-             0.5 * RACER_MIN, 0.5 * RACER_MAX,
-             FALSE,
-             racer_spawn, autocvar_g_vehicle_racer_respawntime,
-             racer_frame,
-             racer_enter, racer_exit,
-             racer_die,   racer_think,
-             TRUE, 
-             autocvar_g_vehicle_racer_health))
+    if(self.scale != 0.5)
     {
-        remove(self);
-        return;
+        if(autocvar_g_vehicle_racer_hovertype != 0)
+            racer_force_from_tag = vehicles_force_fromtag_maglev;
+        else
+            racer_force_from_tag = vehicles_force_fromtag_hover;
+
+        // FIXME: this be hakkz, fix the models insted (scale body, add tag_viewport to the hudmodel).
+        self.scale = 0.5;
+        setattachment(self.vehicle_hudmodel, self, "");
+        setattachment(self.vehicle_viewport, self, "tag_viewport");
+
+        self.mass               = 900;
     }
 
-    if(autocvar_g_vehicle_racer_hovertype != 0)
-        racer_force_from_tag = vehicles_force_fromtag_maglev;
-    else
-        racer_force_from_tag = vehicles_force_fromtag_hover;
+    self.think          = racer_think;
+    self.nextthink      = time;
+    self.vehicle_health = autocvar_g_vehicle_racer_health;
+    self.vehicle_shield = autocvar_g_vehicle_racer_shield;
 
-    // FIXME: this be hakkz, fix the models insted (scale body, add tag_viewport to the hudmodel).
-    self.scale = 0.5;
-    setattachment(self.vehicle_hudmodel, self, "");
-    setattachment(self.vehicle_viewport, self, "tag_viewport");
+    self.movetype       = MOVETYPE_TOSS;
+    self.solid          = SOLID_SLIDEBOX;
+    self.delay          = time;
+    self.scale          = 0.5;
 
-    self.mass               = 900;
+    setsize(self, RACER_MIN * 0.5, RACER_MAX * 0.5);
+    self.bouncefactor = autocvar_g_vehicle_racer_bouncefactor;
+    self.bouncestop = autocvar_g_vehicle_racer_bouncestop;
+    self.vehicle_impact = racer_impact;
+    self.damageforcescale = 0.5;
+    //self.destvec = autocvar_g_vehicle_racer_bouncepain;
 }
 
+
+
 void spawnfunc_vehicle_racer()
 {
+    if(!autocvar_g_vehicle_racer)
+    {
+        remove(self);
+        return;
+    }        
+    
     self.vehicle_flags |= VHF_DMGSHAKE;
     self.vehicle_flags |= VHF_DMGROLL;
 
@@ -661,7 +651,6 @@ void spawnfunc_vehicle_racer()
     precache_model ("models/vehicles/wakizashi.dpm");
     precache_model ("models/vehicles/wakizashi_cockpit.dpm");
 
-    vehicles_configcheck("vehicle_racer.cfg", autocvar_g_vehicle_racer_health);
     if(autocvar_g_vehicle_racer_energy)
         if(autocvar_g_vehicle_racer_energy_regen)
             self.vehicle_flags |= VHF_ENERGYREGEN;
@@ -675,11 +664,25 @@ void spawnfunc_vehicle_racer()
     if(autocvar_g_vehicle_racer_health_regen)
         self.vehicle_flags |= VHF_HEALTHREGEN;
 
-    self.think = racer_dinit;
-
-    if(g_assault)
-        self.nextthink = time + 0.5;
-    else
-        self.nextthink = time + (autocvar_g_vehicles_delayspawn ? autocvar_g_vehicle_racer_respawntime + (random() * autocvar_g_vehicles_delayspawn_jitter) : 0.5);
+    if not (vehicle_initialize(
+             "Wakizashi",
+             "models/vehicles/wakizashi.dpm",
+             "null", // we need this so tur_head is networked and usable for sounds
+             "models/vehicles/wakizashi_cockpit.dpm",
+             "", "", "tag_viewport",
+             HUD_WAKIZASHI,
+             0.5 * RACER_MIN, 0.5 * RACER_MAX,
+             FALSE,
+             racer_spawn, autocvar_g_vehicle_racer_respawntime,
+             racer_frame,
+             racer_enter, racer_exit,
+             racer_die,   racer_think,
+             TRUE, 
+             autocvar_g_vehicle_racer_health,
+             autocvar_g_vehicle_racer_shield))
+    {
+        remove(self);
+        return;
+    }
 }
 #endif // SVQC
index c0e0c31a52e629a19867ca3f836e83d1f78bba31..a623bd540727afbe8f054c6b7fb65ec277004e14 100644 (file)
@@ -7,6 +7,8 @@
 #define RAPTOR_MAX '80 80 70'
 
 #ifdef SVQC
+float autocvar_g_vehicle_raptor;
+
 float autocvar_g_vehicle_raptor_respawntime;
 float autocvar_g_vehicle_raptor_takeofftime;
 
@@ -72,7 +74,7 @@ float autocvar_g_vehicle_raptor_bouncefactor;
 float autocvar_g_vehicle_raptor_bouncestop;
 vector autocvar_g_vehicle_raptor_bouncepain;
 
-void raptor_spawn();
+void raptor_spawn(float);
 float raptor_frame();
 float raptor_takeoff();
 
@@ -183,7 +185,7 @@ void raptor_fire_cannon(entity gun, string tagname)
     vehicles_projectile("raptor_cannon_muzzleflash", "weapons/lasergun_fire.wav",
                            gettaginfo(gun, gettagindex(gun, tagname)), normalize(v_forward + randomvec() * autocvar_g_vehicle_raptor_cannon_spread) * autocvar_g_vehicle_raptor_cannon_speed,
                            autocvar_g_vehicle_raptor_cannon_damage, autocvar_g_vehicle_raptor_cannon_radius, autocvar_g_vehicle_raptor_cannon_force,  0,
-                           DEATH_RAPTOR_CANNON, PROJECTILE_RAPTORCANNON, 0, TRUE, TRUE);
+                           DEATH_RAPTOR_CANNON, PROJECTILE_RAPTORCANNON, 0, TRUE, TRUE, self.owner);
 }
 
 void raptor_think()
@@ -192,6 +194,7 @@ void raptor_think()
 
 void raptor_enter()
 {
+    self.vehicle_weapon2mode = RSM_BOMB;
     self.owner.PlayerPhysplug = raptor_takeoff;
     self.movetype       = MOVETYPE_BOUNCEMISSILE;
     self.solid          = SOLID_SLIDEBOX;
@@ -248,7 +251,7 @@ void raptor_exit(float eject)
 
     if not (self.owner)
         return;
-
+       
        makevectors(self.angles);
        if(eject)
        {
@@ -260,12 +263,24 @@ void raptor_exit(float eject)
        }
        else
        {
-           self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed;
+               if(vlen(self.velocity) > 2 * autocvar_sv_maxairspeed)
+               {
+                       self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed * 2;
+                       self.owner.velocity_z += 200;
+                       spot = self.origin + v_forward * 32 + '0 0 64';
+                       spot = vehicles_findgoodexit(spot);
+               }
+               else
+               {
+                       self.owner.velocity = self.velocity * 0.5;
+                       self.owner.velocity_z += 10;
+                       spot = self.origin - v_forward * 200 + '0 0 64';
+                       spot = vehicles_findgoodexit(spot);
+               }
            self.owner.oldvelocity = self.owner.velocity;
-           spot = self.origin - v_forward * 200 + '0 0 64';
-           spot = vehicles_findgoodexit(spot);
            setorigin(self.owner , spot);
        }
+       
        antilag_clear(self.owner);      
     self.owner = world;
 }
@@ -298,22 +313,22 @@ float raptor_takeoff()
         player.PlayerPhysplug = raptor_frame;
 
     if(self.vehicle_flags  & VHF_SHIELDREGEN)
-        vehicles_regen(dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, TRUE);
+        vehicles_regen(raptor.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, TRUE);
 
     if(self.vehicle_flags  & VHF_HEALTHREGEN)
-        vehicles_regen(dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, FALSE);
+        vehicles_regen(raptor.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, FALSE);
 
     if(self.vehicle_flags  & VHF_ENERGYREGEN)
-        vehicles_regen(cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, FALSE);
+        vehicles_regen(raptor.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, FALSE);
 
 
     raptor.bomb1.alpha = raptor.bomb2.alpha = (time - raptor.lip) / (raptor.delay - raptor.lip);
     player.vehicle_reload2 = bound(0, raptor.bomb1.alpha * 100, 100);
 
-    VEHICLE_UPDATE_PLAYER(health, raptor);
-    VEHICLE_UPDATE_PLAYER(energy, raptor);
+    VEHICLE_UPDATE_PLAYER(player, health, raptor);
+    VEHICLE_UPDATE_PLAYER(player, energy, raptor);
     if(self.vehicle_flags & VHF_HASSHIELD)
-        VEHICLE_UPDATE_PLAYER(shield, raptor);
+        VEHICLE_UPDATE_PLAYER(player, shield, raptor);
 
     player.BUTTON_ATCK = player.BUTTON_ATCK2 = player.BUTTON_CROUCH = 0;
     self = player;
@@ -463,7 +478,57 @@ float raptor_frame()
 
     vector vf, ad;
     // Target lock & predict
-    if(autocvar_g_vehicle_raptor_cannon_locktarget)
+    if(autocvar_g_vehicle_raptor_cannon_locktarget == 2)
+    {
+        if(raptor.gun1.lock_time < time || raptor.gun1.enemy.deadflag)
+            raptor.gun1.enemy = world;
+    
+        if(trace_ent)
+        if(trace_ent.movetype)
+        if(trace_ent.takedamage)
+        if(!trace_ent.deadflag)
+        {
+            if(teamplay)
+            {
+                if(trace_ent.team != player.team)
+                {
+                    raptor.gun1.enemy = trace_ent;
+                    raptor.gun1.lock_time = time + 5;
+                }
+            }
+            else
+            {            
+                raptor.gun1.enemy = trace_ent;
+                raptor.gun1.lock_time = time + 0.5;
+            }
+        }
+            
+        if(raptor.gun1.enemy)
+        {
+            float i, distance, impact_time;
+
+            vf = real_origin(raptor.gun1.enemy);
+            UpdateAuxiliaryXhair(player, vf, '1 0 0', 1);
+            vector _vel = raptor.gun1.enemy.velocity;
+            if(raptor.gun1.enemy.movetype == MOVETYPE_WALK)
+                _vel_z *= 0.1;
+            
+            if(autocvar_g_vehicle_raptor_cannon_predicttarget)
+            {
+                ad = vf;
+                for(i = 0; i < 4; ++i)
+                {
+                    distance = vlen(ad - player.origin);
+                    impact_time = distance / autocvar_g_vehicle_raptor_cannon_speed;
+                    ad = vf + _vel * impact_time;
+                }
+                trace_endpos = ad;                        
+            }
+            else
+                trace_endpos = vf;                        
+        }
+    }
+    else if(autocvar_g_vehicle_raptor_cannon_locktarget == 1)
     {
 
         vehicles_locktarget((1 / autocvar_g_vehicle_raptor_cannon_locking_time) * frametime,
@@ -535,13 +600,13 @@ float raptor_frame()
     }
 
     if(self.vehicle_flags  & VHF_SHIELDREGEN)
-        vehicles_regen(dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, TRUE);
+        vehicles_regen(raptor.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, frametime, TRUE);
 
     if(self.vehicle_flags  & VHF_HEALTHREGEN)
-        vehicles_regen(dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, FALSE);
+        vehicles_regen(raptor.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, frametime, FALSE);
 
     if(self.vehicle_flags  & VHF_ENERGYREGEN)
-        vehicles_regen(cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, FALSE);
+        vehicles_regen(raptor.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, frametime, FALSE);
 
     if(raptor.vehicle_weapon2mode == RSM_BOMB)
     {
@@ -610,10 +675,10 @@ float raptor_frame()
     }
     
 
-    VEHICLE_UPDATE_PLAYER(health, raptor);
-    VEHICLE_UPDATE_PLAYER(energy, raptor);
+    VEHICLE_UPDATE_PLAYER(player, health, raptor);
+    VEHICLE_UPDATE_PLAYER(player, energy, raptor);
     if(self.vehicle_flags & VHF_HASSHIELD)
-        VEHICLE_UPDATE_PLAYER(shield, raptor);
+        VEHICLE_UPDATE_PLAYER(player, shield, raptor);
 
     player.BUTTON_ATCK = player.BUTTON_ATCK2 = player.BUTTON_CROUCH = 0;
     
@@ -641,6 +706,9 @@ void raptor_blowup()
 
 void raptor_diethink()
 {
+       if(time >= self.wait)
+               self.think = raptor_blowup;
+    
     if(random() < 0.1)
     {
         sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
@@ -659,6 +727,7 @@ void raptor_die()
     self.movetype     = MOVETYPE_BOUNCE;
     self.think        = raptor_diethink;
     self.nextthink    = time;
+    self.wait            = time + 5 + (random() * 5);
     
     pointparticles(particleeffectnum("explosion_medium"), findbetterlocation (self.origin, 16), '0 0 0', 1);
 
@@ -677,26 +746,6 @@ void raptor_impact()
         vehilces_impact(autocvar_g_vehicle_raptor_bouncepain_x, autocvar_g_vehicle_raptor_bouncepain_y, autocvar_g_vehicle_raptor_bouncepain_z);
 }
 
-void raptor_spawn()
-{
-    self.frame          = 0;
-    self.vehicle_health = autocvar_g_vehicle_raptor_health;
-    self.vehicle_shield = autocvar_g_vehicle_raptor_shield;
-    self.movetype       = MOVETYPE_TOSS;
-    self.solid          = SOLID_SLIDEBOX;
-    self.vehicle_energy = 1;
-
-    self.bomb1.gun1.avelocity_y = 90;
-    self.bomb1.gun2.avelocity_y = -90;
-
-    setsize(self, RAPTOR_MIN, RAPTOR_MAX );
-    self.delay = time;
-        
-    self.bouncefactor = autocvar_g_vehicle_raptor_bouncefactor;
-    self.bouncestop = autocvar_g_vehicle_raptor_bouncestop;    
-    self.vehicle_impact = raptor_impact;    
-}
-
 // If we dont do this ever now and then, the raptors rotors
 // stop working, presumably due to angle overflow. cute.
 void raptor_rotor_anglefix()
@@ -705,6 +754,7 @@ void raptor_rotor_anglefix()
     self.gun2.angles_y = anglemods(self.gun2.angles_y);
     self.nextthink = time + 15;
 }
+
 float raptor_impulse(float _imp)
 {
     switch(_imp)
@@ -738,100 +788,105 @@ float raptor_impulse(float _imp)
     return FALSE;
 }
 
-void raptor_dinit()
+void raptor_spawn(float _f)
 {
-    entity spinner;
-    vector ofs;
-
-    if not (vehicle_initialize(
-             "Raptor",
-             "models/vehicles/raptor.dpm",
-             "",
-             "models/vehicles/raptor_cockpit.dpm",
-             "", "tag_hud", "tag_camera",
-             HUD_RAPTOR,
-             RAPTOR_MIN, RAPTOR_MAX,
-             FALSE,
-             raptor_spawn, autocvar_g_vehicle_raptor_respawntime,
-             raptor_frame,
-             raptor_enter, raptor_exit,
-             raptor_die,   raptor_think,
-             FALSE, 
-             autocvar_g_vehicle_raptor_health))
+    if(!self.gun1)
     {
-        remove(self);
-        return;
+        entity spinner;
+        vector ofs;
+
+        //FIXME: Camera is in a bad place in HUD model.
+        //setorigin(self.vehicle_viewport, '25 0 5');
+        
+        self.vehicles_impusle   = raptor_impulse;
+        
+        self.frame = 0;
+
+        self.bomb1 = spawn();
+        self.bomb2 = spawn();
+        self.gun1  = spawn();
+        self.gun2  = spawn();
+
+        setmodel(self.bomb1,"models/vehicles/clusterbomb_folded.md3");
+        setmodel(self.bomb2,"models/vehicles/clusterbomb_folded.md3");
+        setmodel(self.gun1, "models/vehicles/raptor_gun.dpm");
+        setmodel(self.gun2, "models/vehicles/raptor_gun.dpm");
+        setmodel(self.tur_head, "models/vehicles/raptor_body.dpm");
+
+        setattachment(self.bomb1, self, "bombmount_left");
+        setattachment(self.bomb2, self, "bombmount_right");
+        setattachment(self.tur_head, self,"root");
+
+        // FIXMODEL Guns mounts to angled bones
+        self.bomb1.angles = self.angles;
+        self.angles = '0 0 0';
+        // This messes up gun-aim, so work arround it.
+        //setattachment(self.gun1, self, "gunmount_left");
+        ofs = gettaginfo(self, gettagindex(self, "gunmount_left"));
+        ofs -= self.origin;
+        setattachment(self.gun1, self, "");
+        setorigin(self.gun1, ofs);
+
+        //setattachment(self.gun2, self, "gunmount_right");
+        ofs = gettaginfo(self, gettagindex(self, "gunmount_right"));
+        ofs -= self.origin;
+        setattachment(self.gun2, self, "");
+        setorigin(self.gun2, ofs);
+
+        self.angles = self.bomb1.angles;
+        self.bomb1.angles = '0 0 0';
+
+        spinner = spawn();
+        spinner.owner = self;
+        setmodel(spinner,"models/vehicles/spinner.dpm");
+        setattachment(spinner, self, "engine_left");
+        spinner.movetype = MOVETYPE_NOCLIP;
+        spinner.avelocity = '0 90 0';
+        self.bomb1.gun1 = spinner;
+
+        spinner = spawn();
+        spinner.owner = self;
+        setmodel(spinner,"models/vehicles/spinner.dpm");
+        setattachment(spinner, self, "engine_right");
+        spinner.movetype = MOVETYPE_NOCLIP;
+        spinner.avelocity = '0 -90 0';
+        self.bomb1.gun2 = spinner;
+
+        // Sigh.
+        self.bomb1.think = raptor_rotor_anglefix;
+        self.bomb1.nextthink = time;
+
+        self.mass               = 1 ;
     }
 
-    //FIXME: Camera is in a bad place in HUD model.
-    //setorigin(self.vehicle_viewport, '25 0 5');
-    
-    self.vehicles_impusle   = raptor_impulse;
-    
-    self.frame = 0;
-
-    self.bomb1 = spawn();
-    self.bomb2 = spawn();
-    self.gun1  = spawn();
-    self.gun2  = spawn();
-
-    setmodel(self.bomb1,"models/vehicles/clusterbomb_folded.md3");
-    setmodel(self.bomb2,"models/vehicles/clusterbomb_folded.md3");
-    setmodel(self.gun1, "models/vehicles/raptor_gun.dpm");
-    setmodel(self.gun2, "models/vehicles/raptor_gun.dpm");
-    setmodel(self.tur_head, "models/vehicles/raptor_body.dpm");
-
-    setattachment(self.bomb1, self,"bombmount_left");
-    setattachment(self.bomb2, self,"bombmount_right");
-    setattachment(self.tur_head, self,"root");
-
-
-    // FIXMODEL Guns mounts to angled bones
-    self.bomb1.angles = self.angles;
-    self.angles = '0 0 0';
-    // This messes up gun-aim, so work arround it.
-    //setattachment(self.gun1, self, "gunmount_left");
-    ofs = gettaginfo(self, gettagindex(self, "gunmount_left"));
-    ofs -= self.origin;
-    setattachment(self.gun1, self, "");
-    setorigin(self.gun1, ofs);
-
-    //setattachment(self.gun2, self, "gunmount_right");
-    ofs = gettaginfo(self, gettagindex(self, "gunmount_right"));
-    ofs -= self.origin;
-    setattachment(self.gun2, self, "");
-    setorigin(self.gun2, ofs);
-
-    self.angles = self.bomb1.angles;
-    self.bomb1.angles = '0 0 0';
-
-    spinner = spawn();
-    spinner.owner = self;
-    setmodel(spinner,"models/vehicles/spinner.dpm");
-    setattachment(spinner, self, "engine_left");
-    spinner.movetype = MOVETYPE_NOCLIP;
-    spinner.avelocity = '0 90 0';
-    self.bomb1.gun1 = spinner;
-
-    spinner = spawn();
-    spinner.owner = self;
-    setmodel(spinner,"models/vehicles/spinner.dpm");
-    setattachment(spinner, self, "engine_right");
-    spinner.movetype = MOVETYPE_NOCLIP;
-    spinner.avelocity = '0 -90 0';
-    self.bomb1.gun2 = spinner;
-
-    // Sigh.
-    self.bomb1.think = raptor_rotor_anglefix;
-    self.bomb1.nextthink = time;
-
-    self.mass               = 1 ;
+
+    self.frame          = 0;
+    self.vehicle_health = autocvar_g_vehicle_raptor_health;
+    self.vehicle_shield = autocvar_g_vehicle_raptor_shield;
+    self.movetype       = MOVETYPE_TOSS;
+    self.solid          = SOLID_SLIDEBOX;
+    self.vehicle_energy = 1;
+
+    self.bomb1.gun1.avelocity_y = 90;
+    self.bomb1.gun2.avelocity_y = -90;
+
+    setsize(self, RAPTOR_MIN, RAPTOR_MAX );
+    self.delay = time;
+        
+    self.bouncefactor = autocvar_g_vehicle_raptor_bouncefactor;
+    self.bouncestop = autocvar_g_vehicle_raptor_bouncestop;    
+    self.vehicle_impact = raptor_impact;    
+    self.damageforcescale = 0.25;
 }
 
 void spawnfunc_vehicle_raptor()
 {
-    vehicles_configcheck("vehicle_raptor.cfg", autocvar_g_vehicle_raptor_health);
-
+    if(!autocvar_g_vehicle_raptor)
+    {
+        remove(self);
+        return;
+    }        
+    
     self.vehicle_flags |= VHF_DMGSHAKE;
     self.vehicle_flags |= VHF_DMGROLL;
    
@@ -859,11 +914,27 @@ void spawnfunc_vehicle_raptor()
     precache_sound ("vehicles/raptor_speed.wav");
     precache_sound ("vehicles/missile_alarm.wav");
     
-    self.think = raptor_dinit;
+    if not (vehicle_initialize(
+             "Raptor",
+             "models/vehicles/raptor.dpm",
+             "",
+             "models/vehicles/raptor_cockpit.dpm",
+             "", "tag_hud", "tag_camera",
+             HUD_RAPTOR,
+             RAPTOR_MIN, RAPTOR_MAX,
+             FALSE,
+             raptor_spawn, autocvar_g_vehicle_raptor_respawntime,
+             raptor_frame,
+             raptor_enter, raptor_exit,
+             raptor_die,   raptor_think,
+             FALSE, 
+             autocvar_g_vehicle_raptor_health,
+             autocvar_g_vehicle_raptor_shield))
+    {
+        remove(self);
+        return;
+    }
+    
     
-    if(g_assault)
-        self.nextthink = time + 0.5;
-    else
-        self.nextthink = time + (autocvar_g_vehicles_delayspawn ? autocvar_g_vehicle_raptor_respawntime + (random() * autocvar_g_vehicles_delayspawn_jitter) : 0.5);
 }
 #endif // SVQC
index 42bd7902b654ef7685bd533cd587fb74aba077c5..71e57a037f88213efee91d2f8a7091607c3abfe9 100644 (file)
@@ -2,6 +2,8 @@ const vector SPIDERBOT_MIN = '-75 -75 10';
 const vector SPIDERBOT_MAX  = '75 75 125';
 
 #ifdef SVQC
+float autocvar_g_vehicle_spiderbot;
+
 float autocvar_g_vehicle_spiderbot_respawntime;
 
 float autocvar_g_vehicle_spiderbot_speed_stop;
@@ -62,7 +64,7 @@ vector autocvar_g_vehicle_spiderbot_bouncepain;
 
 void spiderbot_exit(float eject);
 void spiderbot_enter();
-void spiderbot_spawn();
+void spiderbot_spawn(float);
 #define SBRM_FIRST 0
 #define SBRM_VOLLY 0
 #define SBRM_GUIDE 1
@@ -255,7 +257,7 @@ void spiderbot_rocket_do()
             rocket = vehicles_projectile("spiderbot_rocket_launch", "weapons/rocket_fire.wav",
                                    v, normalize(randomvec() * autocvar_g_vehicle_spiderbot_rocket_spread + v_forward) * autocvar_g_vehicle_spiderbot_rocket_speed,
                                    autocvar_g_vehicle_spiderbot_rocket_damage, autocvar_g_vehicle_spiderbot_rocket_radius, autocvar_g_vehicle_spiderbot_rocket_force, 1,
-                                   DEATH_SBROCKET, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, FALSE, TRUE);
+                                   DEATH_SBROCKET, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, FALSE, TRUE, self.owner);
             crosshair_trace(self.owner);
             float _dist = (random() * autocvar_g_vehicle_spiderbot_rocket_radius) + vlen(v - trace_endpos);
             _dist -= (random() * autocvar_g_vehicle_spiderbot_rocket_radius) ;
@@ -269,7 +271,7 @@ void spiderbot_rocket_do()
             rocket = vehicles_projectile("spiderbot_rocket_launch", "weapons/rocket_fire.wav",
                                    v, normalize(v_forward) * autocvar_g_vehicle_spiderbot_rocket_speed,
                                    autocvar_g_vehicle_spiderbot_rocket_damage, autocvar_g_vehicle_spiderbot_rocket_radius, autocvar_g_vehicle_spiderbot_rocket_force, 1,
-                                   DEATH_SBROCKET, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, FALSE, FALSE);
+                                   DEATH_SBROCKET, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, FALSE, FALSE, self.owner);
             crosshair_trace(self.owner);
             rocket.pos1       = trace_endpos;
             rocket.nextthink  = time;
@@ -281,14 +283,22 @@ void spiderbot_rocket_do()
             rocket = vehicles_projectile("spiderbot_rocket_launch", "weapons/rocket_fire.wav",
                                    v, normalize(v_forward) * autocvar_g_vehicle_spiderbot_rocket_speed,
                                    autocvar_g_vehicle_spiderbot_rocket_damage, autocvar_g_vehicle_spiderbot_rocket_radius, autocvar_g_vehicle_spiderbot_rocket_force, 1,
-                                   DEATH_SBROCKET, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, FALSE, TRUE);
+                                   DEATH_SBROCKET, PROJECTILE_SPIDERROCKET, autocvar_g_vehicle_spiderbot_rocket_health, FALSE, TRUE, self.owner);
             
             crosshair_trace(self.owner);
+            vector _ct_end = trace_endpos + trace_plane_normal;
+            
             rocket.pos1       = trace_endpos + randomvec() * (0.75 * autocvar_g_vehicle_spiderbot_rocket_radius);
             rocket.pos1_z       = trace_endpos_z;
-            traceline(v, v + '0 0 1' * MAX_SHOT_DISTANCE, MOVE_WORLDONLY, self);     
             
-            rocket.velocity  = spiberbot_calcartillery(v, rocket.pos1, (0.75 * vlen(v - trace_endpos)));
+            traceline(v, v + '0 0 1' * MAX_SHOT_DISTANCE, MOVE_WORLDONLY, self);             
+            float h1 = 0.75 * vlen(v - trace_endpos);
+            
+            //v = trace_endpos;
+            traceline(v , rocket.pos1 + '0 0 1' * MAX_SHOT_DISTANCE, MOVE_WORLDONLY, self); 
+            float h2 = 0.75 * vlen(rocket.pos1 - v);
+            
+            rocket.velocity  = spiberbot_calcartillery(v, rocket.pos1, ((h1 < h2) ? h1 : h2));
             rocket.movetype  = MOVETYPE_TOSS;            
             rocket.gravity   = 1;
             //rocket.think     = spiderbot_rocket_artillery;   
@@ -307,6 +317,11 @@ void spiderbot_rocket_do()
     self.gun2.cnt = time + self.attack_finished_single;
 }
 
+float spiderbot_aiframe()
+{
+    return FALSE;
+}
+
 float spiderbot_frame()
 {
     vector ad, vf;
@@ -325,11 +340,6 @@ float spiderbot_frame()
     player.BUTTON_ZOOM      = 0;
     player.BUTTON_CROUCH    = 0;
     player.switchweapon     = 0;
-
-    if(player.impulse == 12)
-    {
-        dprint("WOOOOOOOOOOOtotototototOOOOOOOOOOOOttt\n");
-    }
     
 
 #if 1 // 0 to enable per-gun impact aux crosshairs
@@ -514,7 +524,7 @@ float spiderbot_frame()
         }
     }
     else
-        vehicles_regen(cnt, vehicle_ammo1, autocvar_g_vehicle_spiderbot_minigun_ammo_max,
+        vehicles_regen(spider.cnt, vehicle_ammo1, autocvar_g_vehicle_spiderbot_minigun_ammo_max,
                                            autocvar_g_vehicle_spiderbot_minigun_ammo_regen_pause,
                                            autocvar_g_vehicle_spiderbot_minigun_ammo_regen, frametime, FALSE);
         
@@ -522,10 +532,10 @@ float spiderbot_frame()
     spiderbot_rocket_do();
 
     if(self.vehicle_flags  & VHF_SHIELDREGEN)
-        vehicles_regen(dmg_time, vehicle_shield, autocvar_g_vehicle_spiderbot_shield, autocvar_g_vehicle_spiderbot_shield_regen_pause, autocvar_g_vehicle_spiderbot_shield_regen, frametime, TRUE);
+        vehicles_regen(spider.dmg_time, vehicle_shield, autocvar_g_vehicle_spiderbot_shield, autocvar_g_vehicle_spiderbot_shield_regen_pause, autocvar_g_vehicle_spiderbot_shield_regen, frametime, TRUE);
 
     if(self.vehicle_flags  & VHF_HEALTHREGEN)
-        vehicles_regen(dmg_time, vehicle_health, autocvar_g_vehicle_spiderbot_health, autocvar_g_vehicle_spiderbot_health_regen_pause, autocvar_g_vehicle_spiderbot_health_regen, frametime, FALSE);
+        vehicles_regen(spider.dmg_time, vehicle_health, autocvar_g_vehicle_spiderbot_health, autocvar_g_vehicle_spiderbot_health_regen_pause, autocvar_g_vehicle_spiderbot_health_regen, frametime, FALSE);
 
     player.BUTTON_ATCK = player.BUTTON_ATCK2 = 0;
     player.vehicle_ammo2 = spider.tur_head.frame;
@@ -538,10 +548,10 @@ float spiderbot_frame()
     setorigin(player, spider.origin + '0 0 1' * SPIDERBOT_MAX_z);
     player.velocity = spider.velocity;
 
-    VEHICLE_UPDATE_PLAYER(health, spiderbot);
+    VEHICLE_UPDATE_PLAYER(player, health, spiderbot);
 
     if(self.vehicle_flags & VHF_HASSHIELD)
-        VEHICLE_UPDATE_PLAYER(shield, spiderbot);
+        VEHICLE_UPDATE_PLAYER(player, shield, spiderbot);
 
     self = player;
     return 1;    
@@ -556,6 +566,7 @@ void spiderbot_think()
 
 void spiderbot_enter()
 {
+    self.vehicle_weapon2mode = SBRM_GUIDE;
     self.movetype   = MOVETYPE_WALK;
     CSQCVehicleSetup(self.owner, 0);
     self.owner.vehicle_health = (self.vehicle_health / autocvar_g_vehicle_spiderbot_health) * 100;
@@ -584,7 +595,7 @@ void spiderbot_exit(float eject)
         e = e.chain;
     }
 
-    self.velocity   = '0 0 0';
+    //self.velocity   = '0 0 0';
     self.think      = spiderbot_think;
     self.nextthink  = time;
     self.frame      = 5;
@@ -604,37 +615,33 @@ void spiderbot_exit(float eject)
        }
        else
        {
-           self.owner.velocity = normalize(self.velocity) * autocvar_sv_maxairspeed;
-           self.owner.oldvelocity = self.owner.velocity;           
-           spot = self.origin - v_forward * 200 + '0 0 64';
-           spot = vehicles_findgoodexit(spot);
+               if(vlen(self.velocity) > autocvar_g_vehicle_spiderbot_speed_strafe)
+               {
+                       self.owner.velocity = normalize(self.velocity) * vlen(self.velocity);
+                       self.owner.velocity_z += 200;
+                       spot = self.origin + v_forward * 128 + '0 0 64';
+                       spot = vehicles_findgoodexit(spot);
+               }
+               else
+               {
+                       self.owner.velocity = self.velocity * 0.5;
+                       self.owner.velocity_z += 10;
+                       spot = self.origin + v_forward * 256 + '0 0 64';
+                       spot = vehicles_findgoodexit(spot);
+               }
+           self.owner.oldvelocity = self.owner.velocity;
            setorigin(self.owner , spot);
        }
-    antilag_clear(self.owner);
+       
+       antilag_clear(self.owner);
     self.owner = world;
 }
+
 void spider_impact()
 {
     if(autocvar_g_vehicle_spiderbot_bouncepain_x)
         vehilces_impact(autocvar_g_vehicle_spiderbot_bouncepain_x, autocvar_g_vehicle_spiderbot_bouncepain_y, autocvar_g_vehicle_spiderbot_bouncepain_z);    
 }
-void spiderbot_spawn()
-{
-    self.frame              = 5;
-    self.tur_head.frame     = 1;
-    self.think              = spiderbot_think;
-    self.nextthink          = time;
-    self.vehicle_health     = autocvar_g_vehicle_spiderbot_health;
-    self.vehicle_shield     = autocvar_g_vehicle_spiderbot_shield;
-    self.movetype           = MOVETYPE_WALK;
-    self.solid              = SOLID_SLIDEBOX;
-    self.alpha              = self.tur_head.alpha = self.gun1.alpha = self.gun2.alpha = 1;
-    self.tur_head.angles    = '0 0 0';
-
-    setorigin(self, self.pos1 + '0 0 128');
-    self.angles = self.pos2;
-    self.vehicle_impact = spider_impact;
-}
 
 void spiderbot_headfade()
 {
@@ -778,46 +785,46 @@ float spiderbot_impulse(float _imp)
     return FALSE;
 }
 
-void vewhicle_spiderbot_dinit()
+void spiderbot_spawn(float _f)
 {
-    if not (vehicle_initialize(
-             "Spiderbot",
-             "models/vehicles/spiderbot.dpm",
-             "models/vehicles/spiderbot_top.dpm",
-             "models/vehicles/spiderbot_cockpit.dpm",
-             "tag_head", "tag_hud", "",
-             HUD_SPIDERBOT,
-             SPIDERBOT_MIN, SPIDERBOT_MAX,
-             FALSE,
-             spiderbot_spawn, autocvar_g_vehicle_spiderbot_respawntime,
-             spiderbot_frame,
-             spiderbot_enter, spiderbot_exit,
-             spiderbot_die,   spiderbot_think,
-             FALSE, 
-             autocvar_g_vehicle_spiderbot_health))
-    {
-        remove(self);
-        return;
+    if(!self.gun1)
+    {        
+        self.vehicles_impusle   = spiderbot_impulse;
+        self.gun1               = spawn();
+        self.gun2               = spawn();    
+        setmodel(self.gun1, "models/vehicles/spiderbot_barrels.dpm");
+        setmodel(self.gun2, "models/vehicles/spiderbot_barrels.dpm");
+        setattachment(self.gun1, self.tur_head, "tag_hardpoint01");
+        setattachment(self.gun2, self.tur_head, "tag_hardpoint02");
+        self.gravity            = 2;
+        self.mass               = 5000;
     }
 
+    self.frame              = 5;
+    self.tur_head.frame     = 1;
+    self.think              = spiderbot_think;
+    self.nextthink          = time;
+    self.vehicle_health     = autocvar_g_vehicle_spiderbot_health;
+    self.vehicle_shield     = autocvar_g_vehicle_spiderbot_shield;
+    self.movetype           = MOVETYPE_WALK;
+    self.solid              = SOLID_SLIDEBOX;
+    self.alpha              = self.tur_head.alpha = self.gun1.alpha = self.gun2.alpha = 1;
+    self.tur_head.angles    = '0 0 0';    
 
-    self.gun1               = spawn();
-    self.gun2               = spawn();
-    
-    self.vehicles_impusle   = spiderbot_impulse;
-    
-    setmodel(self.gun1, "models/vehicles/spiderbot_barrels.dpm");
-    setmodel(self.gun2, "models/vehicles/spiderbot_barrels.dpm");
-
-    setattachment(self.gun1, self.tur_head, "tag_hardpoint01");
-    setattachment(self.gun2, self.tur_head, "tag_hardpoint02");
-
-    self.gravity            = 2;
-    self.mass               = 5000;
+    setorigin(self, self.pos1 + '0 0 128');
+    self.angles = self.pos2;
+    self.vehicle_impact = spider_impact;
+    self.damageforcescale = 0.03;
 }
 
 void spawnfunc_vehicle_spiderbot()
 {
+    if(!autocvar_g_vehicle_spiderbot)
+    {
+        remove(self);
+        return;
+    }        
+
     self.vehicle_flags |= VHF_DMGSHAKE;
     //self.vehicle_flags |= VHF_DMGROLL;
     //self.vehicle_flags |= VHF_DMGHEADROLL;
@@ -839,7 +846,6 @@ void spawnfunc_vehicle_spiderbot()
     precache_sound ( "vehicles/spiderbot_walk.wav");
     precache_sound ( "vehicles/spiderbot_land.wav");
 
-    vehicles_configcheck("vehicle_spiderbot.cfg", autocvar_g_vehicle_spiderbot_health);
     if(autocvar_g_vehicle_spiderbot_shield)
         self.vehicle_flags |= VHF_HASSHIELD;
 
@@ -848,12 +854,26 @@ void spawnfunc_vehicle_spiderbot()
 
     if(autocvar_g_vehicle_spiderbot_health_regen)
         self.vehicle_flags |= VHF_HEALTHREGEN;
-    
-    self.think = vewhicle_spiderbot_dinit;
-    
-    if(g_assault)
-        self.nextthink = time + 0.5;
-    else
-        self.nextthink = time + (autocvar_g_vehicles_delayspawn ? autocvar_g_vehicle_spiderbot_respawntime + (random() * autocvar_g_vehicles_delayspawn_jitter) : 0.5);
+        
+    if not (vehicle_initialize(
+             "Spiderbot",
+             "models/vehicles/spiderbot.dpm",
+             "models/vehicles/spiderbot_top.dpm",
+             "models/vehicles/spiderbot_cockpit.dpm",
+             "tag_head", "tag_hud", "",
+             HUD_SPIDERBOT,
+             SPIDERBOT_MIN, SPIDERBOT_MAX,
+             FALSE,
+             spiderbot_spawn, autocvar_g_vehicle_spiderbot_respawntime,
+             spiderbot_frame,
+             spiderbot_enter, spiderbot_exit,
+             spiderbot_die,   spiderbot_think,
+             FALSE, 
+             autocvar_g_vehicle_spiderbot_health,
+             autocvar_g_vehicle_spiderbot_shield))
+    {
+        remove(self);
+        return;
+    }
 }
 #endif // SVQC
index 2f6a191bf57b222764ff781291c0c99cf9cbada3..16a339e09f40380dec3e1f84cfb5512d63252d5f 100644 (file)
@@ -2,13 +2,14 @@ float autocvar_g_vehicles_crush_dmg;
 float autocvar_g_vehicles_crush_force;
 float autocvar_g_vehicles_delayspawn;
 float autocvar_g_vehicles_delayspawn_jitter;
-float autocvar_g_vehicles_allow_flagcarry;
 
-float autocvar_g_vehicles_nex_damagerate = 0.5;
-float autocvar_g_vehicles_uzi_damagerate = 0.5;
-float autocvar_g_vehicles_rifle_damagerate = 0.75;
-float autocvar_g_vehicles_minstanex_damagerate = 0.001;
+var float autocvar_g_vehicles_nex_damagerate = 0.5;
+var float autocvar_g_vehicles_uzi_damagerate = 0.5;
+var float autocvar_g_vehicles_rifle_damagerate = 0.75;
+var float autocvar_g_vehicles_minstanex_damagerate = 0.001;
+var float autocvar_g_vehicles_tag_damagerate = 5;
 
+float autocvar_g_vehicles;
 
 void vehicles_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force);
 void vehicles_return();
@@ -45,6 +46,9 @@ float SendAuxiliaryXhair(entity to, float sf)
 
 void UpdateAuxiliaryXhair(entity own, vector loc, vector clr, float axh_id)
 {
+    if (clienttype(own) != CLIENTTYPE_REAL)
+        return;
+
     entity axh;
 
     axh_id = bound(0, axh_id, MAX_AXH);
@@ -70,7 +74,7 @@ void UpdateAuxiliaryXhair(entity own, vector loc, vector clr, float axh_id)
 // WriteByte(MSG_ONE, SVC_TEMPENTITY) uses reliable messagess, never use for thinsg that need continous updates.
 void SendAuxiliaryXhair2(entity own, vector loc, vector clr, float axh_id)
 {
-       msg_entity = own;
+       msgexntity = own;
 
        WriteByte(MSG_ONE, SVC_TEMPENTITY);
        WriteByte(MSG_ONE, TE_CSQC_AUXILIARYXHAIR);
@@ -97,6 +101,9 @@ void SendAuxiliaryXhair2(entity own, vector loc, vector clr, float axh_id)
 **/
 void CSQCVehicleSetup(entity own, float vehicle_id)
 {
+    if (clienttype(own) != CLIENTTYPE_REAL)
+        return;
+       
        msg_entity = own;
 
        WriteByte(MSG_ONE, SVC_TEMPENTITY);
@@ -312,8 +319,8 @@ void vehicles_locktarget(float incr, float decr, float _lock_time)
     }
 }
 
-#define VEHICLE_UPDATE_PLAYER(fld,vhname) \
-self.owner.vehicle_##fld = (self.vehicle_##fld / autocvar_g_vehicle_##vhname##_##fld) * 100
+#define VEHICLE_UPDATE_PLAYER(ply,fld,vhname) \
+ply.vehicle_##fld = (self.vehicle_##fld / autocvar_g_vehicle_##vhname##_##fld) * 100
 
 #define vehicles_sweap_collision(orig,vel,dt,acm,mult) \
 traceline(orig, orig + vel * dt, MOVE_NORMAL, self); \
@@ -399,7 +406,7 @@ entity vehicles_projectile(string _mzlfx, string _mzlsound,
                            vector _org, vector _vel,
                            float _dmg, float _radi, float _force,  float _size,
                            float _deahtype, float _projtype, float _health,
-                           float _cull, float _clianim)
+                           float _cull, float _clianim, entity _owner)
 {
     entity proj;
 
@@ -421,7 +428,7 @@ entity vehicles_projectile(string _mzlfx, string _mzlsound,
     proj.touch            = vehicles_projectile_explode;
     proj.use              = vehicles_projectile_explode;
     proj.owner            = self;
-    proj.realowner        = self.owner;
+    proj.realowner        = _owner;
     proj.think            = SUB_Remove;
     proj.nextthink        = time + 30;
 
@@ -464,6 +471,7 @@ void vehicles_spawn()
     self.touch              = vehicles_touch;
     self.event_damage       = vehicles_damage;
     self.iscreature         = TRUE;
+    self.teleportable       = FALSE; // no teleporting for vehicles, too buggy
     self.damagedbycontents     = TRUE;
     self.movetype           = MOVETYPE_WALK;
     self.solid              = SOLID_SLIDEBOX;
@@ -484,9 +492,12 @@ void vehicles_spawn()
     setorigin(self, self.pos1 + '0 0 0');
     // Show it
     pointparticles(particleeffectnum("teleport"), self.origin + '0 0 64', '0 0 0', 1);
-
+    
+    if(self.vehicle_controller)
+        self.team = self.vehicle_controller.team;
+       
     vehicles_reset_colors();
-    self.vehicle_spawn();
+    self.vehicle_spawn(VHSF_NORMAL);
 }
 
 // Better way of determening whats crushable needed! (fl_crushable?)
@@ -554,18 +565,18 @@ void vehicles_touch()
     if(other.vehicle != world)
         return;
 
-    // Remove this when bots know how to use vehicles.
-    if (clienttype(other) != CLIENTTYPE_REAL)
-        return;
-
     vehicles_enter();
 }
-
+var float autocvar_g_vehicles_allow_bots = 0;
 void vehicles_enter()
 {
    // Remove this when bots know how to use vehicles
-    if (clienttype(other) != CLIENTTYPE_REAL)
-        return;
+   
+    if (clienttype(other) == CLIENTTYPE_BOT)    
+        if (autocvar_g_vehicles_allow_bots)
+            dprint("Bot enters vehicle\n"); // This is where we need to disconnect (some, all?) normal bot AI and hand over to vehicle's _aiframe()
+        else
+            return;
 
     if(self.phase > time)
         return;
@@ -622,40 +633,37 @@ void vehicles_enter()
 
     self.team                 = self.owner.team;
     self.flags               -= FL_NOTARGET;
-
-    msg_entity = other;
-    WriteByte (MSG_ONE, SVC_SETVIEWPORT);
-    WriteEntity(MSG_ONE, self.vehicle_viewport);
-
-    WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
-    if(self.tur_head)
-    {
-        WriteAngle(MSG_ONE, self.tur_head.angles_x + self.angles_x); // tilt
-        WriteAngle(MSG_ONE, self.tur_head.angles_y + self.angles_y); // yaw
-        WriteAngle(MSG_ONE, 0);                                      // roll
-    }
-    else
+    
+    if (clienttype(other) == CLIENTTYPE_REAL)
     {
-        WriteAngle(MSG_ONE,  self.angles_x * -1); // tilt
-        WriteAngle(MSG_ONE,  self.angles_y);      // yaw
-        WriteAngle(MSG_ONE,  0);                  // roll
+        msg_entity = other;
+        WriteByte (MSG_ONE, SVC_SETVIEWPORT);
+        WriteEntity(MSG_ONE, self.vehicle_viewport);
+                
+        WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
+        if(self.tur_head)
+        {
+            WriteAngle(MSG_ONE, self.tur_head.angles_x + self.angles_x); // tilt
+            WriteAngle(MSG_ONE, self.tur_head.angles_y + self.angles_y); // yaw
+            WriteAngle(MSG_ONE, 0);                                      // roll
+        }
+        else
+        {
+            WriteAngle(MSG_ONE,  self.angles_x * -1); // tilt
+            WriteAngle(MSG_ONE,  self.angles_y);      // yaw
+            WriteAngle(MSG_ONE,  0);                  // roll
+        }
     }
 
     vehicles_clearrturn();
 
     CSQCVehicleSetup(self.owner, self.hud);
-
-    if(other.flagcarried)
-    {
-        if(!autocvar_g_vehicles_allow_flagcarry)
-            DropFlag(other.flagcarried, world, world);
-        else
-        {
-            other.flagcarried.scale = 1;
-            setattachment(other.flagcarried, self, "");
-            setorigin(other, '0 0 96');
-        }
-    }
+    
+    vh_player = other;
+    vh_vehicle = self;
+    MUTATOR_CALLHOOK(VehicleEnter);
+    other = vh_player;
+    self = vh_vehicle;
 
     self.vehicle_enter();
     antilag_clear(other);
@@ -675,7 +683,7 @@ vector vehicles_findgoodexit(vector prefer_spot)
     if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid)
         return prefer_spot;
 
-    mysize = vlen(self.maxs - self.mins);
+    mysize = 1.5 * vlen(self.maxs - self.mins);
     float i;
     vector v, v2;
     v2 = 0.5 * (self.absmin + self.absmax);
@@ -718,81 +726,107 @@ vector vehicles_findgoodexit(vector prefer_spot)
     Standarrd vehicle release fucntion.
     custom code goes in self.vehicle_exit
 **/
+float vehicles_exit_running;
 void vehicles_exit(float eject)
 {
-    entity oldself;
-    if(self.flags & FL_CLIENT)
+    entity _vehicle;
+    entity _player;
+    entity _oldself = self;
+    
+    if(vehicles_exit_running)
     {
-        oldself = self;
-        self = self.vehicle;
+        dprint("^1vehicles_exit allready running! this is not good..\n");
+        return;
     }
-
-       self.flags |= FL_NOTARGET;
-
-    if (self.owner)
+    
+    vehicles_exit_running = TRUE;
+    if(self.flags & FL_CLIENT)
     {
-        msg_entity = self.owner;
-        WriteByte (MSG_ONE, SVC_SETVIEWPORT);
-        WriteEntity( MSG_ONE, self.owner);
-
-        WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
-        WriteAngle(MSG_ONE, 0);                 // pich
-        WriteAngle(MSG_ONE, self.angles_y);     // yaw
-        WriteAngle(MSG_ONE, 0);                 // roll
-
-        setsize(self.owner, PL_MIN,PL_MAX);
-
-        self.owner.takedamage     = DAMAGE_AIM;
-        self.owner.solid          = SOLID_SLIDEBOX;
-        self.owner.movetype       = MOVETYPE_WALK;
-        self.owner.effects        &~= EF_NODRAW;
-        self.owner.alpha          = 1;
-        self.owner.PlayerPhysplug = SUB_Null;
-        self.owner.vehicle        = world;
-        self.owner.view_ofs       = PL_VIEW_OFS;
-        self.owner.event_damage   = PlayerDamage;
-        self.owner.hud            = HUD_NORMAL;
-        self.owner.switchweapon   = self.switchweapon;
-        //self.owner.BUTTON_USE     = 0;
-
-        CSQCVehicleSetup(self.owner, HUD_NORMAL);
+        _vehicle = self.vehicle;
+            
+        if (_vehicle.vehicle_flags & VHF_PLAYERSLOT)
+        {
+            _vehicle.vehicle_exit(eject);
+            self = _oldself;
+            vehicles_exit_running = FALSE;
+            return;            
+        }
     }
-
-    if(self.deadflag == DEAD_NO)
-        self.avelocity          = '0 0 0';
-
-    self.vehicle_hudmodel.viewmodelforclient = self;
-       self.tur_head.nodrawtoclient             = world;
-    vehicles_setreturn();
-
-    self.phase = time + 1;
-
-    if(!teamplay)
-        self.team = 0;
     else
-        self.team = self.tur_head.team;
+        _vehicle = self;
+    
+    _player = _vehicle.owner;
+    
+    self = _vehicle;
 
-    if(self.owner.flagcarried)
+    if (_player)
     {
-        self.owner.flagcarried.scale = 0.6;
-        setattachment(self.owner.flagcarried, self.owner, "");
-        setorigin(self.owner.flagcarried, FLAG_CARRY_POS);
+        if (clienttype(_player) == CLIENTTYPE_REAL)
+        {
+            msg_entity = _player;
+            WriteByte (MSG_ONE, SVC_SETVIEWPORT);
+            WriteEntity( MSG_ONE, _player);
+
+            WriteByte (MSG_ONE, SVC_SETVIEWANGLES);
+            WriteAngle(MSG_ONE, 0);
+            WriteAngle(MSG_ONE, _vehicle.angles_y);
+            WriteAngle(MSG_ONE, 0);
+        }
+        
+        setsize(_player, PL_MIN,PL_MAX);
+
+        _player.takedamage     = DAMAGE_AIM;
+        _player.solid          = SOLID_SLIDEBOX;
+        _player.movetype       = MOVETYPE_WALK;
+        _player.effects        &~= EF_NODRAW;
+        _player.alpha          = 1;
+        _player.PlayerPhysplug = SUB_Null;
+        _player.vehicle        = world;
+        _player.view_ofs       = PL_VIEW_OFS;
+        _player.event_damage   = PlayerDamage;
+        _player.hud            = HUD_NORMAL;
+        _player.switchweapon   = _vehicle.switchweapon;
+
+        CSQCVehicleSetup(_player, HUD_NORMAL);
     }
+    _vehicle.flags |= FL_NOTARGET;
+    
+    if(_vehicle.deadflag == DEAD_NO)
+        _vehicle.avelocity          = '0 0 0';
+    
+    _vehicle.tur_head.nodrawtoclient             = world;
+    
+    if(!teamplay)
+        _vehicle.team = 0;
+    else
 
-    sound (self, CH_TRIGGER_SINGLE, "misc/null.wav", 1, ATTN_NORM);
-    self.vehicle_exit(eject);
-    self.owner = world;
-    vehicles_reset_colors();
+    vh_player = _player;
+    vh_vehicle = _vehicle;
+    MUTATOR_CALLHOOK(VehicleExit);
+    _player = vh_player;
+    _vehicle = vh_vehicle;
 
-    if(oldself)
-        self = oldself;
+    _vehicle.team = _vehicle.tur_head.team;
+        
+    sound (_vehicle, CH_TRIGGER_SINGLE, "misc/null.wav", 1, ATTN_NORM);
+    _vehicle.vehicle_hudmodel.viewmodelforclient = _vehicle;   
+    _vehicle.phase = time + 1;
+    
+    _vehicle.vehicle_exit(eject);
+    
+    vehicles_setreturn();
+    vehicles_reset_colors();        
+    _vehicle.owner = world;
+    self = _oldself;
+    
+    vehicles_exit_running = FALSE;
 }
 
 
-void vehicles_regen(.float timer, .float regen_field, float field_max, float rpause, float regen, float delta_time, float _healthscale)
+void vehicles_regen(float timer, .float regen_field, float field_max, float rpause, float regen, float delta_time, float _healthscale)
 {
     if(self.regen_field < field_max)
-    if(self.timer + rpause < time)
+    if(timer + rpause < time)
     {
         if(_healthscale)
             regen = regen * (self.vehicle_health / self.tur_health);
@@ -811,6 +845,7 @@ void shieldhit_think()
     {
         //setmodel(self, "");
         self.alpha = -1;
+        self.effects |= EF_NODRAW;
     }
     else
     {
@@ -855,6 +890,9 @@ void vehicles_damage(entity inflictor, entity attacker, float damage, float deat
         
     if(DEATH_ISWEAPON(deathtype, WEP_MINSTANEX))
         damage *= autocvar_g_vehicles_minstanex_damagerate;
+
+    if(DEATH_ISWEAPON(deathtype, WEP_SEEKER))
+        damage *= autocvar_g_vehicles_tag_damagerate;
     
     self.enemy = attacker;
     
@@ -876,6 +914,7 @@ void vehicles_damage(entity inflictor, entity attacker, float damage, float deat
         self.vehicle_shieldent.alpha       = 0.45;
         self.vehicle_shieldent.angles      = vectoangles(normalize(hitloc - (self.origin + self.vehicle_shieldent.origin))) - self.angles;
         self.vehicle_shieldent.nextthink   = time;
+        self.vehicle_shieldent.effects &~= EF_NODRAW;
 
         self.vehicle_shield -= damage;
 
@@ -901,8 +940,11 @@ void vehicles_damage(entity inflictor, entity attacker, float damage, float deat
         if(sound_allowed(MSG_BROADCAST, attacker))
             spamsound (self, CH_PAIN, "onslaught/ons_hit2.wav", VOL_BASE, ATTN_NORM);  // FIXME: PLACEHOLDER
     }
-
-    self.velocity += force; // * (vlen(force) / self.mass);
+    
+       if(self.damageforcescale < 1 && self.damageforcescale > 0)
+               self.velocity += force * self.damageforcescale;
+       else
+               self.velocity += force;
 
     if(self.vehicle_health <= 0)
     {
@@ -912,6 +954,9 @@ void vehicles_damage(entity inflictor, entity attacker, float damage, float deat
             else
                 vehicles_exit(VHEF_RELESE);
 
+
+        antilag_clear(self);
+
         self.vehicle_die();
         vehicles_setreturn();
     }
@@ -1031,12 +1076,6 @@ void vehicles_setreturn()
 
 }
 
-void vehicles_configcheck(string  configname, float check_cvar)
-{
-    if(check_cvar == 0)
-        localcmd(strcat("exec ", configname, "\n"));
-}
-
 void vehicles_reset_colors()
 {
     entity e;
@@ -1082,6 +1121,57 @@ void vehicles_reset_colors()
     self.effects   = _effects;
 }
 
+void vehicle_use()
+{
+    dprint("vehicle ",self.netname, " used by ", activator.classname, "\n");
+
+    self.tur_head.team = activator.team;
+
+    if(self.tur_head.team == 0)
+        self.active = ACTIVE_NOT;
+    else
+        self.active = ACTIVE_ACTIVE;
+    
+    if(self.active == ACTIVE_ACTIVE && self.deadflag == DEAD_NO)
+    {
+        dprint("^3Eat shit yall!\n");
+        vehicles_setreturn();
+        vehicles_reset_colors();
+    }
+    else if(self.active == ACTIVE_NOT && self.deadflag != DEAD_NO)
+    {
+        
+    }
+}
+
+float vehicle_addplayerslot(    entity _owner, 
+                                entity _slot, 
+                                float _hud, 
+                                string _hud_model,
+                                float() _framefunc, 
+                                void(float) _exitfunc)
+{
+    if not (_owner.vehicle_flags & VHF_MULTISLOT)
+        _owner.vehicle_flags |= VHF_MULTISLOT;
+
+    _slot.PlayerPhysplug = _framefunc;
+    _slot.vehicle_exit = _exitfunc;
+    _slot.hud = _hud;
+    _slot.vehicle_flags = VHF_PLAYERSLOT;
+    _slot.vehicle_viewport = spawn();
+    _slot.vehicle_hudmodel = spawn();
+    _slot.vehicle_hudmodel.viewmodelforclient = _slot;
+    _slot.vehicle_viewport.effects = (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NOGUNBOB | EF_NOSHADOW | EF_LOWPRECISION | EF_SELECTABLE | EF_TELEPORT_BIT);
+    
+    setmodel(_slot.vehicle_hudmodel, _hud_model);
+    setmodel(_slot.vehicle_viewport, "null");
+    
+    setattachment(_slot.vehicle_hudmodel, _slot, "");
+    setattachment(_slot.vehicle_viewport, _slot.vehicle_hudmodel, "");
+    
+    return TRUE;
+}
+
 float vehicle_initialize(string  net_name,
                          string  bodymodel,
                          string  topmodel,
@@ -1093,7 +1183,7 @@ float vehicle_initialize(string  net_name,
                          vector  min_s,
                          vector  max_s,
                          float   nodrop,
-                         void()  spawnproc,
+                         void(float _spawnflag)  spawnproc,
                          float   _respawntime,
                          float() physproc,
                          void()  enterproc,
@@ -1101,17 +1191,47 @@ float vehicle_initialize(string  net_name,
                          void() dieproc,
                          void() thinkproc,
                          float  use_csqc,
-                         float _max_health)
+                         float _max_health,
+                         float _max_shield)
 {
+       if(!autocvar_g_vehicles)
+               return FALSE;
+       
+    if(self.targetname)
+    {
+        self.vehicle_controller = find(world, target, self.targetname);
+        if(!self.vehicle_controller)
+        {
+            bprint("^1WARNING: ^7Vehicle with invalid .targetname\n");
+        }
+        else
+        {
+            self.team = self.vehicle_controller.team;        
+            self.use = vehicle_use;
+            
+            if(teamplay)
+            {
+                if(self.vehicle_controller.team == 0)
+                    self.active = ACTIVE_NOT;
+                else
+                    self.active = ACTIVE_ACTIVE;                
+            }
+        }
+    }
+    
+    precache_sound("onslaught/ons_hit2.wav");
+    precache_sound("onslaught/electricity_explode.wav");
+
+
     addstat(STAT_HUD, AS_INT,  hud);
        addstat(STAT_VEHICLESTAT_HEALTH,  AS_INT, vehicle_health);
        addstat(STAT_VEHICLESTAT_SHIELD,  AS_INT, vehicle_shield);
        addstat(STAT_VEHICLESTAT_ENERGY,  AS_INT, vehicle_energy);
 
-       addstat(STAT_VEHICLESTAT_AMMO1,   AS_INT,   vehicle_ammo1);
+       addstat(STAT_VEHICLESTAT_AMMO1,   AS_INT, vehicle_ammo1);
        addstat(STAT_VEHICLESTAT_RELOAD1, AS_INT, vehicle_reload1);
 
-       addstat(STAT_VEHICLESTAT_AMMO2,   AS_INT,   vehicle_ammo2);
+       addstat(STAT_VEHICLESTAT_AMMO2,   AS_INT, vehicle_ammo2);
        addstat(STAT_VEHICLESTAT_RELOAD2, AS_INT, vehicle_reload2);
 
     if(bodymodel == "")
@@ -1139,9 +1259,11 @@ float vehicle_initialize(string  net_name,
     self.takedamage         = DAMAGE_AIM;
     self.bot_attack         = TRUE;
     self.iscreature         = TRUE;
+    self.teleportable       = FALSE; // no teleporting for vehicles, too buggy
     self.damagedbycontents     = TRUE;
     self.hud                = vhud;
     self.tur_health          = _max_health;
+    self.tur_head.tur_health = _max_shield;
     self.vehicle_die         = dieproc;
     self.vehicle_exit        = exitfunc;
     self.vehicle_enter       = enterproc;
@@ -1152,6 +1274,12 @@ float vehicle_initialize(string  net_name,
     self.nextthink           = time;
     self.vehicle_respawntime = _respawntime;
     self.vehicle_spawn       = spawnproc;
+    self.effects             = EF_NODRAW;
+    if(g_assault || !autocvar_g_vehicles_delayspawn)
+        self.nextthink = time + 0.5;
+    else
+        self.nextthink = time + _respawntime + (random() * autocvar_g_vehicles_delayspawn_jitter);
+
 
     if(autocvar_g_nodepthtestplayers)
         self.effects = self.effects | EF_NODEPTHTEST;
@@ -1162,7 +1290,6 @@ float vehicle_initialize(string  net_name,
     setmodel(self.vehicle_hudmodel, hudmodel);
     setmodel(self.vehicle_viewport, "null");
 
-
     if(topmodel != "")
     {
         setmodel(self.tur_head, topmodel);
@@ -1184,7 +1311,7 @@ float vehicle_initialize(string  net_name,
         tracebox(self.origin + '0 0 100', min_s, max_s, self.origin - '0 0 10000', MOVE_WORLDONLY, self);
         setorigin(self, trace_endpos);
     }
-
+    
     self.pos1 = self.origin;
     self.pos2 = self.angles;
     self.tur_head.team = self.team;
@@ -1192,30 +1319,90 @@ float vehicle_initialize(string  net_name,
     return TRUE;
 }
 
-void vehicle_aimturret(entity _vehic, vector _target, entity _turrret, string _tagname, 
+vector vehicle_aimturret(entity _vehic, vector _target, entity _turrret, string _tagname, 
                          float _pichlimit_min, float _pichlimit_max, 
                          float _rotlimit_min, float _rotlimit_max, float _aimspeed)
 {
-    vector vtmp;
+    vector vtmp, vtag;
     float ftmp;
-    
-    vtmp = vectoangles(normalize(_target - gettaginfo(_turrret, gettagindex(_turrret, _tagname))));
+    vtag = gettaginfo(_turrret, gettagindex(_turrret, _tagname));
+    vtmp = vectoangles(normalize(_target - vtag));
     vtmp = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(_vehic.angles), AnglesTransform_FromAngles(vtmp))) - _turrret.angles;
     vtmp = AnglesTransform_Normalize(vtmp, TRUE);
-
-    ftmp = _aimspeed * sys_frametime;
+    ftmp = _aimspeed * frametime;
     vtmp_y = bound(-ftmp, vtmp_y, ftmp);
     vtmp_x = bound(-ftmp, vtmp_x, ftmp);
     _turrret.angles_y = bound(_rotlimit_min, _turrret.angles_y + vtmp_y, _rotlimit_max);    
-    _turrret.angles_x = bound(_pichlimit_min, _turrret.angles_x + vtmp_x, _pichlimit_max);    
+    _turrret.angles_x = bound(_pichlimit_min, _turrret.angles_x + vtmp_x, _pichlimit_max);
+    return vtag;
 }
 
+void vehicles_gib_explode()
+{
+       sound (self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTN_NORM);
+       pointparticles(particleeffectnum("explosion_small"), randomvec() * 80 + (self.origin + '0 0 100'), '0 0 0', 1);
+       remove(self);
+}
 
-void bugmenot()
+void vehicles_gib_think()
 {
-    self.vehicle_exit       = self.vehicle_exit;
-    self.vehicle_enter      = self.vehicle_exit;
-    self.vehicle_die        = self.vehicle_exit;
-    self.vehicle_spawn      = self.vehicle_exit;
-    self.AuxiliaryXhair     = self.AuxiliaryXhair;
+       self.alpha -= 0.1;
+       if(self.cnt >= time)
+               remove(self);
+       else
+               self.nextthink = time + 0.1;
 }
+
+entity vehicle_tossgib(entity _template, vector _vel, string _tag, float _burn, float _explode, float _maxtime, vector _rot)
+{
+       entity _gib = spawn();
+       setmodel(_gib, _template.model);
+       setorigin(_gib, gettaginfo(self, gettagindex(self, _tag)));
+       _gib.velocity = _vel;
+       _gib.movetype = MOVETYPE_TOSS;
+       _gib.solid = SOLID_CORPSE;
+       _gib.colormod = '-0.5 -0.5 -0.5';
+       _gib.effects = EF_LOWPRECISION; 
+       _gib.avelocity = _rot;
+       
+       if(_burn)
+               _gib.effects |= EF_FLAME;
+       
+       if(_explode)
+       {
+               _gib.think = vehicles_gib_explode; 
+               _gib.nextthink = time + random() * _explode;
+               _gib.touch = vehicles_gib_explode;
+       }
+       else
+       {
+               _gib.cnt = time + _maxtime;
+               _gib.think = vehicles_gib_think; 
+               _gib.nextthink = time + _maxtime - 1;           
+               _gib.alpha = 1;
+       }
+       return _gib;
+}
+
+/*
+vector predict_target(entity _targ, vector _from, float _shot_speed)
+{
+    float i;                // loop
+    float _distance;        // How far to target
+    float _impact_time;     // How long untill projectile impacts
+    vector _predict_pos;    // Predicted enemy location
+    vector _original_origin;// Where target is before predicted
+
+     _original_origin = real_origin(_targ); // Typicaly center of target BBOX
+
+    _predict_pos = _original_origin;
+    for(i = 0; i < 4; ++i)  // Loop a few times to increase prediction accuracy (increase loop count if accuracy is to low)
+    {
+        _distance = vlen(_predict_pos - _from); // Get distance to previos predicted location
+        _impact_time = _distance / _shot_speed; // Calculate impact time
+        _predict_pos = _original_origin + _targ.velocity * _impact_time; // Calculate new predicted location
+    }
+
+    return _predict_pos;
+}
+*/
index 79fc9cbf9a9ddd9c5d86213d550dab00bf48007d..549dfeab7fffcf16b2e33fc48814ff07e330f706 100644 (file)
@@ -4,5 +4,7 @@
 #include "racer.qc"
 #include "spiderbot.qc"
 #include "raptor.qc"
-//#include "bumblebee.qc"
+#ifndef VEHICLES_NO_UNSTABLE
+#include "bumblebee.qc"
+#endif
 #endif
index b9d9fb4a6d068dde6ab06bbe8fa6e79e4803d6b0..834e6516787ba0411677ea34075cf769adb1bda3 100644 (file)
@@ -12,35 +12,40 @@ float VHF_DEATHEJECT    = 64;   /// Vehicle ejects pilot upon fatal damage
 float VHF_MOVE_GROUND   = 128;  /// Vehicle moves on gound
 float VHF_MOVE_HOVER    = 256;  /// Vehicle hover close to gound
 float VHF_MOVE_FLY      = 512;  /// Vehicle is airborn
-float VHF_DMGSHAKE      = 1024;
-float VHF_DMGROLL       = 2048;
-float VHF_DMGHEADROLL   = 4096;
+float VHF_DMGSHAKE      = 1024; /// Add random velocity each frame if health < 50%
+float VHF_DMGROLL       = 2048; /// Add random angles each frame if health < 50%
+float VHF_DMGHEADROLL   = 4096; /// Add random head angles each frame if health < 50%
+float VHF_MULTISLOT     = 8192; /// Vehicle has multiple player slots
+float VHF_PLAYERSLOT    = 16384;    /// This ent is a player slot on a multi-person vehicle
 
 .entity gun1;
 .entity gun2;
 .entity gun3;
+.entity vehicle_shieldent;  /// Entity to disply the shild effect on damage
+.entity vehicle;
+.entity vehicle_viewport;
+.entity vehicle_hudmodel;
+.entity vehicle_controller;
+
+.entity gunner1;
+.entity gunner2;
 
 .float vehicle_health;      /// If self is player this is 0..100 indicating precentage of health left on vehicle. If self is vehile, this is the real health value.
 .float vehicle_energy;      /// If self is player this is 0..100 indicating precentage of energy left on vehicle. If self is vehile, this is the real energy value.
 .float vehicle_shield;      /// If self is player this is 0..100 indicating precentage of shield left on vehicle. If self is vehile, this is the real shield value.
-.entity vehicle_shieldent;  /// Entity to disply the shild effect on damage
 
 .float vehicle_ammo1;   /// If self is player this field's use depends on the individual vehile. If self is vehile, this is the real ammo1 value.
 .float vehicle_reload1; /// If self is player this field's use depends on the individual vehile. If self is vehile, this is the real reload1 value.
 .float vehicle_ammo2;   /// If self is player this field's use depends on the individual vehile. If self is vehile, this is the real ammo2 value.
 .float vehicle_reload2; /// If self is player this field's use depends on the individual vehile. If self is vehile, this is the real reload2 value.
 
-.entity vehicle;
-.entity vehicle_viewport;
-.entity vehicle_hudmodel;
-
 .float sound_nexttime;
 #define VOL_VEHICLEENGINE 1
 
 .float hud;
 .float dmg_time;
 .float  vehicle_respawntime;
-.void() vehicle_spawn;
+//.void() vehicle_spawn;
 
 void vehicles_exit(float eject);
 var .void(float exit_flags) vehicle_exit;
@@ -54,11 +59,15 @@ float SVC_UPDATEENTITY  = 128; // Net.Protocol 0x80
 
 var .void() vehicle_enter;  /// Vehicles custom funciton to be executed when owner exit it
 var .void() vehicle_die;    /// Vehicles custom function to be executed when vehile die
-var .void() vehicle_spawn;  /// Vehicles custom fucntion to be efecuted when vehicle (re)spawns
+#define VHSF_NORMAL 0
+#define VHSF_FACTORY 2
+var .void(float _spawnflag) vehicle_spawn;  /// Vehicles custom fucntion to be efecuted when vehicle (re)spawns
 const var .float(float _imp) vehicles_impusle_null;
 var .float(float _imp) vehicles_impusle;
 .float vehicle_weapon2mode = volly_counter;
 
+//§ var .void() vehicle_factory()
+
 #ifdef VEHICLES_USE_ODE
 void(entity e, float physics_enabled) physics_enable = #540; // enable or disable physics on object
 void(entity e, vector force, vector force_pos) physics_addforce = #541; // apply a force from certain origin, length of force vector is power of force
index 1ce463827423fe2d2ca24cfc279772824128f569..bb44e827e709cd0eb84e37bdf04c361179055e45 100644 (file)
@@ -225,6 +225,28 @@ void W_Crylink_LinkJoinEffect_Think()
        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;
+       float hit_enemy;
+
+       while(head)
+       {
+               if((head.takedamage != DAMAGE_NO) && (head.deadflag == DEAD_NO))
+               {
+                       if(IsDifferentTeam(head, projectile.realowner))
+                               ++hit_enemy;
+                       else
+                               ++hit_friendly;
+               }
+                       
+               head = head.chain;
+       }
+
+       return (hit_enemy ? FALSE : hit_friendly);
+}
+
 // NO bounce protection, as bounces are limited!
 void W_Crylink_Touch (void)
 {
@@ -242,7 +264,10 @@ void W_Crylink_Touch (void)
                f = autocvar_g_balance_crylink_primary_bouncedamagefactor;
        if(a)
                f *= a;
-       if (RadiusDamage (self, self.realowner, autocvar_g_balance_crylink_primary_damage * f, autocvar_g_balance_crylink_primary_edgedamage * f, autocvar_g_balance_crylink_primary_radius, world, autocvar_g_balance_crylink_primary_force * f, self.projectiledeathtype, other) && autocvar_g_balance_crylink_primary_linkexplode)
+
+       float totaldamage = RadiusDamage(self, self.realowner, autocvar_g_balance_crylink_primary_damage * f, autocvar_g_balance_crylink_primary_edgedamage * f, autocvar_g_balance_crylink_primary_radius, world, autocvar_g_balance_crylink_primary_force * f, self.projectiledeathtype, other);
+       
+       if(totaldamage && ((autocvar_g_balance_crylink_primary_linkexplode == 2) || ((autocvar_g_balance_crylink_primary_linkexplode == 1) && !W_Crylink_Touch_WouldHitFriendly(self, autocvar_g_balance_crylink_primary_radius))))
        {
                if(self == self.realowner.crylink_lastgroup)
                        self.realowner.crylink_lastgroup = world;
@@ -283,7 +308,10 @@ void W_Crylink_Touch2 (void)
                f = autocvar_g_balance_crylink_secondary_bouncedamagefactor;
        if(a)
                f *= a;
-       if (RadiusDamage (self, self.realowner, autocvar_g_balance_crylink_secondary_damage * f, autocvar_g_balance_crylink_secondary_edgedamage * f, autocvar_g_balance_crylink_secondary_radius, world, autocvar_g_balance_crylink_secondary_force * f, self.projectiledeathtype, other) && autocvar_g_balance_crylink_secondary_linkexplode)
+
+       float totaldamage = RadiusDamage(self, self.realowner, autocvar_g_balance_crylink_secondary_damage * f, autocvar_g_balance_crylink_secondary_edgedamage * f, autocvar_g_balance_crylink_secondary_radius, world, autocvar_g_balance_crylink_secondary_force * f, self.projectiledeathtype, other);
+               
+       if(totaldamage && ((autocvar_g_balance_crylink_secondary_linkexplode == 2) || ((autocvar_g_balance_crylink_secondary_linkexplode == 1) && !W_Crylink_Touch_WouldHitFriendly(self, autocvar_g_balance_crylink_secondary_radius))))
        {
                if(self == self.realowner.crylink_lastgroup)
                        self.realowner.crylink_lastgroup = world;
@@ -427,6 +455,8 @@ 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);
@@ -437,6 +467,9 @@ void W_Crylink_Attack2 (void)
                maxdmg += autocvar_g_balance_crylink_secondary_joinexplode_damage;
 
        W_SetupShot (self, FALSE, 2, "weapons/crylink_fire2.wav", CH_WEAPON_A, maxdmg);
+       forward = v_forward;
+       right = v_right;
+       up = v_up;
 
        shots = autocvar_g_balance_crylink_secondary_shots;
        pointparticles(particleeffectnum("crylink_muzzleflash"), w_shotorg, w_shotdir * 1000, shots);
@@ -477,7 +510,26 @@ void W_Crylink_Attack2 (void)
                setorigin (proj, w_shotorg);
                setsize(proj, '0 0 0', '0 0 0');
 
-               W_SetupProjectileVelocityEx(proj, (w_shotdir + (((counter + 0.5) / shots) * 2 - 1) * v_right * autocvar_g_balance_crylink_secondary_spread * g_weaponspreadfactor), v_up, autocvar_g_balance_crylink_secondary_speed, 0, 0, 0, FALSE);
+               if(autocvar_g_balance_crylink_secondary_spreadtype == 1)
+               {
+                       s = '0 0 0';
+                       if (counter == 0)
+                               s = '0 0 0';
+                       else
+                       {
+                               makevectors('0 360 0' * (0.75 + (counter - 0.5) / (shots - 1)));
+                               s_y = v_forward_x;
+                               s_z = v_forward_y;
+                       }
+                       s = s * autocvar_g_balance_crylink_secondary_spread * g_weaponspreadfactor;
+                       s = w_shotdir + right * s_y + up * s_z;
+               }
+               else
+               {
+                       s = (w_shotdir + (((counter + 0.5) / shots) * 2 - 1) * v_right * autocvar_g_balance_crylink_secondary_spread * g_weaponspreadfactor);
+               }
+
+               W_SetupProjectileVelocityEx(proj, s, v_up, autocvar_g_balance_crylink_secondary_speed, 0, 0, 0, FALSE);
                proj.touch = W_Crylink_Touch2;
                proj.think = W_Crylink_Fadethink;
                if(counter == (shots - 1) / 2)
index 7bbebdc9c2a707ad0cd5366d2f1de44456db6838..9040c72407a8914972d2e4533b2852a767753702 100644 (file)
@@ -16,11 +16,19 @@ void WarpZone_Accumulator_AddTransform(entity acc, vector t, vector s)
 }
 void WarpZone_Accumulator_Add(entity acc, entity wz)
 {
-       vector t, st;
-       t = AnglesTransform_Multiply(wz.warpzone_transform, acc.warpzone_transform);
-       st = AnglesTransform_Multiply_GetPostShift(wz.warpzone_transform, wz.warpzone_shift, acc.warpzone_transform, acc.warpzone_shift);
-       acc.warpzone_transform = t;
-       acc.warpzone_shift = st;
+       WarpZone_Accumulator_AddTransform(acc, wz.warpzone_transform, wz.warpzone_shift);
+}
+void WarpZone_Accumulator_AddInverseTransform(entity acc, vector t, vector s)
+{
+       vector tt, ss;
+       tt = AnglesTransform_Invert(t);
+       ss = AnglesTransform_PrePostShift_GetPostShift(s, tt, '0 0 0');
+       WarpZone_Accumulator_AddTransform(acc, tt, ss);
+       // yes, this probably can be done simpler... but this way is "obvious" :)
+}
+void WarpZone_Accumulator_AddInverse(entity acc, entity wz)
+{
+       WarpZone_Accumulator_AddInverseTransform(acc, wz.warpzone_transform, wz.warpzone_shift);
 }
 
 .vector(vector, vector) camera_transform;
@@ -643,7 +651,7 @@ void WarpZone_RefSys_GC()
        if(self.owner.WarpZone_refsys != self)
                remove(self);
 }
-void WarpZone_RefSys_Add(entity me, entity wz)
+void WarpZone_RefSys_CheckCreate(entity me)
 {
        if(me.WarpZone_refsys.owner != me)
        {
@@ -654,29 +662,48 @@ void WarpZone_RefSys_Add(entity me, entity wz)
                me.WarpZone_refsys.nextthink = time + 1;
                WarpZone_Accumulator_Clear(me.WarpZone_refsys);
        }
-       if(wz)
-               WarpZone_Accumulator_Add(me.WarpZone_refsys, wz);
+}
+void WarpZone_RefSys_Clear(entity me)
+{
+       if(me.WarpZone_refsys)
+       {
+               remove(me.WarpZone_refsys);
+               me.WarpZone_refsys = world;
+       }
+}
+void WarpZone_RefSys_AddTransform(entity me, vector t, vector s)
+{
+       if(t != '0 0 0' || s != '0 0 0')
+       {
+               WarpZone_RefSys_CheckCreate(me);
+               WarpZone_Accumulator_AddTransform(me.WarpZone_refsys, t, s);
+       }
+}
+void WarpZone_RefSys_Add(entity me, entity wz)
+{
+       WarpZone_RefSys_AddTransform(me, wz.warpzone_transform, wz.warpzone_shift);
+}
+void WarpZone_RefSys_AddInverseTransform(entity me, vector t, vector s)
+{
+       if(t != '0 0 0' || s != '0 0 0')
+       {
+               WarpZone_RefSys_CheckCreate(me);
+               WarpZone_Accumulator_AddInverseTransform(me.WarpZone_refsys, t, s);
+       }
+}
+void WarpZone_RefSys_AddInverse(entity me, entity wz)
+{
+       WarpZone_RefSys_AddInverseTransform(me, wz.warpzone_transform, wz.warpzone_shift);
 }
 .vector WarpZone_refsys_incremental_shift;
 .vector WarpZone_refsys_incremental_transform;
 void WarpZone_RefSys_AddIncrementally(entity me, entity ref)
 {
-       vector t, s;
+       //vector t, s;
        if(me.WarpZone_refsys_incremental_transform == ref.WarpZone_refsys.warpzone_transform)
        if(me.WarpZone_refsys_incremental_shift == ref.WarpZone_refsys.warpzone_shift)
                return;
-       if(me.WarpZone_refsys.owner != me)
-       {
-               me.WarpZone_refsys = spawn();
-               me.WarpZone_refsys.classname = "warpzone_refsys";
-               me.WarpZone_refsys.owner = me;
-               me.WarpZone_refsys.think = WarpZone_RefSys_GC;
-               me.WarpZone_refsys.nextthink = time + 1;
-               WarpZone_Accumulator_Clear(me.WarpZone_refsys);
-       }
-       t = AnglesTransform_Invert(me.WarpZone_refsys_incremental_transform);
-       s = AnglesTransform_PrePostShift_GetPostShift(me.WarpZone_refsys_incremental_shift, t, '0 0 0');
-       WarpZone_Accumulator_AddTransform(me.WarpZone_refsys, t, s);
+       WarpZone_Accumulator_AddInverseTransform(me.WarpZone_refsys, me.WarpZone_refsys_incremental_transform, me.WarpZone_refsys_incremental_shift);
        WarpZone_Accumulator_Add(me.WarpZone_refsys, ref.WarpZone_refsys);
        me.WarpZone_refsys_incremental_shift = ref.WarpZone_refsys.warpzone_shift;
        me.WarpZone_refsys_incremental_transform = ref.WarpZone_refsys.warpzone_transform;
@@ -718,19 +745,21 @@ vector WarpZone_RefSys_TransformVAngles(entity from, entity to, vector ang)
                ang = WarpZone_TransformVAngles(to.WarpZone_refsys, ang);
        return ang;
 }
+void WarpZone_RefSys_Copy(entity me, entity from)
+{
+       if(from.WarpZone_refsys)
+       {
+               WarpZone_RefSys_CheckCreate(me);
+               me.WarpZone_refsys.warpzone_shift = from.WarpZone_refsys.warpzone_shift;
+               me.WarpZone_refsys.warpzone_transform = from.WarpZone_refsys.warpzone_transform;
+       }
+       else
+               WarpZone_RefSys_Clear(me);
+}
 entity WarpZone_RefSys_SpawnSameRefSys(entity me)
 {
        entity e;
        e = spawn();
-       if(me.WarpZone_refsys)
-       {
-               e.WarpZone_refsys = spawn();
-               e.WarpZone_refsys.classname = "warpzone_refsys";
-               e.WarpZone_refsys.owner = e;
-               e.WarpZone_refsys.think = WarpZone_RefSys_GC;
-               e.WarpZone_refsys.nextthink = time + 1;
-               e.WarpZone_refsys.warpzone_shift = me.WarpZone_refsys.warpzone_shift;
-               e.WarpZone_refsys.warpzone_transform = me.WarpZone_refsys.warpzone_transform;
-       }
+       WarpZone_RefSys_Copy(e, me);
        return e;
 }
index c2f36bea868d8baf7ad06c37294b3b7bb2316a24..e7cf23908eda0198025a0c5ebaf437b1eb890757 100644 (file)
@@ -62,14 +62,25 @@ vector WarpZone_UnTransformAngles(entity wz, vector v);
 vector WarpZone_UnTransformVAngles(entity wz, vector v);
 
 // reference systems (chained warpzone transforms)
-void WarpZone_RefSys_Add(entity me, entity wz);
-void WarpZone_RefSys_AddIncrementally(entity me, entity ref);
-void WarpZone_RefSys_BeginAddingIncrementally(entity me, entity ref);
-vector WarpZone_RefSys_TransformOrigin(entity from, entity to, vector org);
-vector WarpZone_RefSys_TransformVelocity(entity from, entity to, vector vel);
-vector WarpZone_RefSys_TransformAngles(entity from, entity to, vector ang);
-vector WarpZone_RefSys_TransformVAngles(entity from, entity to, vector ang);
-entity WarpZone_RefSys_SpawnSameRefSys(entity me);
+void WarpZone_RefSys_Clear(entity me); // R := id
+void WarpZone_RefSys_Add(entity me, entity wz); // me.R := wz me.R
+void WarpZone_RefSys_AddInverse(entity me, entity wz); // me.R := wz^-1 me.R
+void WarpZone_RefSys_AddTransform(entity me, vector t, vector s); // me.R := [t s] me.R
+void WarpZone_RefSys_AddInverseTransform(entity me, vector t, vector s); // me.R := [t s]^-1 me.R
+
+// makes this reference system track ref's changes
+// NOTE: this is ONLY sensible if WarpZone_RefSys_Add is no longer called on "me" while doing this
+// To achieve this, make sure no touch events on warpzone are raised by this entity
+// or set a movetype that causes no warpzoning (e.g. MOVETYPE_NONE, MOVETYPE_FOLLOW)
+void WarpZone_RefSys_AddIncrementally(entity me, entity ref); // me.R := ref.R me.Rref^-1 me.R; me.Rref := ref.R
+void WarpZone_RefSys_BeginAddingIncrementally(entity me, entity ref); // me.Rref := ref.R
+
+vector WarpZone_RefSys_TransformOrigin(entity from, entity to, vector org); // return to.R from.R^-1 org
+vector WarpZone_RefSys_TransformVelocity(entity from, entity to, vector vel); // return to.R from.R^-1 vel
+vector WarpZone_RefSys_TransformAngles(entity from, entity to, vector ang); // return to.R from.R^-1 ang
+vector WarpZone_RefSys_TransformVAngles(entity from, entity to, vector ang); // return to.R from.R^-1 ang
+void WarpZone_RefSys_Copy(entity me, entity from); // to.R := from.R
+entity WarpZone_RefSys_SpawnSameRefSys(entity me); // spawn().R = me.R
 
 #ifndef BITCLR
 # define BITCLR(a,b) ((a) - ((a) & (b)))
diff --git a/sound/ctf/pass.wav b/sound/ctf/pass.wav
new file mode 100644 (file)
index 0000000..0be02d5
Binary files /dev/null and b/sound/ctf/pass.wav differ
diff --git a/sound/ctf/touch.wav b/sound/ctf/touch.wav
new file mode 100644 (file)
index 0000000..2ab908b
Binary files /dev/null and b/sound/ctf/touch.wav differ
index 2037f7b10be9cef4dff290bed58a7f053a3e7cc4..929805a30f2ef0e4a8b1ba243a9d059d0e04ae44 100644 (file)
Binary files a/textures/raptor.tga and b/textures/raptor.tga differ
diff --git a/textures/raptor_pants.jpg b/textures/raptor_pants.jpg
deleted file mode 100644 (file)
index 5cfa32a..0000000
Binary files a/textures/raptor_pants.jpg and /dev/null differ
diff --git a/textures/raptor_shirt.jpg b/textures/raptor_shirt.jpg
deleted file mode 100644 (file)
index e5bae42..0000000
Binary files a/textures/raptor_shirt.jpg and /dev/null differ
diff --git a/textures/raptor_shirt.tga b/textures/raptor_shirt.tga
new file mode 100644 (file)
index 0000000..5aa36ec
Binary files /dev/null and b/textures/raptor_shirt.tga differ
index 718287f2cace29e78c946137cde82cd1199e3498..705d2a9883341ab2a5904fb1461d16973f6f8af9 100644 (file)
@@ -1,45 +1,64 @@
-set g_vehicle_bumblebee_speed_forward            300
-set g_vehicle_bumblebee_speed_strafe             300
-set g_vehicle_bumblebee_speed_up                 300
-set g_vehicle_bumblebee_speed_down               300
-set g_vehicle_bumblebee_turnspeed                64
-set g_vehicle_bumblebee_pitchspeed               64
+set g_vehicle_bumblebee_respawntime              60
+
+set g_vehicle_bumblebee_speed_forward            350
+set g_vehicle_bumblebee_speed_strafe             350
+set g_vehicle_bumblebee_speed_up                 350
+set g_vehicle_bumblebee_speed_down               350
+set g_vehicle_bumblebee_turnspeed                120
+set g_vehicle_bumblebee_pitchspeed               60
 set g_vehicle_bumblebee_pitchlimit               60
-set g_vehicle_bumblebee_friction                 0.4
+set g_vehicle_bumblebee_friction                 0.5
 
 set g_vehicle_bumblebee_energy                   500
 set g_vehicle_bumblebee_energy_regen             50
 set g_vehicle_bumblebee_energy_regen_pause       1
 
-set g_vehicle_bumblebee_health                   750
-set g_vehicle_bumblebee_health_regen             25
-set g_vehicle_bumblebee_health_regen_pause       5
-
-set g_vehicle_bumblebee_shield                   250
-set g_vehicle_bumblebee_shield_regen             100
-set g_vehicle_bumblebee_shield_regen_pause       2
-
-set g_vehicle_bumblebee_cannon_cost              10
-set g_vehicle_bumblebee_cannon_damage            125
-set g_vehicle_bumblebee_cannon_radius            300
-set g_vehicle_bumblebee_cannon_refire            0.75
-set g_vehicle_bumblebee_cannon_speed             2500
-set g_vehicle_bumblebee_cannon_spread            0.0125
-set g_vehicle_bumblebee_cannon_force             200
-set g_vehicle_bumblebee_cannon_turnspeed         90
-set g_vehicle_bumblebee_cannon_pitchlimit_down   60
-set g_vehicle_bumblebee_cannon_pitchlimit_up     60
-set g_vehicle_bumblebee_cannon_turnlimit_in      15
-set g_vehicle_bumblebee_cannon_turnlimit_out     45
-
-set g_vehicle_bumblebee_raygun_turnspeed 50
+set g_vehicle_bumblebee_health                   1000
+set g_vehicle_bumblebee_health_regen             65
+set g_vehicle_bumblebee_health_regen_pause       10
+
+set g_vehicle_bumblebee_shield                   400
+set g_vehicle_bumblebee_shield_regen             150
+set g_vehicle_bumblebee_shield_regen_pause       0.75
+
+set g_vehicle_bumblebee_cannon_lock                            0
+set g_vehicle_bumblebee_cannon_cost                            2
+set g_vehicle_bumblebee_cannon_damage                  60
+set g_vehicle_bumblebee_cannon_radius                  225
+set g_vehicle_bumblebee_cannon_refire                  0.2
+set g_vehicle_bumblebee_cannon_speed                   20000
+set g_vehicle_bumblebee_cannon_spread                  0.02
+set g_vehicle_bumblebee_cannon_force                   -35
+set g_vehicle_bumblebee_cannon_turnspeed               160
+set g_vehicle_bumblebee_cannon_pitchlimit_down 60
+set g_vehicle_bumblebee_cannon_pitchlimit_up   60
+set g_vehicle_bumblebee_cannon_turnlimit_in            20
+set g_vehicle_bumblebee_cannon_turnlimit_out   80
+set g_vehicle_bumblebee_cannon_ammo                            100
+set g_vehicle_bumblebee_cannon_ammo_regen              100
+set g_vehicle_bumblebee_cannon_ammo_regen_pause        1
+
+set g_vehicle_bumblebee_raygun_turnspeed 180
 set g_vehicle_bumblebee_raygun_pitchlimit_down 20
-set g_vehicle_bumblebee_raygun_pitchlimit_up 30
-set g_vehicle_bumblebee_raygun_turnlimit_sides 30
+set g_vehicle_bumblebee_raygun_pitchlimit_up 5
+set g_vehicle_bumblebee_raygun_turnlimit_sides 35
+
+set g_vehicle_bumblebee_raygun 0
+set g_vehicle_bumblebee_raygun_range 2048
+set g_vehicle_bumblebee_raygun_dps 250
+set g_vehicle_bumblebee_raygun_aps 100
+set g_vehicle_bumblebee_raygun_fps 100
 
-set g_vehicle_bumblebee_respawntime              10
+set g_vehicle_bumblebee_healgun_hps 150
+set g_vehicle_bumblebee_healgun_hmax 100
+set g_vehicle_bumblebee_healgun_aps 75
+set g_vehicle_bumblebee_healgun_amax 100
+set g_vehicle_bumblebee_healgun_sps 100
+set g_vehicle_bumblebee_healgun_smax 100
+set g_vehicle_bumblebee_healgun_locktime 2.5
 
 set g_vehicle_bumblebee_blowup_radius            500
 set g_vehicle_bumblebee_blowup_coredamage        500
 set g_vehicle_bumblebee_blowup_edgedamage        100
-set g_vehicle_bumblebee_blowup_forceintensity    600
\ No newline at end of file
+set g_vehicle_bumblebee_blowup_forceintensity    600
+set g_vehicle_bumblebee_bouncepain "1 100 200"
\ No newline at end of file
index b4a1d5b766f4e05e668e0f5b9e8c7863c805907f..5c3e06aff1cc9f4db3b590407cecd65beec6db58 100644 (file)
@@ -13,14 +13,14 @@ set g_vehicle_racer_energy_regen        50
 set g_vehicle_racer_energy_regen_pause  1
 
 set g_vehicle_racer_speed_stop          2500
-set g_vehicle_racer_speed_forward       700
-set g_vehicle_racer_speed_strafe        700
-set g_vehicle_racer_speed_afterburn     1000
-set g_vehicle_racer_friction            0.35
-set g_vehicle_racer_afterburn_cost      25      // energy consumed per second
+set g_vehicle_racer_speed_forward       650
+set g_vehicle_racer_speed_strafe        650
+set g_vehicle_racer_speed_afterburn     3000
+set g_vehicle_racer_friction            0.45
+set g_vehicle_racer_afterburn_cost      100       // energy consumed per second
 
 set g_vehicle_racer_hovertype           0       // 0 = hover, != 0 = maglev
-set g_vehicle_racer_hoverpower          7500    // NOTE!! x 4 (4 engines)
+set g_vehicle_racer_hoverpower          8000    // NOTE!! x 4 (4 engines)
 set g_vehicle_racer_upforcedamper       10
 
 set g_vehicle_racer_downforce            0.01
@@ -28,33 +28,33 @@ set g_vehicle_racer_springlength         70
 set g_vehicle_racer_collision_multiplier 0.05
 set g_vehicle_racer_anglestabilizer      1.75
 
-set g_vehicle_racer_turnspeed          200
+set g_vehicle_racer_turnspeed          220
 set g_vehicle_racer_pitchspeed         125
 set g_vehicle_racer_maxpitch           25
 set g_vehicle_racer_turnroll           30
 
-set g_vehicle_racer_cannon_speed        9000
-set g_vehicle_racer_cannon_damage       30
+set g_vehicle_racer_cannon_speed        15000
+set g_vehicle_racer_cannon_damage       15
 set g_vehicle_racer_cannon_radius       100
-set g_vehicle_racer_cannon_refire       0.15
-set g_vehicle_racer_cannon_cost         10
+set g_vehicle_racer_cannon_refire       0.05
+set g_vehicle_racer_cannon_cost         2
 set g_vehicle_racer_cannon_spread       0.0125
 set g_vehicle_racer_cannon_force        50
 
-set g_vehicle_racer_rocket_speed       1000
-set g_vehicle_racer_rocket_accel       1500
+set g_vehicle_racer_rocket_speed       900
+set g_vehicle_racer_rocket_accel       1600
 set g_vehicle_racer_rocket_turnrate    0.2
-set g_vehicle_racer_rocket_damage      165
+set g_vehicle_racer_rocket_damage      100
 set g_vehicle_racer_rocket_force       350
 set g_vehicle_racer_rocket_radius      125
-set g_vehicle_racer_rocket_refire      6
+set g_vehicle_racer_rocket_refire      3
 set g_vehicle_racer_rocket_cost        0
-set g_vehicle_racer_rocket_climbspeed  1500
+set g_vehicle_racer_rocket_climbspeed  1600
 
 set g_vehicle_racer_rocket_locktarget           1
 set g_vehicle_racer_rocket_locking_time         0.35
-set g_vehicle_racer_rocket_locking_releasetime  1.5
-set g_vehicle_racer_rocket_locked_time          6
+set g_vehicle_racer_rocket_locking_releasetime  0.5
+set g_vehicle_racer_rocket_locked_time          4
 set g_vehicle_racer_rocket_locked_maxangle      1.8
 
 set g_vehicle_racer_blowup_radius           250
index ff0008afe1a0cd87bbad64b60c9709c187e0f1f6..1efc2f17534f291a1387c90af40bff49acb5fbfc 100644 (file)
@@ -12,11 +12,11 @@ set g_vehicle_raptor_turnspeed  200
 set g_vehicle_raptor_pitchspeed 50
 set g_vehicle_raptor_pitchlimit 45
 
-set g_vehicle_raptor_speed_forward 760
-set g_vehicle_raptor_speed_strafe  500
-set g_vehicle_raptor_speed_up      700
-set g_vehicle_raptor_speed_down    900
-set g_vehicle_raptor_friction      0.6
+set g_vehicle_raptor_speed_forward 1700
+set g_vehicle_raptor_speed_strafe  900
+set g_vehicle_raptor_speed_up      1700
+set g_vehicle_raptor_speed_down    1700
+set g_vehicle_raptor_friction      2
 
 set g_vehicle_raptor_bomblets           8
 set g_vehicle_raptor_bomblet_alt        750
@@ -29,21 +29,21 @@ set g_vehicle_raptor_bomblet_force      150
 set g_vehicle_raptor_bomblet_explode_delay 0.4
 set g_vehicle_raptor_bombs_refire       5
 
-set g_vehicle_raptor_cannon_turnspeed 40
+set g_vehicle_raptor_cannon_turnspeed 60
 set g_vehicle_raptor_cannon_turnlimit 20
 set g_vehicle_raptor_cannon_pitchlimit_up   12
 set g_vehicle_raptor_cannon_pitchlimit_down 32
 
 set g_vehicle_raptor_cannon_locktarget          1
-set g_vehicle_raptor_cannon_locking_time        0.4
-set g_vehicle_raptor_cannon_locking_releasetime 1.6
-set g_vehicle_raptor_cannon_locked_time         5
+set g_vehicle_raptor_cannon_locking_time        0.2
+set g_vehicle_raptor_cannon_locking_releasetime 0.45
+set g_vehicle_raptor_cannon_locked_time         1
 set g_vehicle_raptor_cannon_predicttarget       1
 
 set g_vehicle_raptor_cannon_cost     1
 set g_vehicle_raptor_cannon_damage   10
 set g_vehicle_raptor_cannon_radius   60
-set g_vehicle_raptor_cannon_refire   0.05
+set g_vehicle_raptor_cannon_refire   0.03
 set g_vehicle_raptor_cannon_speed    12000
 set g_vehicle_raptor_cannon_spread   0.01
 set g_vehicle_raptor_cannon_force    25
@@ -51,22 +51,22 @@ set g_vehicle_raptor_cannon_force    25
 set g_vehicle_raptor_flare_refire 5
 set g_vehicle_raptor_flare_lifetime 10
 set g_vehicle_raptor_flare_chase 0.9
-set g_vehicle_raptor_flare_range 1750
+set g_vehicle_raptor_flare_range 2000
 
-set g_vehicle_raptor_energy             50
-set g_vehicle_raptor_energy_regen       20
-set g_vehicle_raptor_energy_regen_pause 1
+set g_vehicle_raptor_energy             100
+set g_vehicle_raptor_energy_regen       25
+set g_vehicle_raptor_energy_regen_pause 0.25
 
-set g_vehicle_raptor_health             200
+set g_vehicle_raptor_health             150
 set g_vehicle_raptor_health_regen       0
 set g_vehicle_raptor_health_regen_pause 0
 
-set g_vehicle_raptor_shield             100
+set g_vehicle_raptor_shield             75
 set g_vehicle_raptor_shield_regen       25
 set g_vehicle_raptor_shield_regen_pause 1.5
 
 set g_vehicle_raptor_bouncefactor 0.2
 set g_vehicle_raptor_bouncestop 0
-set g_vehicle_raptor_bouncepain "1 1 500"
+set g_vehicle_raptor_bouncepain "1 4 1000"
 
 set g_vehicle_raptor_mass              2200
index a7c0e5228f86a49017d9691baf81d16e775dd491..0223b09e897770b09ca9ae84b19fd561041758a9 100644 (file)
@@ -1,10 +1,10 @@
 set g_vehicle_spiderbot_respawntime                      45
 
-set g_vehicle_spiderbot_health                500
-set g_vehicle_spiderbot_health_regen             15
+set g_vehicle_spiderbot_health                800
+set g_vehicle_spiderbot_health_regen             10
 set g_vehicle_spiderbot_health_regen_pause    5
 
-set g_vehicle_spiderbot_shield                300
+set g_vehicle_spiderbot_shield                200
 set g_vehicle_spiderbot_shield_block          1
 set g_vehicle_spiderbot_shield_regen          25
 set g_vehicle_spiderbot_shield_regen_pause    0.35
@@ -15,25 +15,25 @@ set g_vehicle_spiderbot_energy_regen_pause  0
 
 set g_vehicle_spiderbot_turnspeed            90
 set g_vehicle_spiderbot_turnspeed_strafe     300
-set g_vehicle_spiderbot_head_turnspeed       100
+set g_vehicle_spiderbot_head_turnspeed       110
 set g_vehicle_spiderbot_head_turnlimit       90
 set g_vehicle_spiderbot_head_pitchlimit_up   30
 set g_vehicle_spiderbot_head_pitchlimit_down -20
 
 set g_vehicle_spiderbot_speed_stop         50
-set g_vehicle_spiderbot_speed_walk         400
-set g_vehicle_spiderbot_speed_strafe       300
+set g_vehicle_spiderbot_speed_walk         500
+set g_vehicle_spiderbot_speed_strafe       400
 set g_vehicle_spiderbot_movement_inertia   0.15
 set g_vehicle_spiderbot_tiltlimit          90
 
 set g_vehicle_spiderbot_minigun_damage          12       // 400 (x2) DPS 
 set g_vehicle_spiderbot_minigun_refire          0.03
 set g_vehicle_spiderbot_minigun_force           9
-set g_vehicle_spiderbot_minigun_spread          0.025
+set g_vehicle_spiderbot_minigun_spread          0.015
 set g_vehicle_spiderbot_minigun_speed           45000  // ~ 32QU
 set g_vehicle_spiderbot_minigun_bulletconstant  110
 set g_vehicle_spiderbot_minigun_ammo_cost       1
-set g_vehicle_spiderbot_minigun_ammo_max        100
+set g_vehicle_spiderbot_minigun_ammo_max        200
 set g_vehicle_spiderbot_minigun_ammo_regen      40
 set g_vehicle_spiderbot_minigun_ammo_regen_pause 1
 
@@ -42,10 +42,10 @@ set g_vehicle_spiderbot_springup            20
 set g_vehicle_spiderbot_springblend         0.1
 
 set g_vehicle_spiderbot_rocket_health     100
-set g_vehicle_spiderbot_rocket_damage     70
+set g_vehicle_spiderbot_rocket_damage     50
 set g_vehicle_spiderbot_rocket_edgedamage 25
 set g_vehicle_spiderbot_rocket_force      150
-set g_vehicle_spiderbot_rocket_radius     175
+set g_vehicle_spiderbot_rocket_radius     250
 set g_vehicle_spiderbot_rocket_reload     4
 set g_vehicle_spiderbot_rocket_refire     0.1
 set g_vehicle_spiderbot_rocket_refire2    0.025  // volly
@@ -55,11 +55,6 @@ set g_vehicle_spiderbot_rocket_noise      0.2
 set g_vehicle_spiderbot_rocket_lifetime   20
 set g_vehicle_spiderbot_rocket_spread     0.05
 
-set g_vehicle_spiderbot_crush_dmg         75
-set g_vehicle_spiderbot_crush_force       50
-
-set g_vehicle_spiderbot_mass              5000
-
 set g_vehicle_spiderbot_bouncefactor 0   // Factor of old velocity to keep after colission
 set g_vehicle_spiderbot_bouncestop 0        // if != 0, New veloctiy after bounce = 0 if new velocity < this
 set g_vehicle_spiderbot_bouncepain "0 0 0" // "minspeed_for_pain speedchange_to_pain_factor max_damage"
index 67f3c5c5f593826df6b9b55cf45cc374f2c5ad84..4030c64885d7945d728e9608a309c2b742e8e063 100644 (file)
@@ -5,9 +5,10 @@ exec vehicle_raptor.cfg
 exec vehicle_spiderbot.cfg
 exec vehicle_bumblebee.cfg
 
-//set g_vehicle_racer_respawntime     10
-//set g_vehicle_spiderbot_respawntime 10
-//set g_vehicle_raptor_respawntime    10
+set g_vehicle_racer 1
+set g_vehicle_spiderbot 1
+set g_vehicle_raptor 1
+set g_vehicle_bumblebee 1
 
 set g_vehicles_crush_dmg 70
 set g_vehicles_crush_force 50
@@ -16,9 +17,10 @@ set cl_vehicles_hudscale 0.5way
 
 set g_vehicles_delayspawn 1
 set g_vehicles_delayspawn_jitter 10
-set g_vehicles_allow_flagcarry 1
 
 set g_vehicles_nex_damagerate 0.5
-set g_vehicles_uzi_damagerate 0.5
-set g_vehicles_rifle_damagerate 0.5
-set g_vehicles_minstanex_damagerate 0.001
\ No newline at end of file
+set g_vehicles_uzi_damagerate 0.65
+set g_vehicles_rifle_damagerate 1
+set g_vehicles_minstanex_damagerate 0.001
+set g_vehicles_tag_damagerate 2
+
index 9129bfc4660cce178bac4c79da14de4c054b98a3..1b0318f21c5a5b515237d088d100575389b530e2 100644 (file)
@@ -141,7 +141,8 @@ SoulKeeper_p
 Stephan "esteel" Stahl
 The player with the unnecessarily long name
 Wolfgang "Blub\0" Bumiller
-
+Erik "Ablu" Schilling
+BlaXpirit
 **Past Contributors
 Alexander "motorsep" Zubov
 Amos "torus" Dudley