set g_balance_crylink_secondary_radius 3
set g_balance_crylink_secondary_speed 7000
set g_balance_crylink_secondary_spread 0.08
+set g_balance_crylink_secondary_spreadtype 0
set g_balance_crylink_secondary_shots 7
set g_balance_crylink_secondary_bounces 0
set g_balance_crylink_secondary_refire 0.5
set g_balance_crylink_secondary_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
--- /dev/null
+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
+// }}}
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
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
// {{{ 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
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
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
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
// {{{ 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
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
+++ /dev/null
-#!/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
// ==========================================================
// 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
--- /dev/null
+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
// 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
// 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"
-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
+++ /dev/null
-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
+++ /dev/null
-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
-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
-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
--- /dev/null
+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
+++ /dev/null
-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
+++ /dev/null
-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
--- /dev/null
+// ================
+// 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
-// 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
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
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
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
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"
// 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"
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"
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
// 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"
// 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"
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
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)"
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"
// 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
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"
count 500
type spark
tex 64 64
-color 0xff8400 0xff2a00
+color 0x807aff 0x4463d5
size 1 1
alpha 0 256 100
stretchfactor 2
size 150 150
alpha 190 190 180
sizeincrease -80
-color 0xff8400 0xff2a00
+color 0x807aff 0x4463d5
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
+
--- /dev/null
+// ===================================
+// 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
+
--- /dev/null
+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
--- /dev/null
+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
+++ /dev/null
-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
+++ /dev/null
-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
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");
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;
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();
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)
{
else
weapon_accuracy[w] = (b - 1.0) / 100.0;
}
+ if(f == 0x800000)
+ f = 1;
+ else
+ f *= 2;
}
}
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)
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));
// 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");
// 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);
}
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)
{
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
CSQC_RAPTOR_HUD();
else if(hud == HUD_BUMBLEBEE)
CSQC_BUMBLE_HUD();
+ else if(hud == HUD_BUMBLEBEE_GUN)
+ CSQC_BUMBLE_GUN_HUD();
}
cl_notice_run();
}
}
-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 ()
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;
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;
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;
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;
#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)
// 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") \
}
// 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;
}
// 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;
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)
{
++column;
}
}
+
+ draw_endBoldFont();
}
// Ammo (#1)
HUD_Panel_UpdateCvars(ammo);
HUD_Panel_ApplyFadeAlpha();
+
+ draw_beginBoldFont();
+
vector pos, mySize;
pos = panel_pos;
mySize = panel_size;
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)
HUD_Panel_UpdateCvars(powerups);
HUD_Panel_ApplyFadeAlpha();
+
+ draw_beginBoldFont();
+
vector pos, mySize;
pos = panel_pos;
mySize = panel_size;
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)
HUD_Panel_UpdateCvars(timer);
HUD_Panel_ApplyFadeAlpha();
+
+ draw_beginBoldFont();
+
vector pos, mySize;
pos = panel_pos;
mySize = panel_size;
}
drawstring_aspect(pos, timer, mySize, timer_color, panel_fg_alpha, DRAWFLAG_NORMAL);
+
+ draw_endBoldFont();
}
// Radar (#6)
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]);
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)
{
string distribution_str;
distribution_str = ftos(distribution);
+ draw_beginBoldFont();
if (distribution >= 0)
{
if (distribution > 0)
}
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
float max_fragcount;
max_fragcount = -99;
+ draw_beginBoldFont();
for(tm = teams.sort_next; tm; tm = tm.sort_next) {
if(tm.team == COLOR_SPECTATOR)
continue;
++rows;
}
}
+ draw_endBoldFont();
}
}
HUD_Panel_UpdateCvars(racetimer);
HUD_Panel_ApplyFadeAlpha();
+
+ draw_beginBoldFont();
+
vector pos, mySize;
pos = panel_pos;
mySize = panel_size;
}
}
}
+
+ draw_endBoldFont();
}
// Vote window (#9)
HUD_Panel_UpdateCvars(modicons);
HUD_Panel_ApplyFadeAlpha();
+
+ draw_beginBoldFont();
+
vector pos, mySize;
pos = panel_pos;
mySize = panel_size;
HUD_Mod_Dom(pos, mySize);
else if(gametype == MAPINFO_TYPE_KEEPAWAY)
HUD_Mod_Keepaway(pos, mySize);
+
+ draw_endBoldFont();
}
// Draw pressed keys (#11)
HUD_Panel_UpdateCvars(physics);
HUD_Panel_ApplyFadeAlpha();
+ draw_beginBoldFont();
+
HUD_Panel_DrawBg(1);
if(panel_bg_padding)
{
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)
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);
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;
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;
+}
target_music.qc
vehicles/vehicles.qc
+../server/vehicles/bumblebee.qc
shownames.qh
shownames.qc
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"));
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"));
hud_fontsize = HUD_GetFontsize("hud_fontsize");
+ draw_beginBoldFont();
for(i = 0; i < argc - 1; ++i)
{
float nocomplain;
}
hud_field[hud_num_fields] = SP_END;
+ draw_endBoldFont();
}
// MOVEUP::
}
// 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)
pos_x -= hud_fontsize_x;
}
}
+ draw_endBoldFont();
pos_x = xmin;
pos_y += 1.25 * hud_fontsize_y; // skip the header
// 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;
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);
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);
}
}
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;
}
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);
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
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;
#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
#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"
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];
.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"
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;
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)
{
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;
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)
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;
}
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
alarm1time = 0;
}
}
-
// Shield bar
picsize = draw_getimagesize(hud_sh_bar) * autocvar_cl_vehicles_hudscale;
}
}
-// 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);
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)
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");
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);
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!");
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);
{
o = drawspritetext(o, ang, 0, rgb, a, waypointsprite_fontsize * '1 1 0', txt);
}
+ draw_endBoldFont();
}
void Ent_RemoveWaypointSprite()
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;
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;
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;
//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
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;
#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
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
// (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;
+}
// 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
string ScoreString(float vflags, float value);
+float dotproduct(vector a, vector b);
vector cross(vector a, vector b);
void compressShortVector_init();
// 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);
// 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;
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;
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)
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);
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)
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)
float spaceAvail;
spaceAvail = 1 - me.keepspaceLeft - me.keepspaceRight;
+ if(me.isBold)
+ draw_beginBoldFont();
+
float spaceUsed;
spaceUsed = draw_TextWidth(t, me.allowColors, me.realFontSize);
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)
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;
draw_Text(me.realOrigin, t, fs, me.colorL, me.alpha, me.allowColors);
draw_fontscale = dfs;
+
+ if(me.isBold)
+ draw_endBoldFont();
}
SUPER(Label).draw(me);
}
float ListBox_mouseRelease(entity me, vector pos)
{
- vector absSize;
if(me.pressed == 1)
{
// slider dragging mode
}
// 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");
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
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)
}
}
s = sprintf(_("%s Arena"), substring(s, 3, strlen(s) - 3));
-
+
weaponarenastring = strzone(s);
return weaponarenastring;
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);
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);
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;
}
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;
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");
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:")));
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);
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)
{
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;
}
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")
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);
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);
void antilag_restore(entity e)
{
+ if (e.vehicle && e.vehicle.vehicle_flags == VHF_PLAYERSLOT)
+ return;
+
if(e.vehicle)
antilag_restore(e.vehicle);
--- /dev/null
+#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);
+}
--- /dev/null
+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;
+}
--- /dev/null
+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;
+}
--- /dev/null
+#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;
+}
+*/
// 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
}
}
self.iscreature = TRUE;
+ self.teleportable = TELEPORT_NORMAL;
self.damagedbycontents = TRUE;
force_retouch = 2; // mainly to detect teleports
}
}
self.iscreature = TRUE;
+ self.teleportable = TELEPORT_NORMAL;
self.damagedbycontents = TRUE;
force_retouch = 2; // mainly to detect teleports
.float teamtime;
+.float nb_dropperid;
+.float nb_droptime;
+
void nb_delayedinit();
void nb_init() // Called early (worldspawn stage)
{
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;
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);
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);
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;
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;
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;
{
if (clienttype(self) != CLIENTTYPE_BOT)
return;
+ bot_clearqueue(self);
if(self.cleanname)
strunzone(self.cleanname);
if(self.netname_freeme)
#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"
.float havocbot_personal_waypoint_failcounter;
.float havocbot_chooseenemy_finished;
.float havocbot_stickenemy;
+.float havocbot_role_timeout;
.entity ignoregoal;
.entity bot_lastseengoal;
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
+++ /dev/null
-#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);
-}
+++ /dev/null
-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;
-}
+++ /dev/null
-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;
-}
{
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)
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
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");
{
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);
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)
// 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
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
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;
}
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;
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;
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();
}
if(self.vehicle)
- vehicles_exit(VHEF_RELESE);
-
- if(self.flagcarried)
- DropFlag(self.flagcarried, world, world);
+ vehicles_exit(VHEF_RELESE);
WaypointSprite_PlayerDead();
self.classname = "observer";
self.iscreature = FALSE;
+ self.teleportable = TELEPORT_SIMPLE;
self.damagedbycontents = FALSE;
self.health = -666;
self.takedamage = DAMAGE_NO;
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;
Called when a client spawns in the server
=============
*/
-//void() ctf_playerchanged;
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;
} else if(self.classname == "observer") {
PutObserverInServer ();
}
-
- //if(g_ctf)
- // ctf_playerchanged();
}
.float ebouncefactor, ebouncestop; // electro's values
{
if(self.killindicator_teamchange == -1)
{
- self.team = -1;
- JoinBestTeam( self, FALSE, FALSE );
+ JoinBestTeam( self, FALSE, TRUE );
}
else if(self.killindicator_teamchange == -2)
{
return;
}
- if (self.owner.alpha < 0)
+ if (self.owner.alpha < 0 && !self.owner.vehicle)
{
self.owner.killindicator = world;
remove(self);
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);
} 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;
if(g_arena)
Spawnqueue_Insert(self);
}
- /*else if(g_ctf)
- {
- ctf_clientconnect();
- }*/
attach_entcs();
return;
sv_notice_join();
+
+ MUTATOR_CALLHOOK(ClientConnect);
}
/*
=============
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.
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);
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)
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 {
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";
self.flags |= FL_CLIENT | FL_NOTARGET;
}
-float ctf_usekey();
void PlayerUseKey()
{
if(self.classname != "player")
}
// a use key was pressed; call handlers
- if(ctf_usekey())
- return;
-
MUTATOR_CALLHOOK(PlayerUseKey);
}
=============
*/
.float usekeypressed;
-void() ctf_setstatus;
void() nexball_setstatus;
.float items_added;
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)
{
if(frametime)
player_anim();
- if(g_ctf)
- ctf_setstatus();
-
if(g_nexball)
nexball_setstatus();
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;
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);
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
{
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)
.entity pusher;
.float pushltime;
+.float istypefrag;
.float CopyBody_nextthink;
.void(void) CopyBody_think;
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;
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)
{
self.alpha = -1;
self.solid = SOLID_NOT; // restore later
self.takedamage = DAMAGE_NO; // restore later
+ self.damagedbycontents = FALSE;
}
}
// 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)
{
self.pushltime = max(self.pushltime, time + 0.6);
}
else
+ {
self.pushltime = 0;
+ self.istypefrag = 0;
+ }
float abot, vbot, awep;
abot = (clienttype(attacker) == CLIENTTYPE_BOT);
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)
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;
+ }
}
}
}
return FALSE;
-}
\ No newline at end of file
+}
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);
}
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)
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;
}
}
}
-void GameCommand_bot_cmd(float request, float argc)
+void GameCommand_bot_cmd(float request, float argc, string command)
{
switch(request)
{
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;
}
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");
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
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
{
if(argv(2))
{
- entity client = GetIndexedEntity(argc, 1));
+ entity client = GetIndexedEntity(argc, 1);
float accepted = VerifyClientEntity(client, TRUE, FALSE);
if(accepted > 0)
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") \
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
+++ /dev/null
-#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;
-}
-*/
// 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;
.float watersound_finished;
.float iscreature;
.float damagedbycontents;
+.float damagedbytriggers;
+.float pushable;
+.float teleportable;
.vector oldvelocity;
.float pauseregen_finished;
.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
float startitem_failed;
-void DropFlag(entity flag, entity penalty_receiver, entity attacker);
void DropAllRunes(entity pl);
string matchid;
.float hitplotfh;
-.string noise4;
.float last_pickup;
-// 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.
*/
.float teamkill_soundtime;
.entity teamkill_soundsource;
.entity pusher;
+.float istypefrag;
.float taunt_soundtime;
}
f = 0;
}
- else if(g_ctf)
- {
- if(g_ctf_ignore_frags)
- f = 0;
- }
}
attacker.totalfrags += f;
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 {
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);
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");
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);
}
}
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;
}
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
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;
self.aiment.flags &~= FL_ONGROUND;
self.aiment.pusher = self.realowner;
self.aiment.pushltime = time + autocvar_g_maxpushtime;
+ self.aiment.istypefrag = self.aiment.BUTTON_CHAT;
}
}
{
self.realowner.pusher = attacker;
self.realowner.pushltime = time + autocvar_g_maxpushtime;
+ self.realowner.istypefrag = self.realowner.BUTTON_CHAT;
}
RemoveGrapplingHook(self.realowner);
}
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);
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);
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);
if (other.iscreature)
{
if (other.takedamage)
+ if (!other.deadflag)
if (other.triggerhealtime < time)
{
EXACTTRIGGER_TOUCH;
+#define LATENCY_THINKRATE 10
+.float latency_sum;
+.float latency_cnt;
+.float latency_time;
entity pingplreport;
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
{
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");
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");
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);
void Map_MarkAsRecent(string m);
float world_already_spawned;
-void RegisterWeapons();
void Nagger_Init();
void Item_ItemsTime_Init();
void ClientInit_Spawn();
}
// 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));
Map_MarkAsRecent(mapname);
+ PlayerStats_Init(); // we need this to be initiated before InitGameplayMode
+
precache_model ("null"); // we need this one before InitGameplayMode
InitGameplayMode();
readlevelcvars();
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
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
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)
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)
#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
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?
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");
{
if(e.iscreature)
return TRUE;
+ if(e.pushable)
+ return TRUE;
switch(e.classname)
{
case "body":
+++ /dev/null
-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);
-}
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;
--- /dev/null
+// ================================================================
+// 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;
+}
--- /dev/null
+// 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);
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
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;
--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");
}
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
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);
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
{
-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
}
}
+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; }
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"))
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);
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
{
--- /dev/null
+// 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;
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;
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)
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);
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");
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
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;
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;
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");
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");
}
//=======================//
-// team ents //
+// team ents //
//=======================//
void spawnfunc_nexball_team(void)
{
//=======================//
-// spawnfuncs //
+// spawnfuncs //
//=======================//
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)
{
void spawnfunc_nexball_football(void)
{
- nexball_mode |= NBM_FOOTBALL;
+ nexball_mode |= NBM_FOOTBALL;
self.classname = "nexball_football";
self.solid = SOLID_TRIGGER;
balls |= BALL_FOOT;
// 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)
{
}
//=======================//
-// 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"))
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 ??
{
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))
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)
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)
// 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;
float ball_scale;
float nb_teams;
-.float teamtime;
\ No newline at end of file
+.entity nb_dropper;
+.float nb_droptime;
+
+.float teamtime;
--- /dev/null
+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;
+}
--- /dev/null
+#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;
+}
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);
MUTATOR_DECLARATION(mutator_rocketflying);
MUTATOR_DECLARATION(mutator_spawn_near_teammate);
MUTATOR_DECLARATION(mutator_vampire);
+MUTATOR_DECLARATION(mutator_superspec);
MUTATOR_DECLARATION(sandbox);
.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;
serverflags |= SERVERFLAG_PLAYERSTATS;
PlayerStats_AddEvent(PLAYERSTATS_ALIVETIME);
+ PlayerStats_AddEvent(PLAYERSTATS_AVGLATENCY);
PlayerStats_AddEvent(PLAYERSTATS_WINS);
PlayerStats_AddEvent(PLAYERSTATS_MATCHES);
PlayerStats_AddEvent(PLAYERSTATS_JOINS);
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
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)
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
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)
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)
// 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";
// 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)
{
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
t_plats.qc
antilag.qc
-ctf.qc
+//ctf.qc
domination.qc
-mode_onslaught.qc
+//mode_onslaught.qc
//nexball.qc
g_hook.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
mutators/mutator_vampire.qc
mutators/mutator_spawn_near_teammate.qc
mutators/sandbox.qc
+mutators/mutator_superspec.qc
../warpzonelib/anglestransform.qc
../warpzonelib/mathlib.qc
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
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()
CreatureFrame ();
CheckRules_World ();
- AuditTeams();
-
RuneMatchGivePoints();
bot_serverframe();
#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)
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();
}
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;
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;
}
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;
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();
}
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
WriteByte(MSG_ENTITY, ENT_CLIENT_ITEM);
WriteByte(MSG_ENTITY, sf);
-
//WriteByte(MSG_ENTITY, self.cnt);
if(sf & ISF_LOCATION)
{
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);
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
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")
{
return;
if (self.owner == other)
return;
+ if(MUTATOR_CALLHOOK(ItemTouch))
+ return;
if (self.classname == "droppedweapon")
{
remove (self);
return;
}
+
+ if(self.angles != '0 0 0')
+ self.SendFlags |= ISF_ANGLES;
self.reset = Item_Reset;
// it's a level item
float PUSH_SILENT = 2;
.float pushltime;
+.float istypefrag;
.float height;
void() SUB_UseTargets;
// reset tracking of who pushed you into a hazard (for kill credit)
other.pushltime = 0;
+ other.istypefrag = 0;
}
if(self.enemy.target)
}
-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
*/
void LinkDoors()
{
- entity t, starte;
+ entity t;
vector cmins, cmaxs;
if (self.enemy)
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);
}
//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(); }
#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;
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
{
{
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;
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)
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;
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)
{
}
else if(value == "activator")
{
- valueent = activator;
+ valueent = act;
value = "";
}
else if(value == "other")
}
else if(value == "pusher")
{
- if(time < activator.pushltime)
- valueent = activator.pusher;
+ if(time < act.pushltime)
+ valueent = act.pusher;
else
valueent = world;
value = "";
oldactivator = activator;
self = e;
- activator = oldself.target_spawn_activator;
+ activator = act;
self.target_spawn_spawnfunc();
}
}
+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;
// # 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)
{
}
void dom_init();
-void ctf_init();
void runematch_init();
void tdm_init();
void entcs_init();
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
}
{
ActivateTeamplay();
have_team_spawns = -1; // request team spawns
+ MUTATOR_ADD(gamemode_onslaught);
}
if(g_race)
float PlayerValue(entity p)
{
- if(IsTeamBalanceForced() == 1)
- return 1;
return 1;
// FIXME: it always returns 1...
}
}
}
+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
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;
}
//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) {
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");
if(self.deadflag == DEAD_NO)
Damage(self, self, self, 100000, DEATH_TEAMCHANGE, self.origin, '0 0 0');
}
- //ctf_playerchanged();
}
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)
{
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;
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;
--- /dev/null
+#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
void racer_enter();
// Auto cvars
+float autocvar_g_vehicle_racer;
+
float autocvar_g_vehicle_racer_speed_afterburn;
float autocvar_g_vehicle_racer_afterburn_cost;
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)
{
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);
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;
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;
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');
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;
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;
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;
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
#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;
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();
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()
void raptor_enter()
{
+ self.vehicle_weapon2mode = RSM_BOMB;
self.owner.PlayerPhysplug = raptor_takeoff;
self.movetype = MOVETYPE_BOUNCEMISSILE;
self.solid = SOLID_SLIDEBOX;
if not (self.owner)
return;
-
+
makevectors(self.angles);
if(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;
}
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;
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,
}
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)
{
}
- 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;
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);
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);
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()
self.gun2.angles_y = anglemods(self.gun2.angles_y);
self.nextthink = time + 15;
}
+
float raptor_impulse(float _imp)
{
switch(_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;
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
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;
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
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) ;
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;
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;
self.gun2.cnt = time + self.attack_finished_single;
}
+float spiderbot_aiframe()
+{
+ return FALSE;
+}
+
float spiderbot_frame()
{
vector ad, vf;
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
}
}
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);
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;
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;
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;
e = e.chain;
}
- self.velocity = '0 0 0';
+ //self.velocity = '0 0 0';
self.think = spiderbot_think;
self.nextthink = time;
self.frame = 5;
}
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()
{
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;
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;
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
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();
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);
// 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);
**/
void CSQCVehicleSetup(entity own, float vehicle_id)
{
+ if (clienttype(own) != CLIENTTYPE_REAL)
+ return;
+
msg_entity = own;
WriteByte(MSG_ONE, SVC_TEMPENTITY);
}
}
-#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); \
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;
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;
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;
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?)
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;
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);
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);
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);
{
//setmodel(self, "");
self.alpha = -1;
+ self.effects |= EF_NODRAW;
}
else
{
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;
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;
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)
{
else
vehicles_exit(VHEF_RELESE);
+
+ antilag_clear(self);
+
self.vehicle_die();
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;
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,
vector min_s,
vector max_s,
float nodrop,
- void() spawnproc,
+ void(float _spawnflag) spawnproc,
float _respawntime,
float() physproc,
void() enterproc,
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 == "")
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;
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;
setmodel(self.vehicle_hudmodel, hudmodel);
setmodel(self.vehicle_viewport, "null");
-
if(topmodel != "")
{
setmodel(self.tur_head, topmodel);
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;
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;
+}
+*/
#include "racer.qc"
#include "spiderbot.qc"
#include "raptor.qc"
-//#include "bumblebee.qc"
+#ifndef VEHICLES_NO_UNSTABLE
+#include "bumblebee.qc"
+#endif
#endif
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;
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
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)
{
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;
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;
{
float counter, shots;
entity proj, prevproj, firstproj;
+ vector s;
+ vector forward, right, up;
float maxdmg;
W_DecreaseAmmo(ammo_cells, autocvar_g_balance_crylink_secondary_ammo, autocvar_g_balance_crylink_reload_ammo);
maxdmg += autocvar_g_balance_crylink_secondary_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);
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)
}
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;
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)
{
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;
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;
}
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)))
-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
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
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
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
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
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
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
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
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
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"
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
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
+
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